开发者

How do I dynamically update an instance array to hold a list of dynamic methods on instantiation?

I am trying to dynamically define methods based on xml mappings. This works really well. However I want to create an instance variable that is a array of the dynamically defined methods.

My code looks something like this

  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

So everything w开发者_如何学运维orks except when I call xml_attributes on an instance it return null (and prints out 'xml_attrs = ').

While the puts before the definition actually prints out the correct array. (when I instantiate the instance)


Update - kandadaboggu solution below works. I really did not explain my question fully though so here is more information. I want to call xml_attr_reader within a class that extends XmlConfig. Basically I want it to work in the same way as attr_reader of active record.

Taking kandadaboggu solution of

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

and

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"]

What I really want is this (where I had XmlConfig as a module not a Class in my version though)

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


Here is a solution for your amended question.

   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

Now you can include the module in any class.

  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

Let us test the result:

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


Try this:

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

Now you can make the following calls:

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"]

Note: All the methods are instance methods.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜