Class: RecursiveOpenStruct
- Inherits:
-
OpenStruct
- Object
- OpenStruct
- RecursiveOpenStruct
- Includes:
- DebugInspect, Dig
- Defined in:
- lib/recursive_open_struct.rb,
lib/recursive_open_struct/dig.rb,
lib/recursive_open_struct/version.rb,
lib/recursive_open_struct/deep_dup.rb,
lib/recursive_open_struct/debug_inspect.rb
Overview
rubocop:disable Metrics/AbcSize rubocop:disable Metrics/CyclomaticComplexity rubocop:disable Metrics/PerceivedComplexity rubocop:disable Style/StringConcatenation
Defined Under Namespace
Modules: DebugInspect, Dig Classes: DeepDup
Constant Summary collapse
- VERSION =
'2.1.1'
Class Method Summary collapse
Instance Method Summary collapse
- #[](name) ⇒ Object
- #[]=(name, value) ⇒ Object
- #delete_field(name) ⇒ Object
-
#freeze ⇒ Object
rubocop:enable Metrics/PerceivedComplexity rubocop:enable Metrics/AbcSize.
-
#initialize(hash = nil, passed_options = {}) ⇒ RecursiveOpenStruct
constructor
rubocop:disable Lint/MissingSuper Intentionally doesn’t call super and initializes @table itself.
- #initialize_copy(orig) ⇒ Object
- #marshal_dump ⇒ Object
-
#marshal_load(attributes) ⇒ Object
rubocop:enable Lint/MissingSuper.
-
#method_missing(mid, *args) ⇒ Object
Adapted implementation of method_missing to accommodate the differences between ROS and OS.
-
#new_ostruct_member(name) ⇒ Object
(also: #new_ostruct_member!)
TODO: Rename to new_ostruct_member! once we care less about Rubies before 2.4.0.
-
#respond_to_missing?(mid, include_private = false) ⇒ Boolean
Makes sure ROS responds as expected on #respond_to? and #method requests.
- #to_h ⇒ Object (also: #to_hash)
Methods included from DebugInspect
#debug_inspect, #display_recursive_open_struct
Methods included from Dig
Constructor Details
#initialize(hash = nil, passed_options = {}) ⇒ RecursiveOpenStruct
rubocop:disable Lint/MissingSuper Intentionally doesn’t call super and initializes @table itself.
36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/recursive_open_struct.rb', line 36 def initialize(hash = nil, = {}) hash = hash.to_h if [hash.is_a?(RecursiveOpenStruct), hash.is_a?(OpenStruct)].any? hash ||= {} @options = self.class..merge!().freeze @deep_dup = DeepDup.new(@options) @table = @options[:mutate_input_hash] ? hash : @deep_dup.call(hash) @sub_elements = {} end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(mid, *args) ⇒ Object
Adapted implementation of method_missing to accommodate the differences between ROS and OS. rubocop:disable Metrics/AbcSize rubocop:disable Metrics/PerceivedComplexity
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/recursive_open_struct.rb', line 131 def method_missing(mid, *args) len = args.length if mid =~ /^(.*)=$/ raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1) if len != 1 # self[$1.to_sym] = args[0] # modifiable?[new_ostruct_member!($1.to_sym)] = args[0] new_ostruct_member!(::Regexp.last_match(1).to_sym) public_send(mid, args[0]) elsif len.zero? key = mid key = ::Regexp.last_match(1) if key =~ /^(.*)_as_a_hash$/ if @table.key?(_get_key_from_table_(key)) new_ostruct_member!(key) public_send(mid) elsif @options[:raise_on_missing] err = NoMethodError.new "undefined method `#{mid}' for #{self}", mid, args err.set_backtrace caller(1) raise err end else err = NoMethodError.new "undefined method `#{mid}' for #{self}", mid, args err.set_backtrace caller(1) raise err end end |
Class Method Details
.default_options ⇒ Object
25 26 27 28 29 30 31 32 |
# File 'lib/recursive_open_struct.rb', line 25 def self. { mutate_input_hash: false, recurse_over_arrays: false, preserve_original_keys: false, raise_on_missing: false } end |
Instance Method Details
#[](name) ⇒ Object
93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/recursive_open_struct.rb', line 93 def [](name) key_name = _get_key_from_table_(name) v = @table[key_name] if v.is_a?(Hash) @sub_elements[key_name] ||= _create_sub_element_(v, mutate_input_hash: true) elsif v.is_a?(Array) && @options[:recurse_over_arrays] @sub_elements[key_name] ||= recurse_over_array(v) @sub_elements[key_name] = recurse_over_array(@sub_elements[key_name]) else v end end |
#[]=(name, value) ⇒ Object
107 108 109 110 111 112 |
# File 'lib/recursive_open_struct.rb', line 107 def []=(name, value) key_name = _get_key_from_table_(name) tbl = modifiable? # Ensure we are modifiable @sub_elements.delete(key_name) tbl[key_name] = value end |
#delete_field(name) ⇒ Object
195 196 197 198 199 200 201 202 203 204 205 |
# File 'lib/recursive_open_struct.rb', line 195 def delete_field(name) sym = _get_key_from_table_(name) begin singleton_class.__send__(:remove_method, sym, "#{sym}=") rescue StandardError # ignore if methods not yet generated. NoMethodError end @sub_elements.delete(sym) @table.delete(sym) end |
#freeze ⇒ Object
rubocop:enable Metrics/PerceivedComplexity rubocop:enable Metrics/AbcSize
160 161 162 163 164 165 166 |
# File 'lib/recursive_open_struct.rb', line 160 def freeze @table.each_key do |key| new_ostruct_member!(key) end super end |
#initialize_copy(orig) ⇒ Object
62 63 64 65 66 67 68 69 |
# File 'lib/recursive_open_struct.rb', line 62 def initialize_copy(orig) super # deep copy the table to separate the two objects @table = @deep_dup.call(@table) # Forget any memoized sub-elements @sub_elements = {} end |
#marshal_dump ⇒ Object
57 58 59 |
# File 'lib/recursive_open_struct.rb', line 57 def marshal_dump [super, @options] end |
#marshal_load(attributes) ⇒ Object
rubocop:enable Lint/MissingSuper
50 51 52 53 54 55 |
# File 'lib/recursive_open_struct.rb', line 50 def marshal_load(attributes) hash, @options = attributes @deep_dup = DeepDup.new(@options) @sub_elements = {} super(hash) end |
#new_ostruct_member(name) ⇒ Object Also known as: new_ostruct_member!
TODO: Rename to new_ostruct_member! once we care less about Rubies before 2.4.0.
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/recursive_open_struct.rb', line 170 def new_ostruct_member(name) key_name = _get_key_from_table_(name) unless singleton_class.method_defined?(name.to_sym) class << self; self; end.class_eval do define_method(name) do self[key_name] end define_method("#{name}=") do |x| self[key_name] = x end define_method("#{name}_as_a_hash") { @table[key_name] } end end key_name end |
#respond_to_missing?(mid, include_private = false) ⇒ Boolean
Makes sure ROS responds as expected on #respond_to? and #method requests
122 123 124 125 |
# File 'lib/recursive_open_struct.rb', line 122 def respond_to_missing?(mid, include_private = false) mname = _get_key_from_table_(mid.to_s.chomp('=').chomp('_as_a_hash')) @table.key?(mname) || super end |
#to_h ⇒ Object Also known as: to_hash
72 73 74 |
# File 'lib/recursive_open_struct.rb', line 72 def to_h @deep_dup.call(@table) end |