2016-11-08 4 views
-1

я создал проект, в котором у меня есть много классов, и каждый класс имеет похожую структуру:Руби - метапрограммирование, define_method, сушильный код

module Project 
    class ExampleClass 
    attr_accessor :title, :body, :elements_1, :elements_2 

    def initialize(attributes = {}) 
     self.title = attributes[:title] 
     self.body = attributes[:body] 
     self.elements1 = attributes[:elements1] || [] 
     self.elements2 = attributes[:elements2] || [] 
    end 

    def ==(other) 
     title == other.title && body == other.body && elements1 == other.elements1 && elements2 == other.elements2 
    end 
    end 
end 

только различие между классами являются его именем и имена elements1 и elements2 массивы. Мой наставник дал мне код DRY мой проект:

module Project 
    class Node 
    extend ActiveModel::Naming 
    include ActiveModel::Model 

    attr_accessor :body, :title 
    cattr_accessor :element_names 
    self.element_names = [] 

    def element 
     element_names.reduce([]) do |name, memo| 
     memo + send(name) 
     end 
    end 

    def ==(other) 
     body == other.body && title == other.title && element_names.all? { |name| element_by_name(name) == other.element_by_name(name) } 
    end 

    def element_by_name(name) 
     instance_variable_get("@#{name}") || instance_variable_set("@#{name}", []) 
    end 

    module ClassMethods 
     def element(name) 
     elements_names << name 

     attr_writer name 
     define_method(name) do 
      element_by_name(name) 
     end 
     end 
    end 
    extend ClassMethods 
    end 
end 

Это довольно выше моего уровня, я пытаюсь заставить его работать - каждый класс должен наследовать после Node. Я должен как-то передать атрибуты - elements1 и elements2. Я экспериментировал с

class ExampleClass < Node  
    cattr_accessor :element_names 
    self.element_names = [:elements1, elements2] 
end 

в моем ExampleClass передать имена массивов.

Я также пробовал с initialize, но я не могу заставить его работать. Буду благодарен за любую помощь.

ответ

1

Метод класса element используется для определения новых элементов.

Вы хотите что-то вроде

class ExampleClass < Node 
    element :elements1 
    element :elements2 
end 

Хотя существует серьезная проблема с кодом ваш наставник дал вам: имена элементов хранятся в переменной класса, что означает, что они будут распределены среди всех подклассов Node , То, что вы хотите, это экземпляр класса переменные.

Заменить этот

cattr_accessor :element_names 
self.element_names = [] 

с этим

def self.element_names 
    @element_names ||= [] 
end 

таким образом каждый подкласс сохраняет свой собственный массив имен элементов (а затем в качестве бонуса вам больше не нужно ничего из этого ActiveModel хлама).

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