Разница между "class << anObject" и anObject.class_eval - PullRequest
0 голосов
/ 05 декабря 2008

Я вижу следующий код в плагине attribute_fu:

module AttributeFu
    module Associations #:nodoc:                                                                                                                                                
        def self.included(base) #:nodoc:                                                                                                                                          
            base.class_eval do
                extend ClassMethods
                class << self; alias_method_chain :has_many, :association_option; end

                class_inheritable_accessor  :managed_association_attributes
                write_inheritable_attribute :managed_association_attributes, []

                after_update :save_managed_associations
            end
        end

        ...
    end
end

Когда я пытаюсь заменить

class << self; alias_method_chain :has_many, :association_option; end

с: alias_method_chain: has_many,: association_option?

Я получаю следующую ошибку

/usr/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/core_ext/module/aliasing.rb:31:in `alias_method': undefined method `has_many' for class `ActiveRecord::Base' (NameError)
        from /usr/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/core_ext/module/aliasing.rb:31:in `alias_method_chain'
        from /home/twong/git/physpace/vendor/plugins/attribute_fu/lib/attribute_fu/associations.rb:9:in `included'

Я думал, что эти две строки сделают одно и то же, но, похоже, я ошибаюсь. Может кто-нибудь объяснить мою ошибку?

Ответы [ 2 ]

3 голосов
/ 05 декабря 2008
# init.rb
ActiveRecord::Base.class_eval { include AttributeFu::Associations }

module AttributeFu
    module Associations
        def self.included(base)
            # base == ActiveRecord::Base (the class)
            base.class_eval do
                # class_eval makes self == ActiveRecord::Base, and makes def define instance methods.
                extend ClassMethods

                # If has_many were an instance method, we could do this
                #   alias_method_chain :has_many, :association_option; end
                # but it's a class method, so we have to do the alias_method_chain on
                # the meta-class for ActiveRecord::Base, which is what class << self does.
                class << self; alias_method_chain :has_many, :association_option; end
            end
        end
    end
end

Еще один способ поиграть с этим - поместить это в IRB:

class A ; end
A.class_eval { puts self.inspect ; class << self ; puts self.inspect ; end }

Смотри также

0 голосов
/ 05 декабря 2008

В этом случае self не означает anObject, это скорее сахарная конструкция.

class << self
  ...
end

определяет методы класса для включающего объекта. Метод alias_method_chain - это метод, который псевдоним вещей. В этом случае это псевдонимы от has_many до has_many_without_association_options и has_many_with_association_options с has_many. В вашем случае это псевдоним методов класса, поэтому вы должны использовать его в области видимости метода класса. Это позволяет расширять методы без особых хлопот.

Методы класса вызываются как, скажем:

SomeThing.bar_method

тогда как методы экземпляров вызываются для экземпляров класса:

assoc = SomeThing.new
assoc.foo_method

Соответствующий код будет:

class SomeThing
  def foo_method
    ...
  end
  class << self
    def bar_method
      ...
    end
  end
end

в вашем случае у вас есть модуль AttributeFu::Associations. При включении в класс Foo он запускает Foo.class_eval, который определяет некоторые атрибуты экземпляра внутри Foo, и запускает метод alias_method_chain внутри области действия методов класса (class << self).

Также есть extends ClassMethods, который должен определять либо:

def self.has_many_with_association_options
    ...
end

или

class << self
  def has_many_with_association_options
    ...
  end
end
...