recursive-open-struct 0.4.1 → 0.4.2

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