T. Onoma
11/15/2004 4:35:00 AM
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 & 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