33
44require 'recursive_open_struct/debug_inspect'
55require 'recursive_open_struct/deep_dup'
6- require 'recursive_open_struct/ruby_19_backport'
76require 'recursive_open_struct/dig'
87
98# TODO: When we care less about Rubies before 2.4.0, match OpenStruct's method
1413# `#to_h`.
1514
1615class RecursiveOpenStruct < OpenStruct
17- include Ruby19Backport if RUBY_VERSION =~ /\A 1.9/
1816 include Dig if OpenStruct . public_instance_methods . include? :dig
1917
2018 # TODO: deprecated, possibly remove or make optional an runtime so that it
2119 # doesn't normally pollute the public method namespace
2220 include DebugInspect
2321
24- def initialize ( hash = nil , args = { } )
22+ def self . default_options
23+ {
24+ mutate_input_hash : false ,
25+ recurse_over_arrays : false ,
26+ preserve_original_keys : false
27+ }
28+ end
29+
30+ def initialize ( hash = nil , passed_options = { } )
2531 hash ||= { }
26- @recurse_over_arrays = args . fetch ( :recurse_over_arrays , false )
27- @preserve_original_keys = args . fetch ( :preserve_original_keys , false )
28- @deep_dup = DeepDup . new (
29- recurse_over_arrays : @recurse_over_arrays ,
30- preserve_original_keys : @preserve_original_keys
31- )
3232
33- @table = args . fetch ( :mutate_input_hash , false ) ? hash : @deep_dup . call ( hash )
33+ @options = self . class . default_options . merge! ( passed_options ) . freeze
34+
35+ @deep_dup = DeepDup . new ( @options )
36+
37+ @table = @options [ :mutate_input_hash ] ? hash : @deep_dup . call ( hash )
3438
3539 @sub_elements = { }
3640 end
@@ -56,20 +60,22 @@ def [](name)
5660 key_name = _get_key_from_table_ ( name )
5761 v = @table [ key_name ]
5862 if v . is_a? ( Hash )
59- @sub_elements [ key_name ] ||= self . class . new (
60- v ,
61- recurse_over_arrays : @recurse_over_arrays ,
62- preserve_original_keys : @preserve_original_keys ,
63- mutate_input_hash : true
64- )
65- elsif v . is_a? ( Array ) and @recurse_over_arrays
63+ @sub_elements [ key_name ] ||= _create_sub_element_ ( v , mutate_input_hash : true )
64+ elsif v . is_a? ( Array ) and @options [ :recurse_over_arrays ]
6665 @sub_elements [ key_name ] ||= recurse_over_array ( v )
6766 @sub_elements [ key_name ] = recurse_over_array ( @sub_elements [ key_name ] )
6867 else
6968 v
7069 end
7170 end
7271
72+ def []=( name , value )
73+ key_name = _get_key_from_table_ ( name )
74+ tbl = modifiable? # Ensure we are modifiable
75+ @sub_elements . delete ( key_name )
76+ tbl [ key_name ] = value
77+ end
78+
7379 # Makes sure ROS responds as expected on #respond_to? and #method requests
7480 def respond_to_missing? ( mid , include_private = false )
7581 mname = _get_key_from_table_ ( mid . to_s . chomp ( '=' ) . chomp ( '_as_a_hash' ) )
@@ -95,13 +101,16 @@ def method_missing(mid, *args)
95101 if len != 1
96102 raise ArgumentError , "wrong number of arguments (#{ len } for 1)" , caller ( 1 )
97103 end
98- modifiable? [ new_ostruct_member! ( $1. to_sym ) ] = args [ 0 ]
104+ # self[$1.to_sym] = args[0]
105+ # modifiable?[new_ostruct_member!($1.to_sym)] = args[0]
106+ new_ostruct_member! ( $1. to_sym )
107+ public_send ( mid , args [ 0 ] )
99108 elsif len == 0
100109 key = mid
101110 key = $1 if key =~ /^(.*)_as_a_hash$/
102111 if @table . key? ( _get_key_from_table_ ( key ) )
103112 new_ostruct_member! ( key )
104- send ( mid )
113+ public_send ( mid )
105114 end
106115 else
107116 err = NoMethodError . new "undefined method `#{ mid } ' for #{ self } " , mid , args
@@ -120,8 +129,7 @@ class << self; self; end.class_eval do
120129 self [ key_name ]
121130 end
122131 define_method ( "#{ name } =" ) do |x |
123- @sub_elements . delete ( key_name )
124- modifiable? [ key_name ] = x
132+ self [ key_name ] = x
125133 end
126134 define_method ( "#{ name } _as_a_hash" ) { @table [ key_name ] }
127135 end
@@ -141,8 +149,8 @@ class << self; self; end.class_eval do
141149 def delete_field ( name )
142150 sym = _get_key_from_table_ ( name )
143151 singleton_class . __send__ ( :remove_method , sym , "#{ sym } =" ) rescue NoMethodError # ignore if methods not yet generated.
144- @sub_elements . delete sym
145- @table . delete sym
152+ @sub_elements . delete ( sym )
153+ @table . delete ( sym )
146154 end
147155
148156 private
@@ -153,11 +161,14 @@ def _get_key_from_table_(name)
153161 name
154162 end
155163
164+ def _create_sub_element_ ( hash , **overrides )
165+ self . class . new ( hash , @options . merge ( overrides ) )
166+ end
167+
156168 def recurse_over_array ( array )
157169 array . each_with_index do |a , i |
158170 if a . is_a? Hash
159- array [ i ] = self . class . new ( a , :recurse_over_arrays => true ,
160- :mutate_input_hash => true , :preserve_original_keys => @preserve_original_keys )
171+ array [ i ] = _create_sub_element_ ( a , mutate_input_hash : true , recurse_over_arrays : true )
161172 elsif a . is_a? Array
162173 array [ i ] = recurse_over_array a
163174 end
0 commit comments