2013-11-01 2 views
1

Я создаю модуль, расширяющий функциональность модели ActiveRecord.Передача блока динамически созданному методу

Вот моя первоначальная настройка.

Мой класс:

class MyClass < ActiveRecord::Base 
    is_my_modiable 
end 

И Модуль:

module MyMod 
    def self.is_my_modiable 
    class_eval do 
     def new_method 
     self.mod = true 
     self.save! 
     end 
    end 
    end 
end 
ActiveRecord::Base(extend,MyMod) 

То, что я хотел бы сделать сейчас расширить функциональность new_method путем передачи в блоке. Что-то вроде этого:

class MyClass < ActiveRecord::Base 
    is_my_modiable do 
    self.something_special 
    end 
end 

module MyMod 
    def self.is_my_modiable 
    class_eval do 
     def new_method 
     yield if block_given? 
     self.mod = true 
     self.save! 
     end 
    end 
    end 
end 

Это не работает, и это имеет смысл. В class_eval метод new_method не выполняется, просто определяется, и поэтому оператор yield не будет выполняться до тех пор, пока метод не будет вызван.

Я попытался назначить блок переменной класса в class_eval, а затем вызвать эту переменную класса в этом методе, но блок вызывается на всех моделях is_my_modiable, даже если они не прошли блок в метод.

Я мог бы просто переопределить метод, чтобы получить тот же эффект, но я надеюсь, что есть более элегантный способ.

+0

Не забывайте поднимать их, когда принимаете их ответы. –

ответ

2

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

bl.call здесь не будет, поскольку он будет выполняться в исходном контексте (классе), и вам нужно выполнить его в рамках этого текущего экземпляра.

module MyMod 
    def is_my_modiable(&block) 
    class_eval do 
     @stored_block = block # back up block 
     def new_method 
     bl = self.class.instance_variable_get(:@stored_block) # get from class and execute 
     instance_eval(&bl) if bl 
     self.mod = true 
     self.save! 
     end 
    end 
    end 
end 

class MyClass 
    extend MyMod 

    is_my_modiable do 
    puts "in my modiable block" 
    self.something_special 
    end 

    def something_special 
    puts "in something special" 
    end 

    attr_accessor :mod 
    def save!; end 
end 

MyClass.new.new_method 
# >> in my modiable block 
# >> in something special 
+0

Спасибо. Я приближался. Я вызывал блок вместо использования instance_eval. Кажется, когда я назвал блок, я внутри блока был не экземпляром, как я ожидал (очевидно, instance_eval исправил это). Мой вопрос заключается в том, что внутри внутри блока используется bl.call вместо instance_eval (& bl). Является ли это примером замыканий, и если да, то закрывается ли это при объявлении в классе или назначается в class_eval (@ stored_block = block)? – cgat

+0

Блок связан с его лексическим контекстом. Вы создаете его в контексте 'MyClass' (класс), так что это' self'. 'instance_eval' перепроверяет блок. –

+0

Если вы хотите передать параметры, используйте [instance_exec] (http://ruby-doc.org/core-1.9.3/BasicObject.html#method-i-instance_exec) вместо [instance_eval] (http: // ruby -doc.org/core-1.9.3/BasicObject.html#method-i-instance_eval) –

1

Вы можете сделать это путем присвоения блока в качестве параметра метода:

module MyMod 
    def self.is_my_modiable 
    class_eval do 
     def new_method(&block) 
     block.call if block 
     self.mod = true 
     self.save! 
     end 
    end 
    end 
end 
+1

OP хочет передать блок 'is_my_modiable', а не' new_method'. –

Смежные вопросы