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.
精彩评论