Project

General

Profile

Bug #13159 ยป doc_ostruct.patch

stomar (Marcus Stollsteimer), 02/03/2017 09:01 PM

View differences:

lib/ostruct.rb
15 15
# accomplished by using Ruby's metaprogramming to define methods on the class
16 16
# itself.
17 17
#
18
# == Examples:
18
# == Examples
19 19
#
20
#   require 'ostruct'
20
#   require "ostruct"
21 21
#
22 22
#   person = OpenStruct.new
23
#   person.name    = "John Smith"
24
#   person.age     = 70
25
#   person.pension = 300
23
#   person.name = "John Smith"
24
#   person.age  = 70
26 25
#
27
#   puts person.name     # -> "John Smith"
28
#   puts person.age      # -> 70
29
#   puts person.address  # -> nil
26
#   person.name      # => "John Smith"
27
#   person.age       # => 70
28
#   person.address   # => nil
30 29
#
31
# An OpenStruct employs a Hash internally to store the methods and values and
32
# can even be initialized with one:
30
# An OpenStruct employs a Hash internally to store the attributes and values
31
# and can even be initialized with one:
33 32
#
34
#   australia = OpenStruct.new(:country => "Australia", :population => 20_000_000)
35
#   p australia   # -> <OpenStruct country="Australia" population=20000000>
33
#   australia = OpenStruct.new(:country => "Australia", :capital => "Canberra")
34
#     # => #<OpenStruct country="Australia", capital="Canberra">
36 35
#
37
# Hash keys with spaces or characters that would normally not be able to use for
38
# method calls (e.g. ()[]*) will not be immediately available on the
39
# OpenStruct object as a method for retrieval or assignment, but can be still be
40
# reached through the Object#send method.
36
# Hash keys with spaces or characters that could normally not be used for
37
# method calls (e.g. <code>()[]*</code>) will not be immediately available
38
# on the OpenStruct object as a method for retrieval or assignment, but can
39
# still be reached through the Object#send method.
41 40
#
42 41
#   measurements = OpenStruct.new("length (in inches)" => 24)
43
#   measurements.send("length (in inches)")  # -> 24
42
#   measurements.send("length (in inches)")   # => 24
44 43
#
45
#   data_point = OpenStruct.new(:queued? => true)
46
#   data_point.queued?                       # -> true
47
#   data_point.send("queued?=",false)
48
#   data_point.queued?                       # -> false
44
#   message = OpenStruct.new(:queued? => true)
45
#   message.queued?                           # => true
46
#   message.send("queued?=", false)
47
#   message.queued?                           # => false
49 48
#
50
# Removing the presence of a method requires the execution the delete_field
51
# method as setting the property value to +nil+ will not remove the method.
49
# Removing the presence of an attribute requires the execution of the
50
# delete_field method as setting the property value to +nil+ will not
51
# remove the attribute.
52 52
#
53
#   first_pet = OpenStruct.new(:name => 'Rowdy', :owner => 'John Smith')
54
#   first_pet.owner = nil
55
#   second_pet = OpenStruct.new(:name => 'Rowdy')
53
#   first_pet  = OpenStruct.new(:name => "Rowdy", :owner => "John Smith")
54
#   second_pet = OpenStruct.new(:name => "Rowdy")
56 55
#
57
#   first_pet == second_pet   # -> false
56
#   first_pet.owner = nil
57
#   first_pet                 # => #<OpenStruct name="Rowdy", owner=nil>
58
#   first_pet == second_pet   # => false
58 59
#
59 60
#   first_pet.delete_field(:owner)
60
#   first_pet == second_pet   # -> true
61
#   first_pet                 # => #<OpenStruct name="Rowdy">
62
#   first_pet == second_pet   # => true
61 63
#
62 64
#
63
# == Implementation:
65
# == Implementation
64 66
#
65 67
# An OpenStruct utilizes Ruby's method lookup structure to find and define the
66
# necessary methods for properties. This is accomplished through the method
67
# method_missing and define_method.
68
# necessary methods for properties. This is accomplished through the methods
69
# method_missing and define_singleton_method.
68 70
#
69 71
# This should be a consideration if there is a concern about the performance of
70 72
# the objects that are created, as there is much more overhead in the setting
......
83 85
  # (can be a Hash, an OpenStruct or a Struct).
84 86
  # For example:
85 87
  #
86
  #   require 'ostruct'
87
  #   hash = { "country" => "Australia", :population => 20_000_000 }
88
  #   require "ostruct"
89
  #   hash = { "country" => "Australia", :capital => "Canberra" }
88 90
  #   data = OpenStruct.new(hash)
89 91
  #
90
  #   p data        # -> <OpenStruct country="Australia" population=20000000>
92
  #   data   # => #<OpenStruct country="Australia", capital="Canberra">
91 93
  #
92 94
  def initialize(hash=nil)
93 95
    @table = {}
......
99 101
    end
100 102
  end
101 103

  
102
  # Duplicate an OpenStruct object members.
103
  def initialize_copy(orig)
104
  # Duplicates an OpenStruct object's Hash table.
105
  def initialize_copy(orig) # :nodoc:
104 106
    super
105 107
    @table = @table.dup
106 108
  end
107 109

  
108 110
  #
109 111
  # Converts the OpenStruct to a hash with keys representing
110
  # each attribute (as symbols) and their corresponding values
112
  # each attribute (as symbols) and their corresponding values.
111 113
  # Example:
112 114
  #
113
  #   require 'ostruct'
114
  #   data = OpenStruct.new("country" => "Australia", :population => 20_000_000)
115
  #   data.to_h   # => {:country => "Australia", :population => 20000000 }
115
  #   require "ostruct"
116
  #   data = OpenStruct.new("country" => "Australia", :capital => "Canberra")
117
  #   data.to_h   # => {:country => "Australia", :capital => "Canberra" }
116 118
  #
117 119
  def to_h
118 120
    @table.dup
119 121
  end
120 122

  
121 123
  #
122
  # Yields all attributes (as a symbol) along with the corresponding values
123
  # or returns an enumerator if not block is given.
124
  # :call-seq:
125
  #   ostruct.each_pair {|name, value| block }  -> ostruct
126
  #   ostruct.each_pair                         -> Enumerator
127
  #
128
  # Yields all attributes (as symbols) along with the corresponding values
129
  # or returns an enumerator if no block is given.
124 130
  # Example:
125 131
  #
126
  #   require 'ostruct'
127
  #   data = OpenStruct.new("country" => "Australia", :population => 20_000_000)
128
  #   data.each_pair.to_a  # => [[:country, "Australia"], [:population, 20000000]]
132
  #   require "ostruct"
133
  #   data = OpenStruct.new("country" => "Australia", :capital => "Canberra")
134
  #   data.each_pair.to_a   # => [[:country, "Australia"], [:capital, "Canberra"]]
129 135
  #
130 136
  def each_pair
131 137
    return to_enum(__method__) { @table.size } unless block_given?
......
189 195
    super
190 196
  end
191 197

  
192
  def respond_to_missing?(mid, include_private = false)
198
  def respond_to_missing?(mid, include_private = false) # :nodoc:
193 199
    mname = mid.to_s.chomp("=").to_sym
194 200
    @table.key?(mname) || super
195 201
  end
......
213 219
    end
214 220
  end
215 221

  
216
  # Returns the value of a member.
222
  # Returns the value of an attribute.
217 223
  #
218
  #   person = OpenStruct.new('name' => 'John Smith', 'age' => 70)
219
  #   person[:age] # => 70, same as ostruct.age
224
  #   require "ostruct"
225
  #   person = OpenStruct.new("name" => "John Smith", "age" => 70)
226
  #   person[:age]   # => 70, same as person.age
220 227
  #
221 228
  def [](name)
222 229
    @table[name.to_sym]
223 230
  end
224 231

  
225 232
  #
226
  # Sets the value of a member.
233
  # Sets the value of an attribute.
227 234
  #
228
  #   person = OpenStruct.new('name' => 'John Smith', 'age' => 70)
229
  #   person[:age] = 42 # => equivalent to ostruct.age = 42
230
  #   person.age # => 42
235
  #   require "ostruct"
236
  #   person = OpenStruct.new("name" => "John Smith", "age" => 70)
237
  #   person[:age] = 42   # equivalent to person.age = 42
238
  #   person.age          # => 42
231 239
  #
232 240
  def []=(name, value)
233 241
    modifiable?[new_ostruct_member!(name)] = value
234 242
  end
235 243

  
236 244
  #
237
  # Retrieves the value object corresponding to the each +name+
238
  # objects repeatedly.
245
  # Extracts the nested value specified by the sequence of +name+
246
  # objects by calling +dig+ at each step, returning +nil+ if any
247
  # intermediate step is +nil+.
248
  #
249
  #   require "ostruct"
250
  #   address = OpenStruct.new("city" => "Anytown NC", "zip" => 12345)
251
  #   person  = OpenStruct.new("name" => "John Smith", "address" => address)
252
  #
253
  #   person.dig(:address, "zip")            # => 12345
254
  #   person.dig(:business_address, "zip")   # => nil
255
  #
256
  #   data = OpenStruct.new(:array => [1, [2, 3]])
239 257
  #
240
  #   address = OpenStruct.new('city' => "Anytown NC", 'zip' => 12345)
241
  #   person = OpenStruct.new('name' => 'John Smith', 'address' => address)
242
  #   person.dig(:address, 'zip') # => 12345
243
  #   person.dig(:business_address, 'zip') # => nil
258
  #   data.dig(:array, 1, 0)   # => 2
259
  #   data.dig(:array, 0, 0)   # TypeError: Integer does not have #dig method
244 260
  #
245 261
  def dig(name, *names)
246 262
    begin
......
252 268
  end
253 269

  
254 270
  #
255
  # Remove the named field from the object. Returns the value that the field
271
  # Removes the named field from the object. Returns the value that the field
256 272
  # contained if it was defined.
257 273
  #
258
  #   require 'ostruct'
274
  #   require "ostruct"
259 275
  #
260
  #   person = OpenStruct.new('name' => 'John Smith', 'age' => 70)
276
  #   person = OpenStruct.new(name: "John", age: 70, pension: 300)
261 277
  #
262
  #   person.delete_field('name')  # => 'John Smith'
278
  #   person.delete_field("age")   # => 70
279
  #   person                       # => #<OpenStruct name="John", pension=300>
280
  #
281
  # Setting the value to +nil+ will not remove the attribute:
282
  #
283
  #   person.pension = nil
284
  #   person                 # => #<OpenStruct name="John", pension=nil>
263 285
  #
264 286
  def delete_field(name)
265 287
    sym = name.to_sym
......
309 331
  # +other+ when +other+ is an OpenStruct and the two objects' Hash tables are
310 332
  # equal.
311 333
  #
334
  #   require "ostruct"
335
  #   first_pet  = OpenStruct.new("name" => "Rowdy")
336
  #   second_pet = OpenStruct.new(:name  => "Rowdy")
337
  #   third_pet  = OpenStruct.new("name" => "Rowdy", :age => nil)
338
  #
339
  #   first_pet == second_pet   # => true
340
  #   first_pet == third_pet    # => false
341
  #
312 342
  def ==(other)
313 343
    return false unless other.kind_of?(OpenStruct)
314 344
    @table == other.table!
......
324 354
    @table.eql?(other.table!)
325 355
  end
326 356

  
327
  # Compute a hash-code for this OpenStruct.
328
  # Two hashes with the same content will have the same hash code
329
  # (and will be eql?).
357
  # Computes a hash code for this OpenStruct.
358
  # Two OpenStruct objects with the same content will have the same hash code
359
  # (and will compare using #eql?).
360
  #
361
  # See also Object#hash.
330 362
  def hash
331 363
    @table.hash
332 364
  end