动态创建类方法

我正在编写一个类方法来创建另一个类方法。 关于class_evalinstance_eval如何在类方法的上下文中运行似乎有些奇怪。 为了显示:

 class Test1 def self.add_foo self.class_eval do # does what it says on the tin define_method :foo do puts "bar" end end end end Test1.add_foo # creates new instance method, like I'd expect Test1.new.foo # => "bar" class Test2 def self.add_foo self.instance_eval do # seems to do the same as "class_eval" define_method :foo do puts "bar" end end end end Test2.add_foo # what is happening here?! Test2.foo # => NoMethodError Test2.new.foo # => "bar" class Test3 def self.add_foo (class < creates new class method, as I'd expect Test3.foo # => "bar" 

我的理解是类方法是在有问题的类的元类上定义的实例方法(在本例中为Test2 )。 基于该逻辑,我希望类方法的接收器调用add_foo作为元类。

  • Test2.add_foo方法中, self指的是什么?
  • 为什么在此接收者对象上调用instance_eval创建实例方法?

instance_evalclass_eval之间的主要区别在于instance_evalinstance_eval的上下文中工作,而class_eval在类的上下文中工作。 我不确定你对Rails有多熟悉,但让我们看一下这个例子:

 class Test3 < ActiveRecord::Base end t = Test3.first t.class_eval { belongs_to :test_25 } #=> Defines a relationship to test_25 for this instance t.test_25 #=> Method is defined (but fails because of how belongs_to works) t2 = Test3.find(2) t2.test_25 #=> NoMethodError t.class.class_eval { belongs_to :another_test } t.another_test #=> returns an instance of another_test (assuming relationship exists) t2.another_test #=> same as t.another_test t.class_eval { id } #=> NameError t.instance_eval { id } #=> returns the id of the instance t.instance_eval { belongs_to :your_mom } #=> NoMethodError 

发生这种情况是因为belongs_to实际上是在类主体的上下文中发生的方法调用,您无法从实例调用它。 当您尝试使用class_eval调用id时,它会失败,因为id是在实例上定义的方法,而不是在类中定义的方法。

在针对实例调用时,使用class_evalinstance_eval定义方法的工作方式基本相同。 它们将仅在被调用的对象的实例上定义方法。

 t.class_eval do def some_method puts "Hi!" end end t.instance_eval do def another_method puts "Hello!" end end t.some_method #=> "Hi!" t.another_method #=> "Hello!" t2.some_method #=> NoMethodError t2.another_method #=> NoMethodError 

然而,他们在与class级打交道时有所不同。

 t.class.class_eval do def meow puts "meow!" end end t.class.instance_eval do def bark puts "woof!" end end t.meow #=> meow! t2.meow #=> meow! t.bark #=> NoMethodError t2.bark #=> NoMethodError 

那么树皮去哪儿了? 它在类’singleton类的实例上定义。 我将在下面解释。 但现在:

 t.class.bark #=> woof! Test3.bark #=> woof! 

因此,为了回答关于self在课堂体内指的是什么的问题,你可以构建一个小测试:

 a = class Test4 def bar puts "Now, I'm a #{self.inspect}" end def self.baz puts "I'm a #{self.inspect}" end class << self def foo puts "I'm a #{self.inspect}" end def self.huh? puts "Hmmm? indeed" end instance_eval do define_method :razors do puts "Sounds painful" end end "But check this out, I'm a #{self.inspect}" end end puts Test4.foo #=> "I'm a Test4" puts Test4.baz #=> "I'm a Test4" puts Test4.new.bar #=> Now I'm a # puts a #=> But check this out, I'm a # 

所以这里发生的事情是,在上面的第一个put语句中,我们看到inspect告诉我们类方法体的上下文中的self指的是Test4类。 在第二次看法中,我们看到相同的事情,它只self.method_name不同的方式定义(使用self.method_name表示法来定义类方法)。 在第三个中,我们看到self指的是Test4的一个实例。 最后一个有点有趣,因为我们看到的是self指的是一个名为Test4Class实例。 那是因为当你定义一个类时,你正在创建一个对象。 Ruby中的所有东西都是一个对象。 这个对象实例称为元类或本征类或单例类。

您可以使用class << self idiom'访问本征class << self 。 当你在那里时,你实际上可以访问本征类的内部。 您可以在本征类内部定义实例方法,这与调用self.method_name 。 但由于您处于本征类的上下文中,因此可以将方法附加到本征类的本征类。

 Test4.huh? #=> NoMethodError Test4.singleton_class.huh? #=> Hmmm? indeed 

当您在方法的上下文中调用instance_eval时,您实际上是在类本身上调用instance_eval ,这意味着您正在Test4上创建实例方法。 那我在本征类里面调用instance_eval怎么样? 它在Test4的本征类实例上创建一个方法:

 Test4.razors #=> Sounds painful 

希望这能解决您的一些问题。 我知道我已经学会了一些输入这个答案的东西!