Class: Faster::OpenStruct
- Inherits:
-
Object
- Object
- Faster::OpenStruct
- Defined in:
- lib/reflexive/faster_open_struct.rb
Overview
## Faster::OpenStruct
Up to 40 (!) times more memory efficient version of OpenStruct
Differences from Ruby MRI OpenStruct:
-
Doesn’t ‘dup` passed initialization hash (NOTE: only reference to hash is stored)
-
Doesn’t convert hash keys to symbols (by default string keys are used, with fallback to symbol keys)
-
Creates methods on the fly on ‘OpenStruct` class, instead of singleton class. Uses `module_eval` with string to avoid holding scope references for every method.
-
Refactored, crud clean, spec covered :)
Constant Summary collapse
- InspectKey =
:nodoc:
:__inspect_key__
Instance Method Summary collapse
-
#==(other) ⇒ Object
Compare this object and
other
for equality. - #__new_ostruct_member__(method_name_sym) ⇒ Object
- #empty? ⇒ Boolean
-
#initialize(hash = nil) ⇒ OpenStruct
constructor
A new instance of OpenStruct.
- #inspect_with_reentrant_guard(default = "...") ⇒ Object (also: #inspect)
- #method_missing(method_name_sym, *args) ⇒ Object
Constructor Details
#initialize(hash = nil) ⇒ OpenStruct
Returns a new instance of OpenStruct.
23 24 25 26 |
# File 'lib/reflexive/faster_open_struct.rb', line 23 def initialize(hash = nil) @hash = hash || {} @initialized_empty = hash == nil end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name_sym, *args) ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/reflexive/faster_open_struct.rb', line 28 def method_missing(method_name_sym, *args) if method_name_sym.to_s[-1] == ?= if args.size != 1 raise ArgumentError, "wrong number of arguments (#{args.size} for 1)", caller(1) end if self.frozen? raise TypeError, "can't modify frozen #{self.class}", caller(1) end __new_ostruct_member__(method_name_sym.to_s.chomp("=")) send(method_name_sym, args[0]) elsif args.size == 0 __new_ostruct_member__(method_name_sym) send(method_name_sym) else raise NoMethodError, "undefined method `#{method_name_sym}' for #{self}", caller(1) end end |
Instance Method Details
#==(other) ⇒ Object
Compare this object and other
for equality.
78 79 80 81 |
# File 'lib/reflexive/faster_open_struct.rb', line 78 def ==(other) return false unless other.is_a?(self.class) @hash == other.instance_variable_get(:@hash) end |
#__new_ostruct_member__(method_name_sym) ⇒ Object
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/reflexive/faster_open_struct.rb', line 48 def __new_ostruct_member__(method_name_sym) self.class.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1 def #{ method_name_sym } @hash.fetch("#{ method_name_sym }", @hash[:#{ method_name_sym }]) # read by default from string key, then try symbol # if string key doesn't exist end END_EVAL unless method_name_sym.to_s[-1] == ?? # can't define writer for predicate method self.class.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1 def #{ method_name_sym }=(val) if @hash.key?("#{ method_name_sym }") || @initialized_empty # write by default to string key (when it is present # in initialization hash or initialization hash # wasn't provided) @hash["#{ method_name_sym }"] = val # if it doesn't exist - write to symbol key else @hash[:#{ method_name_sym }] = val end end END_EVAL end end |
#empty? ⇒ Boolean
71 72 73 |
# File 'lib/reflexive/faster_open_struct.rb', line 71 def empty? @hash.empty? end |
#inspect_with_reentrant_guard(default = "...") ⇒ Object Also known as: inspect
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/reflexive/faster_open_struct.rb', line 94 def inspect_with_reentrant_guard(default = "...") Thread.current[InspectKey] ||= [] if Thread.current[InspectKey].include?(self) return default # reenter detected end Thread.current[InspectKey] << self begin inspect_without_reentrant_guard ensure Thread.current[InspectKey].pop end end |