Class: OpenStruct2
- Inherits:
- BasicObject
- Defined in:
- lib/ostruct2.rb
Overview
OpenStruct2 is a better OpenStruct class.
To demonstrate the weakness of the original OpenStruct, try this IRB session:
irb(main):001:0> o = OpenStruct.new
=> #<OpenStruct>
irb(main):002:0> o.display = "Hello, World!"
=> "Hello, World!"
irb(main):003:0> o.display
#<OpenStruct display="Hello, World!">=> nil
This new OpenStruct class allows almost any member name to be used. The only exceptions are methods starting with double underscores, such as ‘__id__` and `__send__`, and a few neccessary public methods: `clone`, `dup`, `freeze`, `hash`, `to_enum`, `to_h`, `to_s` and `inspect`, as well as `instance_eval` and `instance_exec`.
Also note that ‘empty`, `eql`, `equal`, `frozen` and `key` can be used as members but the key-check shorthand of using `?`-methods cannot be used since these have special definitions.
To offset the loss of most methods, OpenStruct provides numerous bang-methods which can be used to manipulate the data, e.g. ‘#each!`. Currently most bang-methods route directly to the underlying hash table, so developers should keep that in mind when using this feature. A future version may add an intermediate interface to always ensure proper “CRUD”, functonality but in the vast majority of cases it will make no difference, so it is left for later consideration.
This improved version of OpenStruct also has no issues with being cloned since it does not depend on singleton methods to work. But singleton methods are used to help boost performance. But instead of always creating singleton methods, it only creates them on the first attempt to use them.
Direct Known Subclasses
Class Method Summary collapse
-
.auto(data = nil) ⇒ Object
(also: renew)
Create autovivified OpenStruct.
-
.cascade(data = nil) ⇒ Object
Constructor that is both autovivified and nested.
-
.nested(data = nil) ⇒ Object
(also: nest)
Create a nested OpenStruct, such that all sub-hashes added to the table also become OpenStruct objects.
Instance Method Summary collapse
-
#==(other) ⇒ Object
Two OpenStructs are equal if they are the same class and their underlying tables are equal.
-
#[](key) ⇒ Object
Alias for ‘#read!`.
-
#[]=(key, value) ⇒ Object
Alias for ‘#store!`.
-
#__class__ ⇒ Object
Because there is no means of getting the class via a BasicObject instance, we define such a method manually.
-
#delete!(key) ⇒ Object
The CRUD method for destroy.
-
#delete_field(key) ⇒ Object
deprecated
Deprecated.
Use ‘#delete!` method instead.
-
#dup ⇒ OpenStruct
(also: #clone)
Duplicate OpenStruct object.
-
#each! ⇒ Object
CRUDified each.
-
#empty? ⇒ Boolean
Is the OpenStruct void of entries?.
-
#eql?(other) ⇒ Boolean
Two OpenStructs are equal if they are the same class and their underlying tables are equal.
-
#fetch!(key) ⇒ Object
Like #read but will raise a KeyError if key is not found.
-
#freeze ⇒ Object
Freeze OpenStruct instance.
-
#frozen? ⇒ Boolean
Is the OpenStruct instance frozen?.
-
#hash ⇒ Object
Hash number.
-
#initialize(data = nil, &block) ⇒ OpenStruct2
constructor
Initialize new instance of OpenStruct.
-
#initialize_copy(original) ⇒ Object
Duplicate underlying table when OpenStruct is duplicated or cloned.
-
#inspect ⇒ String
(also: #to_s)
Inspect OpenStruct object.
-
#key!(key) ⇒ Object
If key is not present raise a KeyError.
-
#key?(key) ⇒ Boolean
Also a CRUD method like #read!, but for checking for the existence of a key.
-
#keys! ⇒ Object
CRUD method for listing all keys.
- #map!(&block) ⇒ Object
-
#merge!(other) ⇒ OpenStruct
Merge this OpenStruct with another OpenStruct or Hash object returning a new OpenStruct instance.
-
#method_missing(sym, *args, &blk) ⇒ Object
Dispatch unrecognized member calls.
-
#nested!(boolean = nil) ⇒ Object
Get/set nested flag.
-
#read!(key) ⇒ Object
The CRUD method for read.
-
#store!(key, value) ⇒ Object
The CRUD method for create and update.
-
#to_enum(methname = :each!) ⇒ Enumerator
Create an enumerator based on ‘#each!`.
-
#to_h ⇒ Hash
Get a duplicate of the underlying table.
-
#update!(other) ⇒ self
CRUDified update method.
Constructor Details
#initialize(data = nil, &block) ⇒ OpenStruct2
Initialize new instance of OpenStruct.
95 96 97 98 |
# File 'lib/ostruct2.rb', line 95 def initialize(data=nil, &block) @table = ::Hash.new(&block) update!(data || {}) end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(sym, *args, &blk) ⇒ Object
Dispatch unrecognized member calls.
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/ostruct2.rb', line 121 def method_missing(sym, *args, &blk) str = sym.to_s type = str[-1,1] name = str.chomp('=').chomp('!').chomp('?') case type when '!' # TODO: Probably should have an indirect interface to ensure proper # functonality in all cases. @table.public_send(name, *args, &blk) when '=' new_ostruct_member(name) store!(name, args.first) when '?' new_ostruct_member(name) key?(name) else new_ostruct_member(name) read!(name) end end |
Class Method Details
.auto(data = nil) ⇒ Object Also known as: renew
Create autovivified OpenStruct.
45 46 47 48 |
# File 'lib/ostruct2.rb', line 45 def auto(data=nil) leet = lambda{ |h,k| new(&leet) } new(&leet) end |
.cascade(data = nil) ⇒ Object
Constructor that is both autovivified and nested.
76 77 78 79 80 81 |
# File 'lib/ostruct2.rb', line 76 def cascade(data=nil) o = renew o.nested!(true) o.update!(data) if data o end |
.nested(data = nil) ⇒ Object Also known as: nest
Create a nested OpenStruct, such that all sub-hashes added to the table also become OpenStruct objects.
61 62 63 64 65 66 |
# File 'lib/ostruct2.rb', line 61 def nested(data=nil) o = new o.nested!(true) o.update!(data) if data o end |
Instance Method Details
#==(other) ⇒ Object
Two OpenStructs are equal if they are the same class and their underlying tables are equal.
TODO: Why not equal for other hash types, e.g. via #to_h?
390 391 392 393 |
# File 'lib/ostruct2.rb', line 390 def ==(other) return false unless(other.kind_of?(__class__)) return @table == other.table #to_h end |
#[](key) ⇒ Object
Alias for ‘#read!`.
234 235 236 |
# File 'lib/ostruct2.rb', line 234 def [](key) read!(key) end |
#[]=(key, value) ⇒ Object
Alias for ‘#store!`.
247 248 249 |
# File 'lib/ostruct2.rb', line 247 def []=(key, value) store!(key, value) end |
#__class__ ⇒ Object
Because there is no means of getting the class via a BasicObject instance, we define such a method manually.
104 105 106 |
# File 'lib/ostruct2.rb', line 104 def __class__ OpenStruct2 end |
#delete!(key) ⇒ Object
The CRUD method for destroy.
191 192 193 |
# File 'lib/ostruct2.rb', line 191 def delete!(key) @table.delete(key.to_sym) end |
#delete_field(key) ⇒ Object
Use ‘#delete!` method instead.
Same as ‘#delete!`. This method provides compatibility with the original OpenStruct class.
201 202 203 |
# File 'lib/ostruct2.rb', line 201 def delete_field(key) @table.delete(key.to_sym) end |
#dup ⇒ OpenStruct Also known as: clone
Duplicate OpenStruct object.
339 340 341 |
# File 'lib/ostruct2.rb', line 339 def dup __class__.new(@table, &@table.default_proc) end |
#each! ⇒ Object
CRUDified each.
256 257 258 259 260 |
# File 'lib/ostruct2.rb', line 256 def each! @table.each_key do |key| yield(key, read!(key)) end end |
#empty? ⇒ Boolean
Is the OpenStruct void of entries?
371 372 373 |
# File 'lib/ostruct2.rb', line 371 def empty? @table.empty? end |
#eql?(other) ⇒ Boolean
Two OpenStructs are equal if they are the same class and their underlying tables are equal.
379 380 381 382 |
# File 'lib/ostruct2.rb', line 379 def eql?(other) return false unless(other.kind_of?(__class__)) return @table == other.table #to_h end |
#fetch!(key) ⇒ Object
Like #read but will raise a KeyError if key is not found.
208 209 210 211 |
# File 'lib/ostruct2.rb', line 208 def fetch!(key) key!(key) read!(key) end |
#freeze ⇒ Object
Freeze OpenStruct instance.
355 356 357 |
# File 'lib/ostruct2.rb', line 355 def freeze @table.freeze end |
#frozen? ⇒ Boolean
Is the OpenStruct instance frozen?
364 365 366 |
# File 'lib/ostruct2.rb', line 364 def frozen? @table.frozen? end |
#hash ⇒ Object
Hash number.
348 349 350 |
# File 'lib/ostruct2.rb', line 348 def hash @table.hash end |
#initialize_copy(original) ⇒ Object
Duplicate underlying table when OpenStruct is duplicated or cloned.
113 114 115 116 |
# File 'lib/ostruct2.rb', line 113 def initialize_copy(original) super @table = @table.dup end |
#inspect ⇒ String Also known as: to_s
Inspect OpenStruct object.
301 302 303 |
# File 'lib/ostruct2.rb', line 301 def inspect "#<#{__class__}: #{@table.inspect}>" end |
#key!(key) ⇒ Object
If key is not present raise a KeyError.
222 223 224 225 |
# File 'lib/ostruct2.rb', line 222 def key!(key) return key if key?(key) ::Kernel.raise ::KeyError, ("key not found: %s" % [key.inspect]) end |
#key?(key) ⇒ Boolean
Also a CRUD method like #read!, but for checking for the existence of a key.
164 165 166 |
# File 'lib/ostruct2.rb', line 164 def key?(key) @table.key?(key.to_sym) end |
#keys! ⇒ Object
CRUD method for listing all keys.
157 158 159 |
# File 'lib/ostruct2.rb', line 157 def keys! @table.keys end |
#map!(&block) ⇒ Object
263 264 265 |
# File 'lib/ostruct2.rb', line 263 def map!(&block) to_enum.map(&block) end |
#merge!(other) ⇒ OpenStruct
Merge this OpenStruct with another OpenStruct or Hash object returning a new OpenStruct instance.
IMPORTANT! This method does not act in-place like ‘Hash#merge!`, rather it works like `Hash#merge`.
288 289 290 291 292 293 294 |
# File 'lib/ostruct2.rb', line 288 def merge!(other) o = dup other.each do |k,v| o.store!(k,v) end o end |
#nested!(boolean = nil) ⇒ Object
Get/set nested flag.
146 147 148 149 150 151 152 |
# File 'lib/ostruct2.rb', line 146 def nested!(boolean=nil) if boolean.nil? @nested else @nested = !!boolean end end |
#read!(key) ⇒ Object
The CRUD method for read.
171 172 173 |
# File 'lib/ostruct2.rb', line 171 def read!(key) @table[key.to_sym] end |
#store!(key, value) ⇒ Object
The CRUD method for create and update.
178 179 180 181 182 183 184 185 186 |
# File 'lib/ostruct2.rb', line 178 def store!(key, value) if @nested && Hash === value # value.respond_to?(:to_hash) value = OpenStruct2.new(value) end #new_ostruct_member(key) # this is here only for speed bump @table[key.to_sym] = value end |
#to_enum(methname = :each!) ⇒ Enumerator
Create an enumerator based on ‘#each!`.
324 325 326 327 328 329 330 331 332 |
# File 'lib/ostruct2.rb', line 324 def to_enum(methname=:each!) # Why has Ruby 2 deprecated this form? #::Enumerator.new(self, methname) ::Enumerator.new do |y| __send__(methname) do |*a| y.yield *a end end end |
#to_h ⇒ Hash
Get a duplicate of the underlying table.
312 313 314 |
# File 'lib/ostruct2.rb', line 312 def to_h @table.dup end |
#update!(other) ⇒ self
CRUDified update method.
272 273 274 275 276 277 |
# File 'lib/ostruct2.rb', line 272 def update!(other) other.each do |k,v| store!(k,v) end self end |