recursive-open-struct 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aef116134efc7102292ad51046c306be7e93621f688e02255996ca218ed578b1
4
- data.tar.gz: 722c1e817dbdd25d4a48deb5923ce6f83de55211dd86c00a5c292f91e86bc74b
3
+ metadata.gz: f4474b5a862c6ea4da4f2c930664880ca70a01f75c077a2e9952fd80ed2d26da
4
+ data.tar.gz: dcea4c80ebe98a5f91f9ea40b2b7e82cc219b259eae83b62a00329f8075cc85a
5
5
  SHA512:
6
- metadata.gz: fde1398adbd2795f9f108859d1d6c963a89048275be0fbf5e5cb1f92abb404ed8a55d4e97a8e2ba0d6367d3a255ecfdfe3dcfacc74d1b9bdd0d0c4e035acbe09
7
- data.tar.gz: 82d16703403627ea358097b26d47334a984107c68a9c5360ac617169c4b9602292a7d7df77a96971ac3369fcca11256457518a61ce9895b7fcf9b58ab924c50b
6
+ metadata.gz: e8698402e67013a209a3434235e26201550c2249db8ae82480ec2208bdd0c83fb6524be046ecb4af3ed8cfe09adebd513a383d56b8b40d91f2c973fcd1f18196
7
+ data.tar.gz: a15827f31947ca85607086b9cda61ab18b4421bb310dd3034ca4dd39ef43ecf5215d599103cb897152924d6faa1ae94e0bc800cf197c5e96ab50fc794c547a2c
@@ -3,6 +3,7 @@ Recursive-open-struct was written by these fine people:
3
3
  * Ben Langfeld <ben@langfeld.me>
4
4
  * Beni Cherniavsky-Paskin <cben@redhat.com>
5
5
  * Cédric Felizard <cedric@felizard.fr>
6
+ * David Feldman <dbfeldman@gmail.com>
6
7
  * Edward Betts <edward@4angle.com>
7
8
  * Ewoud Kohl van Wijngaarden <ewoud@kohlvanwijngaarden.nl>
8
9
  * Federico Aloi <federico.aloi@gmail.com>
@@ -1,3 +1,12 @@
1
+ 1.1.2 / 2020/06/20
2
+ ==================
3
+
4
+ * FIX [#58](https://github.com/aetherknight/recursive-open-struct/pull/58):
5
+ David Feldman: Fix `[]=` so that it properly updates sub-elements
6
+ * [#58](https://github.com/aetherknight/recursive-open-struct/pull/58):
7
+ David Feldman: Make the default options configurable at the class level to
8
+ simplify adding additional options in subclasses
9
+
1
10
  1.1.1 / 2020/03/10
2
11
  ==================
3
12
 
@@ -3,7 +3,6 @@ require 'recursive_open_struct/version'
3
3
 
4
4
  require 'recursive_open_struct/debug_inspect'
5
5
  require 'recursive_open_struct/deep_dup'
6
- require 'recursive_open_struct/ruby_19_backport'
7
6
  require 'recursive_open_struct/dig'
8
7
 
9
8
  # TODO: When we care less about Rubies before 2.4.0, match OpenStruct's method
@@ -14,23 +13,28 @@ require 'recursive_open_struct/dig'
14
13
  # `#to_h`.
15
14
 
16
15
  class RecursiveOpenStruct < OpenStruct
17
- include Ruby19Backport if RUBY_VERSION =~ /\A1.9/
18
16
  include Dig if OpenStruct.public_instance_methods.include? :dig
19
17
 
20
18
  # TODO: deprecated, possibly remove or make optional an runtime so that it
21
19
  # doesn't normally pollute the public method namespace
22
20
  include DebugInspect
23
21
 
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={})
25
31
  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
- )
32
32
 
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)
34
38
 
35
39
  @sub_elements = {}
36
40
  end
@@ -56,13 +60,8 @@ class RecursiveOpenStruct < OpenStruct
56
60
  key_name = _get_key_from_table_(name)
57
61
  v = @table[key_name]
58
62
  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]
66
65
  @sub_elements[key_name] ||= recurse_over_array(v)
67
66
  @sub_elements[key_name] = recurse_over_array(@sub_elements[key_name])
68
67
  else
@@ -70,6 +69,13 @@ class RecursiveOpenStruct < OpenStruct
70
69
  end
71
70
  end
72
71
 
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
+
73
79
  # Makes sure ROS responds as expected on #respond_to? and #method requests
74
80
  def respond_to_missing?(mid, include_private = false)
75
81
  mname = _get_key_from_table_(mid.to_s.chomp('=').chomp('_as_a_hash'))
@@ -95,13 +101,16 @@ class RecursiveOpenStruct < OpenStruct
95
101
  if len != 1
96
102
  raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
97
103
  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])
99
108
  elsif len == 0
100
109
  key = mid
101
110
  key = $1 if key =~ /^(.*)_as_a_hash$/
102
111
  if @table.key?(_get_key_from_table_(key))
103
112
  new_ostruct_member!(key)
104
- send(mid)
113
+ public_send(mid)
105
114
  end
106
115
  else
107
116
  err = NoMethodError.new "undefined method `#{mid}' for #{self}", mid, args
@@ -120,8 +129,7 @@ class RecursiveOpenStruct < OpenStruct
120
129
  self[key_name]
121
130
  end
122
131
  define_method("#{name}=") do |x|
123
- @sub_elements.delete(key_name)
124
- modifiable?[key_name] = x
132
+ self[key_name] = x
125
133
  end
126
134
  define_method("#{name}_as_a_hash") { @table[key_name] }
127
135
  end
@@ -141,8 +149,8 @@ class RecursiveOpenStruct < OpenStruct
141
149
  def delete_field(name)
142
150
  sym = _get_key_from_table_(name)
143
151
  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)
146
154
  end
147
155
 
148
156
  private
@@ -153,11 +161,14 @@ class RecursiveOpenStruct < OpenStruct
153
161
  name
154
162
  end
155
163
 
164
+ def _create_sub_element_(hash, **overrides)
165
+ self.class.new(hash, @options.merge(overrides))
166
+ end
167
+
156
168
  def recurse_over_array(array)
157
169
  array.each_with_index do |a, i|
158
170
  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)
161
172
  elsif a.is_a? Array
162
173
  array[i] = recurse_over_array a
163
174
  end
@@ -3,5 +3,5 @@
3
3
  require 'ostruct'
4
4
 
5
5
  class RecursiveOpenStruct < OpenStruct
6
- VERSION = "1.1.1"
6
+ VERSION = "1.1.2"
7
7
  end
@@ -28,6 +28,15 @@ describe RecursiveOpenStruct do
28
28
  expect(subject.blah_as_a_hash).to eq({ :another => 'value' })
29
29
  end
30
30
 
31
+ it "handles sub-element replacement with dotted notation before member setup" do
32
+ expect(ros[:blah][:another]).to eql 'value'
33
+ expect(ros.methods).not_to include(:blah)
34
+
35
+ ros.blah = { changed: 'backing' }
36
+
37
+ expect(ros.blah.changed).to eql 'backing'
38
+ end
39
+
31
40
  describe "handling loops in the original Hashes" do
32
41
  let(:h1) { { :a => 'a'} }
33
42
  let(:h2) { { :a => 'b', :h1 => h1 } }
@@ -55,6 +64,34 @@ describe RecursiveOpenStruct do
55
64
  expect(ros.blah.blargh).to eq "Janet"
56
65
  end
57
66
 
67
+ describe 'subscript mutation notation' do
68
+ it 'handles the basic case' do
69
+ subject[:blah] = 12345
70
+ expect(subject.blah).to eql 12345
71
+ end
72
+
73
+ it 'recurses properly' do
74
+ subject[:blah][:another] = 'abc'
75
+ expect(subject.blah.another).to eql 'abc'
76
+ expect(subject.blah_as_a_hash).to eql({ :another => 'abc' })
77
+ end
78
+
79
+ let(:diff){ { :different => 'thing' } }
80
+
81
+ it 'can replace the entire hash' do
82
+ expect(subject.to_h).to eql(h)
83
+ subject[:blah] = diff
84
+ expect(subject.to_h).to eql({ :blah => diff })
85
+ end
86
+
87
+ it 'updates sub-element cache' do
88
+ expect(subject.blah.different).to be_nil
89
+ subject[:blah] = diff
90
+ expect(subject.blah.different).to eql 'thing'
91
+ expect(subject.blah_as_a_hash).to eql(diff)
92
+ end
93
+ end
94
+
58
95
  context "after a sub-element has been modified" do
59
96
  let(:hash) do
60
97
  { :blah => { :blargh => "Brad" }, :some_array => [ 1, 2, 3] }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recursive-open-struct
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - William (B.J.) Snow Orvis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-11 00:00:00.000000000 Z
11
+ date: 2020-06-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -129,7 +129,6 @@ files:
129
129
  - lib/recursive_open_struct/debug_inspect.rb
130
130
  - lib/recursive_open_struct/deep_dup.rb
131
131
  - lib/recursive_open_struct/dig.rb
132
- - lib/recursive_open_struct/ruby_19_backport.rb
133
132
  - lib/recursive_open_struct/version.rb
134
133
  - recursive-open-struct.gemspec
135
134
  - spec/recursive_open_struct/debug_inspect_spec.rb
@@ -1,27 +0,0 @@
1
- module RecursiveOpenStruct::Ruby19Backport
2
- # Apply fix if necessary:
3
- # https://github.com/ruby/ruby/commit/2d952c6d16ffe06a28bb1007e2cd1410c3db2d58
4
- def initialize_copy(orig)
5
- super
6
- @table.each_key{|key| new_ostruct_member(key)}
7
- end
8
-
9
- def []=(name, value)
10
- modifiable[new_ostruct_member(name)] = value
11
- end
12
-
13
- def eql?(other)
14
- return false unless other.kind_of?(OpenStruct)
15
- @table.eql?(other.table)
16
- end
17
-
18
- def hash
19
- @table.hash
20
- end
21
-
22
- def each_pair
23
- return to_enum(:each_pair) { @table.size } unless block_given?
24
- @table.each_pair{|p| yield p}
25
- end
26
- end
27
-