2013-07-05 4 views
8

Я пытался выяснить, как расширить поведение модуля initialize. Я хочу сделать это, не вызвав super в initialize класса, который смешивается. Я хочу поддерживать нормальный шаблон вызова include Я не могу понять это. Я читал все, что мог найти по этому вопросу, и, хотя у людей есть предложения, ни один из них на самом деле не работает (по крайней мере, в моих руках).Можно ли переопределить # инициализацию в модуле Ruby?

Вот что я (думаю) Я знаю:

  • Если это может быть сделано на всех, это должно быть сделано с помощью крючка на include (т.е. Module.included(base)).
  • Крюк include будет выполняться до того, как класс включения определит initialize, поэтому нет смысла просто пытаться определить initialize с base.instance_eval, потому что он будет перезаписан.
  • Было предложено использовать крючок method_added и иметь дело с ним. Это то, что я пытаюсь сейчас, но кажется, что крючок выполняется в начале определения метода, поэтому вы получаете то, что вы видите ниже.

    module Mo 
        def self.included(klass) 
        klass.instance_eval do 
         def method_added(method) 
         puts "Starting creation of #{method} for #{self.name}" 
         case method 
         when :initialize 
          alias_method :original_initialize, :initialize 
          puts "About to define initialize in Mo" 
          def initialize 
          original_initialize 
          puts "Hello from Mo#initialize" 
          end 
          puts "Finished defining initialize in Mo" 
         end 
         puts "Finishing creation of #{method} for #{self.name}" 
         end 
        end 
        end 
    end 
    
    class Foo 
        include Mo 
        def initialize 
        puts "Hello from Foo#initialize" 
        end 
    end 
    
    foo = Foo.new 
    

Это приводит следующий вывод:

Starting creation of initialize for Foo 
    Starting creation of original_initialize for Foo 
    Finishing creation of original_initialize for Foo 
    About to define initialize in Mo 
    Finished defining initialize in Mo 
    Finishing creation of initialize for Foo 
    Hello from Foo#initialize 

Он смотрит на меня как initialize из класса Foo еще перезапись определение из модуля. Я предполагаю, что это потому, что определение все еще открыто, предполагая, что это не вопрос того, какой блок запускается последним, который закончен последним, который «побеждает».

Если кто-то действительно знает, как это сделать, и пусть он работает, пожалуйста, просветите меня.

FWIW, да, я думаю, у меня есть веская причина для этого.

ответ

24

Если вы используете Ruby 2.0 или новее, вы можете использовать только prepend. Либо требовать от пользователя prepend, а не include или сделать:

module Mo 
    module Initializer 
    def initialize 
     puts "Hello from Mo#initialize" 
     super 
    end 
    end 

    def self.included(klass) 
    klass.send :prepend, Initializer 
    end 
end 
+0

Я сразу же узнал о 'prepend', задав вопрос. Мне это нравится! Все еще любопытно, есть ли способ сделать это в версии 1.9. * – Huliax

+0

Есть ли что-то очевидное, что мне не хватает в использовании 'klass.send: prepend' вместо просто' klass.prepend'? – NobodysNightmare

0

Будет ли он удовлетворять условному вызову в инициализации Foo, который вызывает только включенный метод, если он присутствует?

module Mo 
    def initialize_extra 
    puts "Hello from Mo" 
    end 
end 

class Foo 
    include Mo 
    def initialize 
    if defined? initialize_extra 
     initialize_extra 
    else 
     puts "Hello from Foo" 
    end 
    end 
end 

x = Foo.new 
+1

Моя цель - не налагать никаких нагрузок на класс, кроме необходимости включать модуль. – Huliax

6

Хорошо, хорошо в Ruby 1.9 можно добавить функциональность метода new класса ...

module Mo 
    def new(*var) 
    additional_initialize(*var) 
    super(*var) 
    end 
    def additional_initialize(*var) 
    puts "Hello from Mo" 
    end 
end 

class Foo 
    extend Mo 
    def initialize 
    puts "Hello from Foo" 
    end 
end 

foo = Foo.new 

Это возвращает ...

Hello from Mo 
Hello from Foo 
Смежные вопросы