2014-08-29 6 views
1

Существует следующий код:method_missing и define_method в Рубине

class MyOpenStruct 
    def initialize(initial_values = {}) 
    @values = initial_values 
    end 

    def _singleton_class 
     class << self 
      self 
     end 
    end 

    def method_missing(name, *args, &block) 
     if name[-1] == "=" 
      base_name = name[0..-2].intern 
      puts "add_method_to_set" 
      self.class.add_method_to_set(base_name) 
     @values[base_name] = args[0] 
    else 
      puts "add_method_to_get" 
      self.class.add_method_to_get(base_name)   
     @values[name] 
     end 
    end 

    def self.add_method_to_get(name) 
     define_method(name) do |value| 
      @values[name] 
     end 
    end 

    def self.add_method_to_set(name) 
     define_method(name) do |value| 
      @values[name] = value 
     end 
    end 
end 

obj1 = MyOpenStruct.new(name: "Dave") 
obj1.address = "1" 

obj2 = MyOpenStruct.new(name: "Dave") 
obj2.address = "2" 

Я хочу сделать следующее вещь: когда я выполнить некоторый метод (obj1.address) и он отсутствует, я хочу, чтобы добавить этот метод к моему Класс MyOpenStruct. Но когда я выполняю свой код, я теряю «два раза» вместо одного. Зачем? Я не понимаю. Пожалуйста, объясните это мне. Благодарю.

+0

Не было бы лучше просто засеять начальный набор с некоторыми значениями по умолчанию, как вызов 'супер ({адрес: ''} .merge (initial_values))' 'внутри initialize'? – tadman

+0

вы должны просто наследовать от «OpenStruct», если хотите эту функциональность, я сделал это, как только я пошел нормально. – Orlando

+0

Подсказка: проверьте имена метода, который вы определяете –

ответ

0

@koffeinfrei определил одну проблему с вами кодом, но я нашел несколько других. Ниже у меня есть то, что я считаю исправленной версией. Я также предложил альтернативный способ структурирования кода. Мой главный совет - вытащить динамическое создание методов экземпляра, поскольку это довольно общий. Вы могли бы даже поместить это в модуль с другими методами, которые вы могли бы включить по мере необходимости.

Ваш код с ремонтами

class MyOpenStruct 
    def initialize(initial_values = {}) 
    @values = initial_values 
    end 

    def method_missing(name, *args, &block) 
    puts "in mm, name = #{name}" 
    if name[-1] == "=" 
     base_name = name[/\w+/] 
     puts "add_method_to_set: '#{name}'" 
     self.class.add_method_to_set(base_name) 
     @values[base_name.to_sym] = args[0] 
    else 
     puts "add_method_to_get: '#{name}'" 
     self.class.add_method_to_get(name)   
     @values[name.to_sym] 
    end 
    end 

    def self.add_method_to_get(name) 
    define_method(name.to_sym) do 
     @values[name.to_sym] 
    end 
    end 

    def self.add_method_to_set(name) 
    define_method((name+'=').to_sym) do |value| 
     @values[name.to_sym] = value 
    end 
    end 
end 

Альтернативные строительные

def create_instance_eval(klass, method, &block) 
    klass.class_eval { define_method(method, &block) } 
end 

class MyOpenStruct 
    def initialize(initial_values = {}) 
    @values = initial_values 
    end 

    def method_missing(name, *args, &block) 
    if name[-1] == "=" 
     base_name = name[/\w+/] 
     method_name = (base_name+'=').to_sym 
     puts "create method '#{method_name}'" 
     method = create_instance_eval(self.class, method_name) do |value| 
      @values[base_name.to_sym] = value 
     end 
     send(method, args[0]) 
    else 
     method_name = name.to_sym 
     puts "create method '#{method_name}'" 
     method = create_instance_eval(self.class, method_name) do 
     @values[method_name] 
     end 
     send(method) 
    end 
    end 
end 

Пример

MyOpenStruct.instance_methods(false) 
    #=> [:method_missing] 

obj1 = MyOpenStruct.new(name: "Dave") 
    #=> #<MyOpenStruct:0x00000102805b58 @values={:name=>"Dave"}> 
obj1.address = "1" 
    # create method 'address=' 
    #=> "1" 

MyOpenStruct.instance_methods(false) 
    #=> [:method_missing, :address=] 
obj2 = MyOpenStruct.new(name: "Mitzy") 
    #=> #<MyOpenStruct:0x00000101848878 @values={:name=>"Mitzy"}> 
obj2.address = 2 
    #=> 2 

obj2.address 
    # create method 'address' 
    # => 2 

MyOpenStruct.instance_methods(false) 
    $#=> [:method_missing, :address=, :address] 

obj1.instance_variable_get(:@values) 
    #=> {:name=>"Dave", :address=>"1"} 
obj2.instance_variable_get(:@values) 
    #=> {:name=>"Mitzy", :address=>2} 
0

Имя метода для метода setter должно иметь конечный =, поэтому вам необходимо определить метод с помощью name вместо base_name.

self.class.add_method_to_set(name) 
Смежные вопросы