recursive-open-struct 1.1.1 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/AUTHORS.txt +1 -0
- data/CHANGELOG.md +9 -0
- data/lib/recursive_open_struct.rb +36 -25
- data/lib/recursive_open_struct/version.rb +1 -1
- data/spec/recursive_open_struct/recursion_spec.rb +37 -0
- metadata +2 -3
- data/lib/recursive_open_struct/ruby_19_backport.rb +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4474b5a862c6ea4da4f2c930664880ca70a01f75c077a2e9952fd80ed2d26da
|
4
|
+
data.tar.gz: dcea4c80ebe98a5f91f9ea40b2b7e82cc219b259eae83b62a00329f8075cc85a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8698402e67013a209a3434235e26201550c2249db8ae82480ec2208bdd0c83fb6524be046ecb4af3ed8cfe09adebd513a383d56b8b40d91f2c973fcd1f18196
|
7
|
+
data.tar.gz: a15827f31947ca85607086b9cda61ab18b4421bb310dd3034ca4dd39ef43ecf5215d599103cb897152924d6faa1ae94e0bc800cf197c5e96ab50fc794c547a2c
|
data/AUTHORS.txt
CHANGED
@@ -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>
|
data/CHANGELOG.md
CHANGED
@@ -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
|
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
|
-
@
|
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] ||=
|
60
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
145
|
-
@table.delete
|
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] =
|
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
|
@@ -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.
|
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-
|
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
|
-
|