#the object to be added to ObjectContainer
# :label is used to defined accessor method name
# :value is used for return value of to_s
class MyObject < Struct.new(:label, :value)
def to_s
self.value
end
end
#container class to hold instances of MyObject
class ObjectContainer
def add_object(q)
mod = Module.new do
define_method q.label do; q; end
end
extend mod
end
class_eval{ alias :<< add_object }
end
container = ObjectContainer.new
#add 5 objects to container
(1..5).each {|n|
obj = MyObject.new
obj.label = "label#{n}" #accessor name will be label1, label2, etc
obj.value = "value #{n}"
container << obj
}
#example usage of objects
container.instance_eval {
(1..5).each {|n|
o = send("label#{n}")
puts "obj #{n}: #{o.label} = #{o}"
}
}
Refactorings
No refactoring yet !
Adam
October 7, 2009, October 07, 2009 20:10, permalink
class ObjectContainer < Hash
def <<(object)
self[object.label] = object
end
def method_missing(method, *args, &block)
if method.to_s =~ /(.*)=/
self[$1] = args.first
else
self[method.to_s] || super
end
end
end
(1..5).each do |n|
o = container.send("label#{n}")
puts "obj #{n}: #{o.label} = #{o}"
end
mainej
October 8, 2009, October 08, 2009 04:03, permalink
I think Adam is implying that you don't need any fancy define_method/instance_eval programming to get the functionality you seem to want: add an object to a container, then retrieve it later with its label. Maybe iterate through all the objects in the container, too. That sounds very much like a hash, so ... inherit from Hash.
class ObjectContainer < Hash
def <<(object)
self[object.label] = object
end
end
class MyObject < Struct.new(:label, :value)
def to_s
self.value
end
end
container = ObjectContainer.new
#add 5 objects to container
(1..5).each {|n|
obj = MyObject.new
obj.label = "label#{n}"
obj.value = "value #{n}"
container << obj
}
o = container["label3"]
puts "obj #{o}"
#even more interesting:
container.each do |label, object|
puts "label: #{label} obj: #{object}"
end
#and other good stuff you get from hashes
puts container.sort_by { |label, object| label }.inspect
Ben Marini
October 8, 2009, October 08, 2009 06:53, permalink
Seems to me like you might as well just use the built-in Hash.
# Method 1 (Using ObjectContainer)
obj = MyObject.new("label", "value")
container = ObjectContainer.new
container << obj
puts "#{container.label}"
# Method 2 (using built-in Hash class)
container = {}
container[:label] = "value"
puts "#{container[:label]}"
Ben Marini
October 8, 2009, October 08, 2009 06:57, permalink
Is the only point of ObjectContainer to provide a different syntax for accessing it's elements? You could also use a Hash with your "MyObject".
c = {}
c[:label1] = MyObject.new("label1", "value1")
loomis53.myopenid.com
October 9, 2009, October 09, 2009 18:05, permalink
Actually I'm toying with the idea of implementing a simple DSL. But my purpose here is more for learning than anything else.
Initially I thought using define_method was a cleaner approach than method_missing, with define_method having less chance for unintended side effects. But method_missing is much simpler (of course as stated I could just as well use a hash).
Out of curiosity, I ran some benchmarks and my define_method approach seems to be about 10 times slower than method_missing, which is something else to keep in mind.
Thanks for the feedback, everyone.
Ben Marini
October 11, 2009, October 11, 2009 20:50, permalink
Jim Weirich's builder gem for xml is a really good example of a DSL that uses method_missing.
Pharmg156
November 1, 2009, November 01, 2009 18:49, permalink
Very nice site! cheap cialis http://apeoixy.com/xqqava/4.html
Very nice site! cheap cialis http://apeoixy.com/xqqava/4.html
This is a trivial example of what I'm wanting to do. My focus is with the definition of ObjectContainer. Currently the method add_object uses an anonymous Module to extend an instance with the a method that returns that object passed in.
I feel like there should be a better way to accomplish this, but am fairly new to ruby especially when it comes to things of this nature.