* io.c (rb_open_file): encoding in mode string was ignored if perm is
[ruby-svn.git] / lib / ostruct.rb
blobf3ed01524b82334b0bc087828ce00a620fac64e2
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       class << self; self; end.class_eval do
74         define_method(name) { @table[name] }
75         define_method(:"#{name}=") { |x| @table[name] = x }
76       end
77     end
78   end
80   def method_missing(mid, *args) # :nodoc:
81     mname = mid.id2name
82     len = args.length
83     if mname =~ /=$/
84       if len != 1
85         raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
86       end
87       if self.frozen?
88         raise TypeError, "can't modify frozen #{self.class}", caller(1)
89       end
90       mname.chop!
91       self.new_ostruct_member(mname)
92       @table[mname.intern] = args[0]
93     elsif len == 0
94       @table[mid]
95     else
96       raise NoMethodError, "undefined method `#{mname}' for #{self}", caller(1)
97     end
98   end
100   #
101   # Remove the named field from the object.
102   #
103   def delete_field(name)
104     @table.delete name.to_sym
105   end
107   InspectKey = :__inspect_key__ # :nodoc:
109   #
110   # Returns a string containing a detailed summary of the keys and values.
111   #
112   def inspect
113     str = "#<#{self.class}"
115     Thread.current[InspectKey] ||= []
116     if Thread.current[InspectKey].include?(self) then
117       str << " ..."
118     else
119       first = true
120       for k,v in @table
121         str << "," unless first
122         first = false
124         Thread.current[InspectKey] << v
125         begin
126           str << " #{k}=#{v.inspect}"
127         ensure
128           Thread.current[InspectKey].pop
129         end
130       end
131     end
133     str << ">"
134   end
135   alias :to_s :inspect
137   attr_reader :table # :nodoc:
138   protected :table
140   #
141   # Compare this object and +other+ for equality.
142   #
143   def ==(other)
144     return false unless(other.kind_of?(OpenStruct))
145     return @table == other.table
146   end