Yes, no, maybe? --- Original Message --- On Sunday 14 November 2004 06:16 pm, Yukihiro Matsumoto wrote: | Can you be more specific, preferably with code example? | | matz. Ah, what the hek! I pulled down 1.9 and modified. Below you will find code. Let me know if you'd like me to send code as attachment via private mail. Okay so the trick was simply to move everything into a mixin module (expect the initialize method). I also changed @table to @__table__ to help prevent possible name clash. The only other changed required was using @__table__ ||= {} in a number of places (too bad no better way to do that). Finally I just called the module OpenStructable --seems like a good a name as any. Also, I found what looks to be a bug in current 1.9 version. The initialize method wasn't calling the newly added #new_ostruct_member method. I fixed and added #update method. Also, is #new_ostruct_member supposed to be private? Currently it is public. If it remains public, I recommend a more generic name. Not sure what though. Hope you like, T. ------------ # # = ostruct.rb: OpenStruct implementation # # Author:: Yukihiro Matsumoto # Contributor:: Thomas Sawyer # Documentation:: Gavin Sinclair # # OpenStruct allows the creation of data objects with arbitrary attributes. # See OpenStruct for an example. OpenStruct is implemented with a resuable # mixin module OpenStructable. # # OpensStructable is a mixin module which can provide OpenStruct behavior to # any class or object. # # require 'ostruct' # # record = Object.new # record.extend OpenStructable # # record.name = "John Smith" # record.age = 70 # record.pension = 300 # # puts record.name # -> "John Smith" # puts record.address # -> nil # module OpenStructable # Duplicate an OpenStruct object members. def initialize_copy(orig) super @__table__ = @__table__.dup end def new_ostruct_member(name) self.instance_eval %{ def #{name}; @__table__[:#{name}]; end def #{name}=(x); @__table__[:#{name}] = x; end } end # # Generate additional attributes and values. # def update(hash) @__table__ ||= {} if hash for k,v in hash @__table__[k.to_sym] = v new_ostruct_member(k) end end end def method_missing(mid, *args) # :nodoc: mname = mid.id2name len = args.length if mname =~ /=$/ if len != 1 raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1) end if self.frozen? raise TypeError, "can't modify frozen #{self.class}", caller(1) end mname.chop! @__table__ ||= {} @__table__[mname.intern] = args[0] self.new_ostruct_member(mname) elsif len == 0 @__table__ ||= {} @__table__[mid] else raise NoMethodError, "undefined method `#{mname}' for #{self}", caller(1) end end # # Remove the named field from the object. # def delete_field(name) @__table__ ||= {} @__table__.delete name.to_sym end # # Returns a string containing a detailed summary of the keys and values. # def inspect str = "<#{self.class}" for k,v in (@__table__ ||= {}) str << " #{k}=#{v.inspect}" end str << ">" end def __table__ # :nodoc: @__table__ ||= {} end protected :__table__ # # Compare this object and +other+ for equality. # def ==(other) return false unless(other.kind_of?(OpenStruct)) return @__table__ == other.table end end # # OpenStruct allows you to create data objects and set arbitrary attributes. # For example: # # require 'ostruct' # # record = OpenStruct.new # record.name = "John Smith" # record.age = 70 # record.pension = 300 # # puts record.name # -> "John Smith" # puts record.address # -> nil # # It is like a hash with a different way to access the data. In fact, it is # implemented with a hash, and you can initialize it with one. # # hash = { "country" => "Australia", :population => 20_000_000 } # data = OpenStruct.new(hash) # # p data # -> <OpenStruct country="Australia" population=20000000> # class OpenStruct include OpenStructable # # Create a new OpenStruct object. The optional +hash+, if given, will # generate attributes and values. For example. # # require 'ostruct' # hash = { "country" => "Australia", :population => 20_000_000 } # data = OpenStruct.new(hash) # # p data # -> <OpenStruct country="Australia" population=20000000> # # By default, the resulting OpenStruct object will have no attributes. # def initialize(hash=nil) update(hash) end end