recursive-open-struct-sd 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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