recursive-open-struct-sd 1.0.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.
@@ -0,0 +1,105 @@
1
+ require_relative '../spec_helper'
2
+ require 'recursive_open_struct'
3
+
4
+ describe RecursiveOpenStruct do
5
+
6
+ let(:hash) { {:foo => 'foo', 'bar' => :bar} }
7
+ subject(:ros) { RecursiveOpenStruct.new(hash) }
8
+
9
+ describe "OpenStruct 2.0 methods" do
10
+
11
+ context "Hash style setter" do
12
+
13
+ it "method exists" do
14
+ expect(ros.respond_to?('[]=')).to be_truthy
15
+ end
16
+
17
+ it "changes the value" do
18
+ ros[:foo] = :foo
19
+ ros.foo = :foo
20
+ end
21
+
22
+ end
23
+
24
+ context "delete_field" do
25
+
26
+ before(:each) { ros.delete_field :foo }
27
+
28
+ it "removes the value" do
29
+ expect(ros.foo).to be_nil
30
+ expect(ros.to_h).to_not include(:foo)
31
+ end
32
+
33
+ it "removes the getter method" do
34
+ is_expected.to_not respond_to :foo
35
+ end
36
+
37
+ it "removes the setter method" do
38
+ expect(ros.respond_to? 'foo=').to be_falsey
39
+ end
40
+
41
+ it "works with indifferent access" do
42
+ expect(ros.delete_field :bar).to eq :bar
43
+ is_expected.to_not respond_to :bar
44
+ is_expected.to_not respond_to 'bar='
45
+ expect(ros.to_h).to be_empty
46
+ end
47
+
48
+ end
49
+
50
+ context "eql?" do
51
+ subject(:new_ros) { ros.dup }
52
+
53
+ context "with identical ROS" do
54
+ subject { ros }
55
+ it { is_expected.to be_eql ros }
56
+ end
57
+
58
+ context "with similar ROS" do
59
+ subject { RecursiveOpenStruct.new(hash) }
60
+ it { is_expected.to be_eql ros }
61
+ end
62
+
63
+ context "with same Hash" do
64
+ subject { RecursiveOpenStruct.new(hash, recurse_over_arrays: true) }
65
+ it { is_expected.to be_eql ros }
66
+ end
67
+
68
+ context "with duplicated ROS" do
69
+ subject { ros.dup }
70
+
71
+ it "fails on different value" do
72
+ subject.foo = 'bar'
73
+ is_expected.not_to be_eql ros
74
+ end
75
+
76
+ it "fails on missing field" do
77
+ subject.delete_field :bar
78
+ is_expected.not_to be_eql ros
79
+ end
80
+
81
+ it "fails on added field" do
82
+ subject.baz = :baz
83
+ is_expected.not_to be_eql ros
84
+ end
85
+
86
+ end
87
+
88
+ end
89
+
90
+ context "hash" do
91
+ it "calculates table hash" do
92
+ expect(ros.hash).to be ros.instance_variable_get('@table').hash
93
+ end
94
+
95
+ end
96
+
97
+ context "each_pair" do
98
+ it "iterates over hash keys, with keys as symbol" do
99
+ expect(ros.each_pair).to match ({:foo => 'foo', :bar => :bar}.each_pair)
100
+ end
101
+ end
102
+
103
+ end
104
+
105
+ end
@@ -0,0 +1,14 @@
1
+ require_relative '../spec_helper'
2
+ require 'recursive_open_struct'
3
+
4
+ describe RecursiveOpenStruct do
5
+ describe "subclassing RecursiveOpenStruct" do
6
+ let(:subclass) { Class.new(RecursiveOpenStruct) }
7
+
8
+ subject(:rossc) { subclass.new({ :one => [{:two => :three}] }, recurse_over_arrays: true) }
9
+
10
+ specify "nested objects use the subclass of the parent" do
11
+ expect(rossc.one.first.class).to eq subclass
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,299 @@
1
+ require_relative '../spec_helper'
2
+ require 'recursive_open_struct'
3
+
4
+ describe RecursiveOpenStruct do
5
+
6
+ describe "recursive behavior" do
7
+ let(:h) { { :blah => { :another => 'value' } } }
8
+ subject(:ros) { RecursiveOpenStruct.new(h) }
9
+
10
+ it "can convert the entire hash tree back into a hash" do
11
+ blank_obj = Object.new
12
+ h = {:asdf => 'John Smith', :foo => [{:bar => blank_obj}, {:baz => nil}]}
13
+ ros = RecursiveOpenStruct.new(h)
14
+
15
+ expect(ros.to_h).to eq h
16
+ expect(ros.to_hash).to eq h
17
+ end
18
+
19
+ it "returns accessed hashes as RecursiveOpenStructs instead of hashes" do
20
+ expect(subject.blah.another).to eq 'value'
21
+ end
22
+
23
+ it "handles subscript notation the same way as dotted notation" do
24
+ expect(subject.blah.another).to eq subject[:blah].another
25
+ end
26
+
27
+ it "uses #key_as_a_hash to return key as a Hash" do
28
+ expect(subject.blah_as_a_hash).to eq({ :another => 'value' })
29
+ end
30
+
31
+ describe "handling loops in the original Hashes" do
32
+ let(:h1) { { :a => 'a'} }
33
+ let(:h2) { { :a => 'b', :h1 => h1 } }
34
+ before(:each) { h1[:h2] = h2 }
35
+
36
+ subject { RecursiveOpenStruct.new(h2) }
37
+
38
+ it { expect(subject.h1.a).to eq 'a' }
39
+ it { expect(subject.h1.h2.a).to eq 'b' }
40
+ it { expect(subject.h1.h2.h1.a).to eq 'a' }
41
+ it { expect(subject.h1.h2.h1.h2.a).to eq 'b' }
42
+ it { expect(subject.h1).to eq subject.h1.h2.h1 }
43
+ it { expect(subject.h1).to_not eq subject.h1.h2 }
44
+ end # describe handling loops in the origin Hashes
45
+
46
+ it "can modify a key of a sub-element" do
47
+ h = {
48
+ :blah => {
49
+ :blargh => 'Brad'
50
+ }
51
+ }
52
+ ros = RecursiveOpenStruct.new(h)
53
+ ros.blah.blargh = "Janet"
54
+
55
+ expect(ros.blah.blargh).to eq "Janet"
56
+ end
57
+
58
+ context "after a sub-element has been modified" do
59
+ let(:hash) do
60
+ { :blah => { :blargh => "Brad" }, :some_array => [ 1, 2, 3] }
61
+ end
62
+ let(:updated_hash) do
63
+ { :blah => { :blargh => "Janet" }, :some_array => [ 1, 2, 3] }
64
+ end
65
+
66
+ subject { RecursiveOpenStruct.new(hash) }
67
+
68
+ before(:each) { subject.blah.blargh = "Janet" }
69
+
70
+ describe ".to_h" do
71
+ it "returns a hash tree that contains those modifications" do
72
+ expect(subject.to_h).to eq updated_hash
73
+ end
74
+
75
+ specify "modifying the returned hash tree does not modify the ROS" do
76
+ subject.to_h[:blah][:blargh] = "Dr Scott"
77
+
78
+ expect(subject.blah.blargh).to eq "Janet"
79
+ end
80
+ end
81
+
82
+ it "does not mutate the original hash tree passed to the constructor" do
83
+ expect(hash[:blah][:blargh]).to eq 'Brad'
84
+ end
85
+
86
+ it "limits the deep-copy to the initial hash tree" do
87
+ subject.some_array[0] = 4
88
+
89
+ expect(hash[:some_array][0]).to eq 4
90
+ end
91
+
92
+ describe "#dup" do
93
+ let(:duped_subject) { subject.dup }
94
+
95
+ it "preserves sub-element modifications" do
96
+ expect(duped_subject.blah.blargh).to eq subject.blah.blargh
97
+ end
98
+
99
+ it "allows the copy's sub-elements to be modified independently from the original's" do
100
+ expect(subject.blah.blargh).to eq "Janet"
101
+
102
+ duped_subject.blah.blargh = "Dr. Scott"
103
+
104
+ expect(subject.blah.blargh).to eq "Janet"
105
+ expect(duped_subject.blah.blargh).to eq "Dr. Scott"
106
+ end
107
+ end
108
+ end
109
+
110
+ context "when memoizing and then modifying entire recursive structures" do
111
+ subject do
112
+ RecursiveOpenStruct.new(
113
+ { :blah => original_blah }, :recurse_over_arrays => true)
114
+ end
115
+
116
+ before(:each) { subject.blah } # enforce memoization
117
+
118
+ context "when modifying an entire Hash" do
119
+ let(:original_blah) { { :a => 'A', :b => 'B' } }
120
+ let(:new_blah) { { :something_new => "C" } }
121
+
122
+ before(:each) { subject.blah = new_blah }
123
+
124
+ it "returns the modified value instead of the memoized one" do
125
+ expect(subject.blah.something_new).to eq "C"
126
+ end
127
+
128
+ specify "the old value no longer exists" do
129
+ expect(subject.blah.a).to be_nil
130
+ end
131
+ end
132
+
133
+ context "when modifying an entire Array" do
134
+ let(:original_blah) { [1, 2, 3] }
135
+
136
+ it "returns the modified value instead of the memoized one" do
137
+ new_blah = [4, 5, 6]
138
+ subject.blah = new_blah
139
+ expect(subject.blah).to eq new_blah
140
+ end
141
+ end
142
+ end
143
+
144
+ describe 'recursing over arrays' do
145
+ let(:blah_list) { [ { :foo => '1' }, { :foo => '2' }, 'baz' ] }
146
+ let(:h) { { :blah => blah_list } }
147
+
148
+ context "when recursing over arrays is enabled" do
149
+ subject { RecursiveOpenStruct.new(h, :recurse_over_arrays => true) }
150
+
151
+ it { expect(subject.blah.length).to eq 3 }
152
+ it { expect(subject.blah[0].foo).to eq '1' }
153
+ it { expect(subject.blah[1].foo).to eq '2' }
154
+ it { expect(subject.blah_as_a_hash).to eq blah_list }
155
+ it { expect(subject.blah[2]).to eq 'baz' }
156
+
157
+ context "when an inner value changes" do
158
+ let(:updated_blah_list) { [ { :foo => '1' }, { :foo => 'Dr Scott' }, 'baz' ] }
159
+ let(:updated_h) { { :blah => updated_blah_list } }
160
+
161
+ before(:each) { subject.blah[1].foo = "Dr Scott" }
162
+
163
+ it "Retains changes across Array lookups" do
164
+ expect(subject.blah[1].foo).to eq "Dr Scott"
165
+ end
166
+
167
+ it "propagates the changes through to .to_h across Array lookups" do
168
+ expect(subject.to_h).to eq({
169
+ :blah => [ { :foo => '1' }, { :foo => "Dr Scott" }, 'baz' ]
170
+ })
171
+ end
172
+
173
+ it "deep-copies hashes within Arrays" do
174
+ subject.to_h[:blah][1][:foo] = "Rocky"
175
+
176
+ expect(subject.blah[1].foo).to eq "Dr Scott"
177
+ end
178
+
179
+ it "does not mutate the input hash passed to the constructor" do
180
+ expect(h[:blah][1][:foo]).to eq '2'
181
+ end
182
+
183
+ it "the deep copy recurses over Arrays as well" do
184
+ expect(h[:blah][1][:foo]).to eq '2'
185
+ end
186
+
187
+ describe "#dup" do
188
+ let(:duped_subject) { subject.dup }
189
+
190
+ it "preserves sub-element modifications" do
191
+ expect(duped_subject.blah[1].foo).to eq subject.blah[1].foo
192
+ end
193
+
194
+ it "allows the copy's sub-elements to be modified independently from the original's" do
195
+ duped_subject.blah[1].foo = "Rocky"
196
+
197
+ expect(duped_subject.blah[1].foo).to eq "Rocky"
198
+ expect(subject.blah[1].foo).to eq "Dr Scott"
199
+ end
200
+ end
201
+ end
202
+
203
+ context "when array is nested deeper" do
204
+ let(:deep_hash) { { :foo => { :blah => blah_list } } }
205
+ subject { RecursiveOpenStruct.new(deep_hash, :recurse_over_arrays => true) }
206
+
207
+ it { expect(subject.foo.blah.length).to eq 3 }
208
+ it "Retains changes across Array lookups" do
209
+ subject.foo.blah[1].foo = "Dr Scott"
210
+ expect(subject.foo.blah[1].foo).to eq "Dr Scott"
211
+ end
212
+
213
+ end
214
+
215
+ context "when array is in an array" do
216
+ let(:haah) { { :blah => [ blah_list ] } }
217
+ subject { RecursiveOpenStruct.new(haah, :recurse_over_arrays => true) }
218
+
219
+ it { expect(subject.blah.length).to eq 1 }
220
+ it { expect(subject.blah[0].length).to eq 3 }
221
+ it "Retains changes across Array lookups" do
222
+ subject.blah[0][1].foo = "Dr Scott"
223
+
224
+ expect(subject.blah[0][1].foo).to eq "Dr Scott"
225
+ end
226
+
227
+ end
228
+
229
+ end # when recursing over arrays is enabled
230
+
231
+ context "when recursing over arrays is disabled" do
232
+ subject { RecursiveOpenStruct.new(h) }
233
+
234
+ it { expect(subject.blah.length).to eq 3 }
235
+ it { expect(subject.blah[0]).to eq({ :foo => '1' }) }
236
+ it { expect(subject.blah[0][:foo]).to eq '1' }
237
+ end # when recursing over arrays is disabled
238
+
239
+ describe 'modifying an array and recursing over it' do
240
+ let(:h) { {} }
241
+ subject { RecursiveOpenStruct.new(h, recurse_over_arrays: true) }
242
+
243
+ context 'when adding an array with hashes into the tree' do
244
+ before(:each) do
245
+ subject.mystery = {}
246
+ subject.mystery.science = [{ theatre: 9000 }]
247
+ end
248
+
249
+ it "ROS's it" do
250
+ expect(subject.mystery.science[0].theatre).to eq 9000
251
+ end
252
+ end
253
+
254
+ context 'when appending a hash to an array' do
255
+ before(:each) do
256
+ subject.mystery = {}
257
+ subject.mystery.science = []
258
+ subject.mystery.science << { theatre: 9000 }
259
+ end
260
+
261
+ it "ROS's it" do
262
+ expect(subject.mystery.science[0].theatre).to eq 9000
263
+ end
264
+
265
+ specify "the changes show up in .to_h" do
266
+ expect(subject.to_h).to eq({ mystery: { science: [{theatre: 9000}]}})
267
+ end
268
+ end
269
+
270
+ context 'after appending a hash to an array' do
271
+ before(:each) do
272
+ subject.mystery = {}
273
+ subject.mystery.science = []
274
+ subject.mystery.science[0] = {}
275
+ end
276
+
277
+ it "can have new values be set" do
278
+ expect do
279
+ subject.mystery.science[0].theatre = 9000
280
+ end.to_not raise_error
281
+
282
+ expect(subject.mystery.science[0].theatre).to eq 9000
283
+ end
284
+ end
285
+ end # modifying an array and then recursing
286
+ end # recursing over arrays
287
+
288
+ describe 'nested nil values' do
289
+ let(:h) { { foo: { bar: nil }} }
290
+ it 'returns nil' do
291
+ expect(subject.foo.bar).to be_nil
292
+ end
293
+
294
+ it 'returns a hash with the key and a nil value' do
295
+ expect(subject.to_hash).to eq({ foo: { bar: nil }})
296
+ end
297
+ end # nested nil values
298
+ end # recursive behavior
299
+ end
@@ -0,0 +1,19 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'pry'
5
+
6
+ if ENV['COVERAGE'] == 'true'
7
+ require 'simplecov'
8
+ SimpleCov.start
9
+ end
10
+
11
+ # Requires supporting files with custom matchers and macros, etc,
12
+ # in ./support/ and its subdirectories.
13
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
14
+
15
+ RSpec.configure do |config|
16
+ config.run_all_when_everything_filtered = true
17
+ config.filter_run :focus
18
+ # config.expect_with(:rspec) { |c| c.syntax = :should }
19
+ end
metadata ADDED
@@ -0,0 +1,174 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: recursive-open-struct-sd
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.2
5
+ platform: ruby
6
+ authors:
7
+ - William (B.J.) Snow Orvis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-10-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rdoc
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.2'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.2'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: |
98
+ RecursiveOpenStruct is a subclass of OpenStruct. It differs from
99
+ OpenStruct in that it allows nested hashes to be treated in a recursive
100
+ fashion. For example:
101
+
102
+ ros = RecursiveOpenStruct.new({ :a => { :b => 'c' } })
103
+ ros.a.b # 'c'
104
+
105
+ Also, nested hashes can still be accessed as hashes:
106
+
107
+ ros.a_as_a_hash # { :b => 'c' }
108
+
109
+ > This is a fork of the original recursive-open-struct
110
+ > to include a fix for https://github.com/aetherknight/recursive-open-struct/issues/46
111
+ email: aetherknight@gmail.com
112
+ executables: []
113
+ extensions: []
114
+ extra_rdoc_files:
115
+ - CHANGELOG.md
116
+ - LICENSE.txt
117
+ - README.md
118
+ files:
119
+ - ".document"
120
+ - ".gitignore"
121
+ - ".rspec"
122
+ - ".travis.yml"
123
+ - AUTHORS.txt
124
+ - CHANGELOG.md
125
+ - Gemfile
126
+ - LICENSE.txt
127
+ - README.md
128
+ - Rakefile
129
+ - lib/recursive-open-struct.rb
130
+ - lib/recursive_open_struct.rb
131
+ - lib/recursive_open_struct/debug_inspect.rb
132
+ - lib/recursive_open_struct/deep_dup.rb
133
+ - lib/recursive_open_struct/ruby_19_backport.rb
134
+ - lib/recursive_open_struct/version.rb
135
+ - recursive-open-struct-sd.gemspec
136
+ - spec/recursive_open_struct/debug_inspect_spec.rb
137
+ - spec/recursive_open_struct/indifferent_access_spec.rb
138
+ - spec/recursive_open_struct/open_struct_behavior_spec.rb
139
+ - spec/recursive_open_struct/ostruct_2_0_0_spec.rb
140
+ - spec/recursive_open_struct/recursion_and_subclassing_spec.rb
141
+ - spec/recursive_open_struct/recursion_spec.rb
142
+ - spec/spec_helper.rb
143
+ homepage: http://github.com/aetherknight/recursive-open-struct
144
+ licenses:
145
+ - MIT
146
+ metadata: {}
147
+ post_install_message:
148
+ rdoc_options: []
149
+ require_paths:
150
+ - lib
151
+ required_ruby_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ required_rubygems_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ requirements: []
162
+ rubyforge_project:
163
+ rubygems_version: 2.5.1
164
+ signing_key:
165
+ specification_version: 4
166
+ summary: OpenStruct subclass that returns nested hash attributes as RecursiveOpenStructs
167
+ test_files:
168
+ - spec/recursive_open_struct/debug_inspect_spec.rb
169
+ - spec/recursive_open_struct/indifferent_access_spec.rb
170
+ - spec/recursive_open_struct/open_struct_behavior_spec.rb
171
+ - spec/recursive_open_struct/ostruct_2_0_0_spec.rb
172
+ - spec/recursive_open_struct/recursion_and_subclassing_spec.rb
173
+ - spec/recursive_open_struct/recursion_spec.rb
174
+ - spec/spec_helper.rb