Tag unstable CGI specs.
[rbx.git] / lib / ostruct.rb
blob6af5bbdac0a2406e6eac575b45ebfbf46c0db8ec
2 # = ostruct.rb: OpenStruct implementation
4 # Author:: Yukihiro Matsumoto
5 # Documentation:: Gavin Sinclair
7 # OpenStruct allows the creation of data objects with arbitrary attributes.
8 # See OpenStruct for an example.
12 # OpenStruct allows you to create data objects and set arbitrary attributes.
13 # For example:
15 #   require 'ostruct' 
17 #   record = OpenStruct.new
18 #   record.name    = "John Smith"
19 #   record.age     = 70
20 #   record.pension = 300
21 #   
22 #   puts record.name     # -> "John Smith"
23 #   puts record.address  # -> nil
25 # It is like a hash with a different way to access the data.  In fact, it is
26 # implemented with a hash, and you can initialize it with one.
28 #   hash = { "country" => "Australia", :population => 20_000_000 }
29 #   data = OpenStruct.new(hash)
31 #   p data        # -> <OpenStruct country="Australia" population=20000000>
33 class OpenStruct
34   #
35   # Create a new OpenStruct object.  The optional +hash+, if given, will
36   # generate attributes and values.  For example.
37   #
38   #   require 'ostruct'
39   #   hash = { "country" => "Australia", :population => 20_000_000 }
40   #   data = OpenStruct.new(hash)
41   #
42   #   p data        # -> <OpenStruct country="Australia" population=20000000>
43   #
44   # By default, the resulting OpenStruct object will have no attributes. 
45   #
46   def initialize(hash=nil)
47     @table = {}
48     if hash
49       for k,v in hash
50         @table[k.to_sym] = v
51         new_ostruct_member(k)
52       end
53     end
54   end
56   # Duplicate an OpenStruct object members. 
57   def initialize_copy(orig)
58     super
59     @table = @table.dup
60   end
62   def marshal_dump
63     @table
64   end
65   def marshal_load(x)
66     @table = x
67     @table.each_key{|key| new_ostruct_member(key)}
68   end
70   def new_ostruct_member(name)
71     name = name.to_sym
72     unless self.respond_to?(name)
73       meta = class << self; self; end
74       meta.send(:define_method, name) { @table[name] }
75       meta.send(:define_method, :"#{name}=") { |x| @table[name] = x }
76     end
77   end
79   def method_missing(mid, *args) # :nodoc:
80     mname = mid.id2name
81     len = args.length
82     if mname =~ /=$/
83       if len != 1
84         raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
85       end
86       if self.frozen?
87         raise TypeError, "can't modify frozen #{self.class}", caller(1)
88       end
89       mname.chop!
90       self.new_ostruct_member(mname)
91       @table[mname.intern] = args[0]
92     elsif len == 0
93       @table[mid]
94     else
95       raise NoMethodError, "undefined method `#{mname}' for #{self}", caller(1)
96     end
97   end
99   #
100   # Remove the named field from the object.
101   #
102   def delete_field(name)
103     @table.delete name.to_sym
104   end
106   InspectKey = :__inspect_key__ # :nodoc:
108   #
109   # Returns a string containing a detailed summary of the keys and values.
110   #
111   def inspect
112     str = "#<#{self.class}"
114     Thread.current[InspectKey] ||= []
115     if Thread.current[InspectKey].include?(self) then
116       str << " ..."
117     else
118       first = true
119       for k,v in @table
120         str << "," unless first
121         first = false
123         Thread.current[InspectKey] << v
124         begin
125           str << " #{k}=#{v.inspect}"
126         ensure
127           Thread.current[InspectKey].pop
128         end
129       end
130     end
132     str << ">"
133   end
134   alias :to_s :inspect
136   attr_reader :table # :nodoc:
137   protected :table
139   #
140   # Compare this object and +other+ for equality.
141   #
142   def ==(other)
143     return false unless(other.kind_of?(OpenStruct))
144     return @table == other.table
145   end