зачем использовать модуль include, когда достаточно одного class_eval - PullRequest
7 голосов
/ 10 августа 2010

В следующем коде используется модуль включения. На мой взгляд, если удалить модуль include, то будет также создан метод экземпляра. Тогда зачем пользователю включать модуль?

http://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations.rb#L1416

  include Module.new {
          class_eval <<-RUBY, __FILE__, __LINE__ + 1
            def destroy                     # def destroy
              super                         #   super
              #{reflection.name}.clear      #   posts.clear
            end                             # end
          RUBY
        }

Ответы [ 3 ]

12 голосов
/ 13 августа 2010

Прежде всего давайте проясним одну вещь.Когда они вызывают super внутри class_eval - это абсолютно не имеет отношения к тому, почему они использовали include Module.new {}.На самом деле super, который был вызван внутри метода destroy, совершенно не имеет отношения к ответу на ваш вопрос.Внутри этого метода уничтожения может быть любой произвольный код.

Теперь, когда мы его получили, вот что происходит.

В ruby, если вы просто определяете метод класса, изатем определите его снова в том же классе, вы не сможете вызвать super для доступа к предыдущему методу.

Например:

class Foo
  def foo
    'foo'
  end

  def foo
    super + 'bar'
  end
end

Foo.new.foo # => NoMethodError: super: no superclass method `foo' for #<Foo:0x101358098>

Это имеет смысл, потому что первыйfoo не был определен ни в каком суперклассе, ни где-либо в цепочке поиска (где super указывает).Тем не менее, вы можете определить первый foo таким образом, чтобы при последующем его перезаписи он был доступен при вызове super.Это именно то, чего они хотели добиться с помощью включения модуля.

class Foo
  include Module.new { class_eval "def foo; 'foo' end" }

  def foo
    super + 'bar'
  end
end

Foo.new.foo # => "foobar"

Это работает, потому что когда вы включаете модуль, ruby ​​вставляет его в цепочку поиска.Таким образом, вы можете впоследствии вызвать super во втором методе и ожидать вызова включенного метода.Отлично.

Однако вы можете задаться вопросом, почему бы просто не включить модуль без всех хитростей?Почему они используют блочный синтаксис?Мы знаем, что мой приведенный выше пример в точности эквивалентен следующему:

module A
  def foo
    'foo'
  end
end

class Foo
  include A

  def foo
    super + 'bar'
  end
end

Foo.new.foo # => "foobar"

Так почему же они этого не сделали?Ответ - звонок на reflection.Им нужно было захватить переменную (или метод), которая была доступна в текущем контексте, а именно reflection.

Поскольку они определяют новый модуль с использованием синтаксиса блока, все переменные вне блока доступныдля использования внутри блока.Удобно.

Просто чтобы проиллюстрировать.

class Foo
  def self.add_foo_to_lookup_chain_which_returns(something)
    # notice how I can use variable something in the class_eval string
    include Module.new { class_eval "def foo; '#{something}' end" }
  end
end

# so somewhere else I can do

class Foo
  add_foo_to_lookup_chain_which_returns("hello")

  def foo
    super + " world"
  end
end

Foo.new.foo # => "hello world"

Аккуратно, да?

Теперь позвольте мне подчеркнуть это еще раз.Вызов super внутри метода destroy в вашем примере не имеет ничего общего с любым из вышеперечисленного.Они назвали это по своим собственным причинам, потому что, возможно, класс, где это происходит, подклассирует другой класс, который уже определил destroy.

Надеюсь, это прояснило.

0 голосов
/ 10 августа 2010

Благодаря include, destroy метод не перезаписывается. Он попадает в призрачный класс, из которого происходит настоящий класс. Таким образом, когда один вызовет destroy для объекта AR, будет вызван исходный объект, а super вызовет один из анонимного модуля (который позже вызовет original destroy из класса, который он получил от).

Действительно, немного хитро.

0 голосов
/ 10 августа 2010

Полагаю, но ... они не хотят перезаписывать метод "уничтожить" и хотят оставить его доступным для перегрузки некоторым конечным пользователем (вами или мной), не удаляя это "отражение" .clear "функциональность.

Итак, включив его в качестве модуля, они могут вызывать «super», который будет называть исходное уничтожение или перегруженной версией (написанной конечным пользователем).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...