【问题标题】:How do I dynamically update an instance array to hold a list of dynamic methods on instantiation?如何动态更新实例数组以保存实例化的动态方法列表?
【发布时间】:2011-01-26 11:05:10
【问题描述】:

我正在尝试基于 xml 映射动态定义方法。这真的很好用。但是我想创建一个实例变量,它是动态定义的方法的数组。

我的代码看起来像这样

  def xml_attr_reader(*args)
    xml_list = ""
    args.each do |arg|
      string_val = "def #{arg}; " +
                   "   xml_mapping.#{arg}; " +
                   "end; "
      self.class_eval string_val
      xml_hash = xml_list + "'#{arg}',"
    end

    self.class_eval "@xml_attributes = [] if @xml_attributes.nil?;" +
                    "@xml_attributes = @xml_attributes + [#{xml_list}];" +
                    "puts 'xml_attrs = ' + @xml_attributes.to_s;" +
                    "def xml_attributes;" +
                    "  puts 'xml_attrs = ' + @xml_attributes.to_s;" +
                    "  @xml_attributes;" +
                    "end"
  end

所以一切正常,除非我在一个实例上调用 xml_attributes 它返回 null(并打印出 'xml_attrs = ')。

虽然定义之前的 puts 实际上打印出正确的数组。 (当我实例化实例时)


更新 - kandadaboggu 下面的解决方案有效。我真的没有完全解释我的问题,所以这里有更多信息。我想在扩展 XmlConfig 的类中调用 xml_attr_reader。基本上我希望它以与活动记录的 attr_reader 相同的方式工作。

采取kandadaboggu的解决方案

class XmlConfig
  def xml_attr_reader(*args)
    args.each do |arg|
      class_eval(<<-RUBY, __FILE__, __LINE__)
        def #{arg}
          xml_mapping.#{arg}
        end
      RUBY
    end
    unless respond_to?(:xml_attributes)
      class_eval(<<-RUBY, __FILE__, __LINE__)
        attr_accessor :xml_attributes
      RUBY
    end
    (self.xml_attributes ||=[]).concat(args)
  end
end

config = XmlConfig.new
config.xml_attr_reader("age", "name")
config.age #=> age
config.name #=> name
config.xml_attributes #=> ["age", "name" ]

config.xml_attr_reader("city")
config.city #=> city
config.xml_attributes #=> ["age", "name", "city"]

我真正想要的是这个(虽然我将 XmlConfig 作为模块而不是我的版本中的类)

class Event < ActiveWhatever
  extend XmlConfig
  xml_attr_reader :age, :name
  xml_attr_reader :email, :location
end


class PrivateEvent < Event
  xml_attr_reader :owner, :password
end

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    试试这个:

    class XmlConfig
      def xml_attr_reader(*args)
        args.each do |arg|
          class_eval(<<-RUBY, __FILE__, __LINE__)
            def #{arg}
              xml_mapping.#{arg}
            end
          RUBY
        end
        unless respond_to?(:xml_attributes)
          class_eval(<<-RUBY, __FILE__, __LINE__)
            attr_accessor :xml_attributes
          RUBY
        end
        (self.xml_attributes ||=[]).concat(args)
      end
    end
    

    现在您可以进行以下调用:

    config = XmlConfig.new
    config.xml_attr_reader("age", "name")
    config.age #=> age
    config.name #=> name
    config.xml_attributes #=> ["age", "name" ]
    
    config.xml_attr_reader("city")
    config.city #=> city
    config.xml_attributes #=> ["age", "name", "city"]
    

    注意:所有方法都是实例方法。

    【讨论】:

    • 所以这几乎可以工作。它适用于类中一次调用 xml_attr_reader 的情况。但是,如果我有 may 属性,我会多次调用该方法以保持我的行
    • 我已进行更改以解决此问题。让我知道它是否适合您。
    • 感谢您的尝试,但它不起作用。我在 if (self.respond_to?(:xml_attributes)) 之后添加了一个打印语句,但它永远不会被调用。
    • 我的代码中有语法错误。我有一个额外的支架。如果您的代码有那个括号,我建议您尝试使用新代码。我将在今天晚些时候对此进行测试并通知您。
    • 我已经修复并测试了代码。这个对我有用。让我知道它是否适合您。
    【解决方案2】:

    这是您修改后问题的解决方案。

       module XmlConfig
        def self.included(base)
          base.extend ClassMethods
          base.class_inheritable_array(:xml_attributes)
          base.xml_attributes = []
        end
    
        module ClassMethods    
          def xml_attr_reader(*args)
            args.each do |arg|
              self.class_eval(<<-RUBY, __FILE__, __LINE__)
                def #{arg}
                  xml_mapping.#{arg}
                end
              RUBY
            end
            self.xml_attributes = args
          end
        end
      end
    

    现在您可以include 任何类中的模块。

      class Event < ActiveWhatever
        include XmlConfig
        xml_attr_reader :age, :name
        xml_attr_reader :email, :location
      end
    
    
      class PrivateEvent < Event
        xml_attr_reader :owner, :password
      end
    

    让我们测试一下结果:

    Event.xml_attributes        # ->[:age, :name, :email, :location]
    PrivateEvent.xml_attributes # ->[:age, :name, :email, :location, 
                                #                 :owner, :password]
    
    e= Event.new(...)
    e.age     # -> 27
    pe= PrivateEvent.new(...)
    pe.owner  # -> Will
    

    【讨论】:

    • 优秀的答案。奇迹般有效。感谢您坚持这一点,非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-16
    • 1970-01-01
    • 2010-09-14
    • 2014-01-26
    • 2019-11-08
    • 1970-01-01
    相关资源
    最近更新 更多