Class: OpenStruct

Inherits:
Object
  • Object
show all
Defined in:
lib/ostruct.rb,
lib/ostruct/version.rb

Constant Summary collapse

InspectKey =

:nodoc:

:__inspect_key__
VERSION =
"0.2.0"

Instance Method Summary collapse

Constructor Details

#initialize(hash = nil) ⇒ OpenStruct

Creates a new OpenStruct object. By default, the resulting OpenStruct object will have no attributes.

The optional hash, if given, will generate attributes and values (can be a Hash, an OpenStruct or a Struct). For example:

require "ostruct"
hash = { "country" => "Australia", :capital => "Canberra" }
data = OpenStruct.new(hash)

data   # => #<OpenStruct country="Australia", capital="Canberra">


94
95
96
97
98
99
100
101
102
# File 'lib/ostruct.rb', line 94

def initialize(hash=nil)
  @table = {}
  if hash
    hash.each_pair do |k, v|
      k = k.to_sym
      @table[k] = v
    end
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(mid, *args) ⇒ Object

:nodoc:



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/ostruct.rb', line 207

def method_missing(mid, *args) # :nodoc:
  len = args.length
  if mname = mid[/.*(?==\z)/m]
    if len != 1
      raise ArgumentError, "wrong number of arguments (given #{len}, expected 1)", caller(1)
    end
    modifiable?[new_ostruct_member!(mname)] = args[0]
  elsif len == 0 # and /\A[a-z_]\w*\z/ =~ mid #
    if @table.key?(mid)
      new_ostruct_member!(mid) unless frozen?
      @table[mid]
    end
  elsif @table.key?(mid)
    raise ArgumentError, "wrong number of arguments (given #{len}, expected 0)"
  else
    begin
      super
    rescue NoMethodError => err
      err.backtrace.shift
      raise
    end
  end
end

Instance Method Details

#==(other) ⇒ Object

Compares this object and other for equality. An OpenStruct is equal to other when other is an OpenStruct and the two objects’ Hash tables are equal.

require "ostruct"
first_pet  = OpenStruct.new("name" => "Rowdy")
second_pet = OpenStruct.new(:name  => "Rowdy")
third_pet  = OpenStruct.new("name" => "Rowdy", :age => nil)

first_pet == second_pet   # => true
first_pet == third_pet    # => false


356
357
358
359
# File 'lib/ostruct.rb', line 356

def ==(other)
  return false unless other.kind_of?(OpenStruct)
  @table == other.table!
end

#[](name) ⇒ Object

:call-seq:

ostruct[name]  -> object

Returns the value of an attribute.

require "ostruct"
person = OpenStruct.new("name" => "John Smith", "age" => 70)
person[:age]   # => 70, same as person.age


241
242
243
# File 'lib/ostruct.rb', line 241

def [](name)
  @table[name.to_sym]
end

#[]=(name, value) ⇒ Object

:call-seq:

ostruct[name] = obj  -> obj

Sets the value of an attribute.

require "ostruct"
person = OpenStruct.new("name" => "John Smith", "age" => 70)
person[:age] = 42   # equivalent to person.age = 42
person.age          # => 42


256
257
258
# File 'lib/ostruct.rb', line 256

def []=(name, value)
  modifiable?[new_ostruct_member!(name)] = value
end

#delete_field(name) ⇒ Object

Removes the named field from the object. Returns the value that the field contained if it was defined.

require "ostruct"

person = OpenStruct.new(name: "John", age: 70, pension: 300)

person.delete_field("age")   # => 70
person                       # => #<OpenStruct name="John", pension=300>

Setting the value to nil will not remove the attribute:

person.pension = nil
person                 # => #<OpenStruct name="John", pension=nil>


305
306
307
308
309
310
311
312
313
314
# File 'lib/ostruct.rb', line 305

def delete_field(name)
  sym = name.to_sym
  begin
    singleton_class.remove_method(sym, "#{sym}=")
  rescue NameError
  end
  @table.delete(sym) do
    raise NameError.new("no field `#{sym}' in #{self}", sym)
  end
end

#dig(name, *names) ⇒ Object

:call-seq:

ostruct.dig(name, ...)  -> object

Extracts the nested value specified by the sequence of name objects by calling dig at each step, returning nil if any intermediate step is nil.

require "ostruct"
address = OpenStruct.new("city" => "Anytown NC", "zip" => 12345)
person  = OpenStruct.new("name" => "John Smith", "address" => address)

person.dig(:address, "zip")            # => 12345
person.dig(:business_address, "zip")   # => nil

data = OpenStruct.new(:array => [1, [2, 3]])

data.dig(:array, 1, 0)   # => 2
data.dig(:array, 0, 0)   # TypeError: Integer does not have #dig method


280
281
282
283
284
285
286
287
# File 'lib/ostruct.rb', line 280

def dig(name, *names)
  begin
    name = name.to_sym
  rescue NoMethodError
    raise TypeError, "#{name} is not a symbol nor a string"
  end
  @table.dig(name, *names)
end

#each_pairObject

:call-seq:

ostruct.each_pair {|name, value| block }  -> ostruct
ostruct.each_pair                         -> Enumerator

Yields all attributes (as symbols) along with the corresponding values or returns an enumerator if no block is given.

require "ostruct"
data = OpenStruct.new("country" => "Australia", :capital => "Canberra")
data.each_pair.to_a   # => [[:country, "Australia"], [:capital, "Canberra"]]


147
148
149
150
151
# File 'lib/ostruct.rb', line 147

def each_pair
  return to_enum(__method__) { @table.size } unless block_given?
  @table.each_pair{|p| yield p}
  self
end

#eql?(other) ⇒ Boolean

Compares this object and other for equality. An OpenStruct is eql? to other when other is an OpenStruct and the two objects’ Hash tables are eql?.



366
367
368
369
# File 'lib/ostruct.rb', line 366

def eql?(other)
  return false unless other.kind_of?(OpenStruct)
  @table.eql?(other.table!)
end

#freezeObject



197
198
199
200
# File 'lib/ostruct.rb', line 197

def freeze
  @table.each_key {|key| new_ostruct_member!(key)}
  super
end

#hashObject

Computes a hash code for this OpenStruct. Two OpenStruct objects with the same content will have the same hash code (and will compare using #eql?).

See also Object#hash.



376
377
378
# File 'lib/ostruct.rb', line 376

def hash
  @table.hash
end

#initialize_copy(orig) ⇒ Object

Duplicates an OpenStruct object’s Hash table.



105
106
107
108
# File 'lib/ostruct.rb', line 105

def initialize_copy(orig) # :nodoc:
  super
  @table = @table.dup
end

#inspectObject Also known as: to_s

Returns a string containing a detailed summary of the keys and values.



321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/ostruct.rb', line 321

def inspect
  ids = (Thread.current[InspectKey] ||= [])
  if ids.include?(object_id)
    detail = ' ...'
  else
    ids << object_id
    begin
      detail = @table.map do |key, value|
        " #{key}=#{value.inspect}"
      end.join(',')
    ensure
      ids.pop
    end
  end
  ['#<', self.class, detail, '>'].join
end

#marshal_dumpObject

Provides marshalling support for use by the Marshal library.



156
157
158
# File 'lib/ostruct.rb', line 156

def marshal_dump
  @table
end

#marshal_load(x) ⇒ Object

Provides marshalling support for use by the Marshal library.



163
164
165
# File 'lib/ostruct.rb', line 163

def marshal_load(x)
  @table = x
end

#respond_to_missing?(mid, include_private = false) ⇒ Boolean

:nodoc:



202
203
204
205
# File 'lib/ostruct.rb', line 202

def respond_to_missing?(mid, include_private = false) # :nodoc:
  mname = mid.to_s.chomp("=").to_sym
  defined?(@table) && @table.key?(mname) || super
end

#to_h(&block) ⇒ Object

call-seq:

ostruct.to_h                        -> hash
ostruct.to_h {|name, value| block } -> hash

Converts the OpenStruct to a hash with keys representing each attribute (as symbols) and their corresponding values.

If a block is given, the results of the block on each pair of the receiver will be used as pairs.

require "ostruct"
data = OpenStruct.new("country" => "Australia", :capital => "Canberra")
data.to_h   # => {:country => "Australia", :capital => "Canberra" }
data.to_h {|name, value| [name.to_s, value.upcase] }
            # => {"country" => "AUSTRALIA", "capital" => "CANBERRA" }


127
128
129
130
131
132
133
# File 'lib/ostruct.rb', line 127

def to_h(&block)
  if block_given?
    @table.to_h(&block)
  else
    @table.dup
  end
end