Ruby里面extend include prepend 区别

Python011

Ruby里面extend include prepend 区别,第1张

在Ruby中,我们如果需要调用 module 的话可以使用 extend 、 include 、 prepend ,但是这些关键字具体有哪些区别呢。

现在我们在 Person 类中使用 extend 关键字来调用模块,使用相关方法,并且打印出相关信息。

我们由 Person.introduce 和 Person.current_time 可以观察出来 extend 关键字的作用是为 Person 添加了2个类方法。

上面两个类调用的方法都报错了可以看出, include 和 extend 相反, include 是为类中添加的实例方法,而不是类方法。

prepend 和 include 同样都是为类中添加实例方法,但不同之处在于,引入模块后的方法链会有一点区别

我们先看看 include 的方法链

再看看 prepend 的

可以看出 FooModule 和 Person 的顺序有什么不同,那么具体不同体现在哪,接下来我们调用方法就可以看出区别了。

include

prepend

可以看到,如果使用 prepend 的话我们会优先去查找 FooModule 中的 introduce 方法(如果 FooModule 没有 introduce 方法话,会依次去祖先链的上一级去查找 introduce 方法),而 include 是优先在 Person 中去查找。

在Ruby中,有多种方法可以实现方法的动态调用。

1.

使用send方法

第一种实现动态方法调用是使用send方法,send方法在Object类中定义,方法的第一个参数是一个符号用来表示所要调用的方法,后面则是所调用方法需要的参数。

“This

is

a

dog1″.send(:length)

=>

14

上面的代码中通过send方法去对一个字符串执行length操作,返回字符串的长度。

class

TestClass

def

hello(*args)

”Hello

+

args.join(‘

‘)

end

end

a

=

TestClass.new

puts

a.send

:hello,

“This”,

“is”,

“a”,

“dog!”

执行结果为:

Hello

This

is

a

dog!

2.

使用Method类和UnboundMethod类

另一种实现动态方法调用是使用Object类的method方法,这个方法返回一个Method类的对象。我们可以使用call方法来执行方法调用。

test1

=

“This

is

a

dog1″.method(:length)

test1.call

=>

14

class

Test

def

initialize(var)

@var

=

var

end

def

hello()

”Hello,

@var

=

#{@var}”

end

end

k

=

Test.new(10)

m

=

k.method(:hello)

m.call

#=>

“Hello,

@iv

=

99″

l

=

Test.new(‘Grant’)

m

=

l.method(“hello”)

m.call

#=>

“Hello,

@iv

=

Fred”

可以在使用对象的任何地方使用method对象,当调用call方法时,参数所指明的方法会被执行,这种行为有些像C语言中的函数指针。你也可以把method对象作为一个迭代器使用。

def

square(a)

a*a

end

mObj

=

method(:square)

[1,

2,

3,

4].collect(&mObj)

=>

[1

4

9

16]

Method对象都是和某一特定对象绑定的,也就是说你需要通过某一对象使用Method对象。你也可以通过UnboundMethod类创建对象,然后再把它绑定到某个具体的对象中。如果UnboundMethod对象调用时尚未绑定,则会引发异常。

class

Double

def

get_value

2

*

@side

end

def

initialize(side)

@side

=

side

end

end

a

=

Double.instance_method(:get_value)

#返回一个UnboundMethod对象

s

=

Double.new(50)

b

=

a.bind(s)

puts

b.call

执行结果为:

100

看下面一个更具体的例子:

class

CommandInterpreter

def

do_2()

print

“This

is

2

end

def

do_1()

print

“This

is

1

end

def

do_4()

print

“This

is

4

end

def

do_3()

print

“This

is

3

end

Dispatcher

=

{

?2

=>

instance_method(:do_2),

?1

=>

instance_method(:do_1),

?4

=>

instance_method(:do_4),

?3

=>

instance_method(:do_3)

}

def

interpret(string)

string.each_byte

{|i|

Dispatcher[i].bind(self).call

}

end

end

interpreter

=

CommandInterpreter.new

interpreter.interpret(’1234′)

执行结果为:

This

is

1

This

is

2

This

is

3

This

is

4

3.

使用eval方法

我们还可以使用eval方法实现方法动态调用。eval方法在Kernel模块中定义,有多种变体如class_eval,module_eval,instance_eval等。Eval方法将分析其后的字符串参数并把这个字符串参数作为Ruby代码执行。

str

=

“Hello”

eval

“str

+

World!’”

=>

Hello

World!

sentence

=

%q{“This

is

a

test!”.length}

eval

sentence

=>

15

当我们在使用eval方法时,我们可以通过eval方法的第二个参数指明eval所运行代码的上下文环境,这个参数可以是Binding类对象或Proc类对象。Binding类封装了代码在某一环境运行的上下文,可以供以后使用。

class

BindingTest

def

initialize(n)

@value

=

n

end

def

getBinding

return

binding()

#使用Kernel#binding方法返回一个Binding对象

end

end

obj1

=

BindingTest.new(10)

binding1

=

obj1.getBinding

obj2

=

BindingTest.new(“Binding

Test”)

binding2

=

obj2.getBinding

puts

eval(“@value”,

binding1)

#=>

10

puts

eval(“@value”,

binding2)

#=>

Binding

Test

puts

eval(“@value”)

#=>

nil

可以看到上述代码中,@value在binding1所指明的上下文环境中值为10,在binding2所指明的上下文环境中值为Binding

Test。当eval方法不提供binding参数时,在当前上下文环境中@value并未定义,值为nil。

报这个ERROR: While executing gem ... (Gem::FilePermissionError)

You don't have write permissions for the /Library/Ruby/Gems/2.6.0 directory.

权限错误的解决办法

因为ruby环境系统自带,所以Mac系统为了保证自身环境的问题,对权限进行了限制,最终导致一般用户无法对系统的ruby环境相关的文件读写内容。所以解决点也就在这了

解决办法: 1第一种修改权限(不建议这么做,因为是root权限,修改权限可能对系统造成影响,这里不提供修改方法了)

                 2第二种 两套ruby 环境

第二种方案大体也就分成三步了:                 

安装针对于用户所使用的ruby环境

导入新的ruby环境的环境变量

查看是否ruby环境是否安装成功

进行安装之前,我们通过以下命令进行排查,查看当前的ruby环境是否是用的系统自带的环境

whichruby

如果ruby使用的路径如下,那么此时就是用的系统自带的ruby环境了

/usr/bin

推荐使用homebrew安装ruby环境,如果没有安装homebrew`,可以通过以下命令安装:

/bin/bash -c"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

假定已经安装了homebrew环境了,使用以下命令进行ruby的安装

brew install ruby

此时可能默认安装到了系统自带的路径,在执行,建议当前安装的最新版本 把2.7.0换成你安装成的最先版本 如果不知道最新版本 可以执行 ruby -v 查看

echo 'export PATH="/usr/local/opt/ruby/bin:/usr/local/lib/ruby/gems/2.7.0/bin:$PATH"' >>~/.zshrc

然后在执行

source ~/.zshrc

继续执行which ruby

此时能看到路径已经不是系统路径了,退出终端重新执行安装cocoapods,问题解决