12
# OpenStruct allows you to create data objects and set arbitrary attributes.
12
# An OpenStruct is a data structure, similar to a Hash, that allows the
13
# definition of arbitrary attributes with their accompanying values. This is
14
# accomplished by using Ruby's metaprogramming to define methods on the class
15
19
# require 'ostruct'
17
# record = OpenStruct.new
18
# record.name = "John Smith"
20
# record.pension = 300
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>
21
# person = OpenStruct.new
22
# person.name = "John Smith"
24
# person.pension = 300
26
# puts person.name # -> "John Smith"
27
# puts person.age # -> 70
28
# puts person.address # -> nil
30
# An OpenStruct employs a Hash internally to store the methods and values and
31
# can even be initialized with one:
33
# country_data = { :country => "Australia", :population => 20_000_000 }
34
# australia = OpenStruct.new(country_data)
35
# p australia # -> <OpenStruct country="Australia" population=20000000>
37
# You may also define the hash in the initialization call:
39
# australia = OpenStruct.new(:country => "Australia", :population => 20_000_000)
40
# p australia # -> <OpenStruct country="Australia" population=20000000>
42
# Hash keys with spaces or characters that would normally not be able to use for
43
# method calls (e.g. ()[]*) will not be immediately available on the
44
# OpenStruct object as a method for retrieval or assignment, but can be still be
45
# reached through the Object#send method.
47
# measurements = OpenStruct.new("length (in inches)" => 24)
48
# measurements.send("length (in inches)") # -> 24
50
# data_point = OpenStruct.new(:queued? => true)
51
# data_point.queued? # -> true
52
# data_point.send("queued?=",false)
53
# data_point.queued? # -> false
55
# Removing the presence of a method requires the execution the delete_field
56
# method as setting the property value to +nil+ will not remove the method.
58
# first_pet = OpenStruct.new(:name => 'Rowdy', :owner => 'John Smith')
59
# first_pet.owner = nil
60
# second_pet = OpenStruct.new(:name => 'Rowdy')
62
# first_pet == second_pet # -> false
64
# first_pet.delete_field(:owner)
65
# first_pet == second_pet # -> true
70
# An OpenStruct utilizes Ruby's method lookup structure to and find and define
71
# the necessary methods for properties. This is accomplished through the method
72
# method_missing and define_method.
74
# This should be a consideration if there is a concern about the performance of
75
# the objects that are created, as there is much more overhead in the setting
76
# of these properties compared to using a Hash or a Struct.
35
# Create a new OpenStruct object. The optional +hash+, if given, will
36
# generate attributes and values. For example.
80
# Creates a new OpenStruct object. By default, the resulting OpenStruct
81
# object will have no attributes.
83
# The optional +hash+, if given, will generate attributes and values.
38
86
# require 'ostruct'
39
87
# hash = { "country" => "Australia", :population => 20_000_000 }
59
111
@table = @table.dup
115
# Provides marshalling support for use by the Marshal library. Returning the
116
# underlying Hash table that contains the functions defined as the keys and
117
# the values assigned to them.
121
# person = OpenStruct.new
122
# person.name = 'John Smith'
125
# person.marshal_dump # => { :name => 'John Smith', :age => 70 }
132
# Provides marshalling support for use by the Marshal library. Accepting
133
# a Hash of keys and values which will be used to populate the internal table
137
# event = OpenStruct.new
138
# hash = { 'time' => Time.now, 'title' => 'Birthday Party' }
139
# event.marshal_load(hash)
140
# event.title # => 'Birthday Party'
65
142
def marshal_load(x)
67
144
@table.each_key{|key| new_ostruct_member(key)}
148
# #modifiable is used internally to check if the OpenStruct is able to be
149
# modified before granting access to the internal Hash table to be augmented.
72
153
@modifiable = true
91
177
def method_missing(mid, *args) # :nodoc:
92
178
mname = mid.id2name
180
if mname.chomp!('=') && mid != :[]=
96
182
raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
98
184
modifiable[new_ostruct_member(mname)] = args[0]
185
elsif len == 0 && mid != :[]
102
raise NoMethodError, "undefined method `#{mname}' for #{self}", caller(1)
188
raise NoMethodError, "undefined method `#{mid}' for #{self}", caller(1)
107
# Remove the named field from the object.
193
# Remove the named field from the object. Returning the value that the field
194
# contained if it has defined.
198
# person = OpenStruct.new('name' => 'John Smith', 'age' => 70)
200
# person.delete_field('name') # => 'John Smith'
109
202
def delete_field(name)
110
@table.delete name.to_sym
205
singleton_class.__send__(:remove_method, sym, "#{name}=")
113
208
InspectKey = :__inspect_key__ # :nodoc: