Ruby - 元编程,define_method,DRYing代码

时间:2016-11-08 15:23:42

标签: ruby

我创建了一个项目,其中我有很多类,每个类都有一个类似的结构:

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

只有类之间的差异是elements1elements2数组的名称和名称。 我的导师给了我代码来干我的项目:

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之后继承。我必须以某种方式传递属性 - elements1elements2。我正在尝试

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

在我的ExampleClass中传递数组的名称。

我也试过了initialize,但我无法让它发挥作用。我会感激任何帮助。

1 个答案:

答案 0 :(得分: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任何一个。)