recursive-open-struct 0.4.1 → 0.4.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.
@@ -1,15 +1,24 @@
1
1
  require 'ostruct'
2
2
 
3
3
  class RecursiveOpenStruct < OpenStruct
4
- VERSION = "0.4.1"
4
+ VERSION = "0.4.2"
5
5
 
6
6
  def initialize(h=nil, args={})
7
7
  @recurse_over_arrays = args.fetch(:recurse_over_arrays,false)
8
8
  super(h)
9
+ @sub_elements = {}
9
10
  end
10
11
 
11
12
  def to_h
12
- @table.dup
13
+ @table.dup.update(@sub_elements) do |k, oldval, newval|
14
+ if newval.kind_of?(self.class)
15
+ newval.to_h
16
+ elsif newval.kind_of?(Array)
17
+ newval.map { |a| a.kind_of?(self.class) ? a.to_h : a }
18
+ else
19
+ raise "Cached value of unsupported type: #{newval.inspect}"
20
+ end
21
+ end
13
22
  end
14
23
 
15
24
  def new_ostruct_member(name)
@@ -19,9 +28,9 @@ class RecursiveOpenStruct < OpenStruct
19
28
  define_method(name) do
20
29
  v = @table[name]
21
30
  if v.is_a?(Hash)
22
- RecursiveOpenStruct.new(v)
31
+ @sub_elements[name] ||= RecursiveOpenStruct.new(v)
23
32
  elsif v.is_a?(Array) and @recurse_over_arrays
24
- v.map { |a| (a.is_a? Hash) ? RecursiveOpenStruct.new(a, :recurse_over_arrays => true) : a }
33
+ @sub_elements[name] ||= v.map { |a| (a.is_a? Hash) ? RecursiveOpenStruct.new(a, :recurse_over_arrays => true) : a }
25
34
  else
26
35
  v
27
36
  end
@@ -17,12 +17,6 @@ describe RecursiveOpenStruct do
17
17
  ros.asdf.should == "John Smith"
18
18
  end
19
19
 
20
- it "can be converted back to a hash" do
21
- h = { :asdf => 'John Smith' }
22
- ros = RecursiveOpenStruct.new(h)
23
- ros.to_h.should == h
24
- end
25
-
26
20
  it "can modify an existing key" do
27
21
  h = { :blah => 'John Smith' }
28
22
  ros = RecursiveOpenStruct.new(h)
@@ -31,81 +25,120 @@ describe RecursiveOpenStruct do
31
25
  end
32
26
 
33
27
  describe "handling of arbitrary attributes" do
28
+ subject { RecursiveOpenStruct.new }
34
29
  before(:each) do
35
- @ros = RecursiveOpenStruct.new
36
- @ros.blah = "John Smith"
30
+ subject.blah = "John Smith"
37
31
  end
38
32
 
39
33
  describe "#respond?" do
40
- it { @ros.should respond_to :blah }
41
- it { @ros.should respond_to :blah= }
42
- it { @ros.should_not respond_to :asdf }
43
- it { @ros.should_not respond_to :asdf= }
34
+ it { subject.should respond_to :blah }
35
+ it { subject.should respond_to :blah= }
36
+ it { subject.should_not respond_to :asdf }
37
+ it { subject.should_not respond_to :asdf= }
44
38
  end # describe #respond?
45
39
 
46
40
  describe "#methods" do
47
- it { @ros.methods.map(&:to_sym).should include :blah }
48
- it { @ros.methods.map(&:to_sym).should include :blah= }
49
- it { @ros.methods.map(&:to_sym).should_not include :asdf }
50
- it { @ros.methods.map(&:to_sym).should_not include :asdf= }
41
+ it { subject.methods.map(&:to_sym).should include :blah }
42
+ it { subject.methods.map(&:to_sym).should include :blah= }
43
+ it { subject.methods.map(&:to_sym).should_not include :asdf }
44
+ it { subject.methods.map(&:to_sym).should_not include :asdf= }
51
45
  end # describe #methods
52
46
  end # describe handling of arbitrary attributes
53
47
  end # describe behavior it inherits from OpenStruct
54
48
 
55
- describe "recursive behavior" do
56
- before(:each) do
57
- h = { :blah => { :another => 'value' } }
58
- @ros = RecursiveOpenStruct.new(h)
49
+ describe "improvements on OpenStruct" do
50
+ it "can be converted back to a hash" do
51
+ h = { :asdf => 'John Smith' }
52
+ ros = RecursiveOpenStruct.new(h)
53
+ ros.to_h.should == h
59
54
  end
55
+ end
56
+
57
+ describe "recursive behavior" do
58
+ let(:h) { { :blah => { :another => 'value' } } }
59
+ subject { RecursiveOpenStruct.new(h) }
60
60
 
61
61
  it "returns accessed hashes as RecursiveOpenStructs instead of hashes" do
62
- @ros.blah.another.should == 'value'
62
+ subject.blah.another.should == 'value'
63
63
  end
64
64
 
65
65
  it "uses #key_as_a_hash to return key as a Hash" do
66
- @ros.blah_as_a_hash.should == { :another => 'value' }
66
+ subject.blah_as_a_hash.should == { :another => 'value' }
67
67
  end
68
68
 
69
69
  describe "handling loops in the origin Hashes" do
70
- before(:each) do
71
- h1 = { :a => 'a'}
72
- h2 = { :a => 'b', :h1 => h1 }
73
- h1[:h2] = h2
74
- @ros = RecursiveOpenStruct.new(h2)
70
+ let(:h1) { { :a => 'a'} }
71
+ let(:h2) { { :a => 'b', :h1 => h1 } }
72
+ before(:each) { h1[:h2] = h2 }
73
+
74
+ subject { RecursiveOpenStruct.new(h2) }
75
+
76
+ it { subject.h1.a.should == 'a' }
77
+ it { subject.h1.h2.a.should == 'b' }
78
+ it { subject.h1.h2.h1.a.should == 'a' }
79
+ it { subject.h1.h2.h1.h2.a.should == 'b' }
80
+ it { subject.h1.should == subject.h1.h2.h1 }
81
+ it { subject.h1.should_not == subject.h1.h2 }
82
+ end # describe handling loops in the origin Hashes
83
+
84
+ it "can modify a key of a sub-element" do
85
+ h = {
86
+ :blah => {
87
+ :blargh => 'Brad'
88
+ }
89
+ }
90
+ ros = RecursiveOpenStruct.new(h)
91
+ ros.blah.blargh = "Janet"
92
+ ros.blah.blargh.should == "Janet"
93
+ end
94
+
95
+ context "after a sub-element has been modified" do
96
+ let(:hash) do
97
+ {
98
+ :blah => {
99
+ :blargh => 'Brad'
100
+ }
101
+ }
102
+ end
103
+ subject { RecursiveOpenStruct.new(hash) }
104
+ before(:each) { subject.blah.blargh = "Janet" }
105
+ it "returns a hash that contains those modifications" do
106
+ subject.to_h.should == { :blah => { :blargh => "Janet" } }
75
107
  end
108
+ end
76
109
 
77
- it { @ros.h1.a.should == 'a' }
78
- it { @ros.h1.h2.a.should == 'b' }
79
- it { @ros.h1.h2.h1.a.should == 'a' }
80
- it { @ros.h1.h2.h1.h2.a.should == 'b' }
81
- it { @ros.h1.should == @ros.h1.h2.h1 }
82
- it { @ros.h1.should_not == @ros.h1.h2 }
83
- end # describe handling loops in the origin Hashes
84
110
 
85
111
  describe 'recursing over arrays' do
86
112
  let(:blah_list) { [ { :foo => '1' }, { :foo => '2' }, 'baz' ] }
87
113
  let(:h) { { :blah => blah_list } }
88
114
 
89
115
  context "when recursing over arrays is enabled" do
90
- before(:each) do
91
- @ros = RecursiveOpenStruct.new(h, :recurse_over_arrays => true)
116
+ subject { RecursiveOpenStruct.new(h, :recurse_over_arrays => true) }
117
+
118
+ it { subject.blah.length.should == 3 }
119
+ it { subject.blah[0].foo.should == '1' }
120
+ it { subject.blah[1].foo.should == '2' }
121
+ it { subject.blah_as_a_hash.should == blah_list }
122
+ it { subject.blah[2].should == 'baz' }
123
+ it "Retains changes across Array lookups" do
124
+ subject.blah[1].foo = "Dr Scott"
125
+ subject.blah[1].foo.should == "Dr Scott"
126
+ end
127
+ it "propagates the changes through to .to_h across Array lookups" do
128
+ subject.blah[1].foo = "Dr Scott"
129
+ subject.to_h.should == {
130
+ :blah => [ { :foo => '1' }, { :foo => "Dr Scott" }, 'baz' ]
131
+ }
92
132
  end
93
133
 
94
- it { @ros.blah.length.should == 3 }
95
- it { @ros.blah[0].foo.should == '1' }
96
- it { @ros.blah[1].foo.should == '2' }
97
- it { @ros.blah_as_a_hash.should == blah_list }
98
- it { @ros.blah[2].should == 'baz' }
99
134
  end # when recursing over arrays is enabled
100
135
 
101
136
  context "when recursing over arrays is disabled" do
102
- before(:each) do
103
- @ros = RecursiveOpenStruct.new(h)
104
- end
137
+ subject { RecursiveOpenStruct.new(h) }
105
138
 
106
- it { @ros.blah.length.should == 3 }
107
- it { @ros.blah[0].should == { :foo => '1' } }
108
- it { @ros.blah[0][:foo].should == '1' }
139
+ it { subject.blah.length.should == 3 }
140
+ it { subject.blah[0].should == { :foo => '1' } }
141
+ it { subject.blah[0][:foo].should == '1' }
109
142
  end # when recursing over arrays is disabled
110
143
 
111
144
  end # recursing over arrays
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recursive-open-struct
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-29 00:00:00.000000000 Z
12
+ date: 2013-05-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec