fancy-open-struct 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +0 -1
- data/CHANGELOG +4 -0
- data/lib/fancy-open-struct.rb +56 -33
- data/lib/fancy-open-struct/version.rb +1 -1
- data/spec/fancy_open_struct_spec.rb +45 -0
- metadata +1 -1
data/.travis.yml
CHANGED
data/CHANGELOG
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
0.4.0
|
2
|
+
- Changed ostruct methods (new_ostruct_member, recurse_over_array) so that they are private
|
3
|
+
- Overhauled the caching sub-system and how values are set and retrieved to fix a bug with values not updating
|
4
|
+
|
1
5
|
0.3.0
|
2
6
|
- Deprecated the display_recursive_open_hash alias for debug_inspect
|
3
7
|
- Removed some unnecessary code from the to_h method
|
data/lib/fancy-open-struct.rb
CHANGED
@@ -22,43 +22,14 @@ class FancyOpenStruct < OpenStruct
|
|
22
22
|
@sub_elements = {}
|
23
23
|
end
|
24
24
|
|
25
|
-
def new_ostruct_member(name)
|
26
|
-
name = name.to_sym
|
27
|
-
unless self.respond_to?(name)
|
28
|
-
define_singleton_method name do
|
29
|
-
v = @table[name]
|
30
|
-
if v.is_a?(Hash)
|
31
|
-
@sub_elements[name] ||= self.class.new(v, :recurse_over_arrays => @recurse_over_arrays)
|
32
|
-
elsif v.is_a?(Array) and @recurse_over_arrays
|
33
|
-
@sub_elements[name] ||= recurse_over_array v
|
34
|
-
else
|
35
|
-
v
|
36
|
-
end
|
37
|
-
end
|
38
|
-
define_singleton_method("#{name}=") { |x| modifiable[name] = x }
|
39
|
-
define_singleton_method("#{name}_as_a_hash") { @table[name] }
|
40
|
-
end
|
41
|
-
name
|
42
|
-
end
|
43
|
-
|
44
|
-
def recurse_over_array(array)
|
45
|
-
array.map do |a|
|
46
|
-
if a.is_a? Hash
|
47
|
-
self.class.new(a, :recurse_over_arrays => true)
|
48
|
-
elsif a.is_a? Array
|
49
|
-
recurse_over_array a
|
50
|
-
else
|
51
|
-
a
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
25
|
def to_h
|
57
26
|
@table.dup.update(@sub_elements) do |k, oldval, newval|
|
58
27
|
if newval.kind_of?(self.class)
|
59
28
|
newval.to_h
|
60
29
|
elsif newval.kind_of?(Array)
|
61
30
|
newval.map { |a| a.kind_of?(self.class) ? a.to_h : a }
|
31
|
+
else
|
32
|
+
newval
|
62
33
|
end
|
63
34
|
end
|
64
35
|
end
|
@@ -73,17 +44,66 @@ class FancyOpenStruct < OpenStruct
|
|
73
44
|
|
74
45
|
# Hash getter method which translates the key to a Symbol
|
75
46
|
def [](key)
|
76
|
-
|
47
|
+
key = key.to_sym unless key.kind_of? Symbol
|
48
|
+
@table[key]
|
77
49
|
end
|
78
50
|
|
79
51
|
# Hash setter method which translates the key to a Symbol and also creates
|
80
52
|
# a getter method (OpenStruct member) for accessing the key/value later via dot syntax
|
81
53
|
def []=(key, value)
|
82
|
-
|
54
|
+
key = key.to_sym unless key.kind_of? Symbol
|
55
|
+
modifiable[new_ostruct_member(key)] = value
|
56
|
+
build_sub_elements_key_cache(key)
|
83
57
|
end
|
84
58
|
|
85
59
|
private
|
86
60
|
|
61
|
+
def new_ostruct_member(name)
|
62
|
+
name = name.to_sym
|
63
|
+
unless self.respond_to?(name)
|
64
|
+
define_singleton_method name do
|
65
|
+
value = @table[name]
|
66
|
+
if value.is_a?(Hash)
|
67
|
+
@sub_elements[name] ||= self.class.new(value, :recurse_over_arrays => @recurse_over_arrays)
|
68
|
+
elsif value.is_a?(Array) and @recurse_over_arrays
|
69
|
+
@sub_elements[name] ||= recurse_over_array value
|
70
|
+
else
|
71
|
+
value
|
72
|
+
end
|
73
|
+
end
|
74
|
+
define_singleton_method("#{name}=") do |x|
|
75
|
+
modifiable[name] = x
|
76
|
+
build_sub_elements_key_cache(name)
|
77
|
+
end
|
78
|
+
define_singleton_method("#{name}_as_a_hash") { @table[name] }
|
79
|
+
end
|
80
|
+
name
|
81
|
+
end
|
82
|
+
|
83
|
+
def build_sub_elements_key_cache(name)
|
84
|
+
value = @table[name]
|
85
|
+
if value.is_a?(Hash)
|
86
|
+
@sub_elements[name] = self.class.new(value, :recurse_over_arrays => @recurse_over_arrays)
|
87
|
+
elsif value.is_a?(Array) and @recurse_over_arrays
|
88
|
+
@sub_elements[name] = recurse_over_array value
|
89
|
+
else
|
90
|
+
@sub_elements[name] = value
|
91
|
+
end
|
92
|
+
@sub_elements[name]
|
93
|
+
end
|
94
|
+
|
95
|
+
def recurse_over_array(array)
|
96
|
+
array.map do |a|
|
97
|
+
if a.is_a? Hash
|
98
|
+
self.class.new(a, :recurse_over_arrays => true)
|
99
|
+
elsif a.is_a? Array
|
100
|
+
recurse_over_array a
|
101
|
+
else
|
102
|
+
a
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
87
107
|
# Dynamically handle any attempts to get or set values via dot syntax
|
88
108
|
# if the OpenStruct member methods haven't already been created
|
89
109
|
def method_missing(mid, *args) # :nodoc:
|
@@ -93,10 +113,13 @@ class FancyOpenStruct < OpenStruct
|
|
93
113
|
raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1) if len != 1
|
94
114
|
# Set up an instance method to point to the key/value in the table and set the value
|
95
115
|
modifiable[new_ostruct_member(mname.to_sym)] = args[0]
|
116
|
+
build_sub_elements_key_cache(mname.to_sym)
|
117
|
+
@table[mname.to_sym]
|
96
118
|
elsif @table.has_key?(mid)
|
97
119
|
# The table has apparently been modified externally, so we need to set up
|
98
120
|
# an instance method to point to the key/value in the table.
|
99
121
|
new_ostruct_member(mname.to_sym)
|
122
|
+
build_sub_elements_key_cache(mname.to_sym)
|
100
123
|
self.send(mid)
|
101
124
|
else
|
102
125
|
nil
|
@@ -127,6 +127,18 @@ describe FancyOpenStruct do
|
|
127
127
|
fos.to_h.should == {:blah => "John Smith"}
|
128
128
|
fos[:blah].should == "John Smith"
|
129
129
|
end
|
130
|
+
|
131
|
+
it 'lets you access keys via strings or symbols' do
|
132
|
+
h = {'blah' => 'John Smith'}
|
133
|
+
fos = FancyOpenStruct.new(h)
|
134
|
+
fos.to_h.should == {:blah => "John Smith"}
|
135
|
+
fos['blah'].should == "John Smith"
|
136
|
+
fos['foo'] = 'bar'
|
137
|
+
fos.foo.should == 'bar'
|
138
|
+
fos[:foo].should == 'bar'
|
139
|
+
fos['foo'].should == 'bar'
|
140
|
+
fos.to_h.should == {:blah => "John Smith", :foo => 'bar'}
|
141
|
+
end
|
130
142
|
end
|
131
143
|
end
|
132
144
|
|
@@ -216,6 +228,32 @@ describe FancyOpenStruct do
|
|
216
228
|
subject.foo.blah[1].foo.should == "Dr Scott"
|
217
229
|
end
|
218
230
|
|
231
|
+
it 'recurses and updates both shallow and deep values if they are changed' do
|
232
|
+
fos = FancyOpenStruct.new({}, :recurse_over_arrays => true)
|
233
|
+
deep_array_hash = [
|
234
|
+
{
|
235
|
+
:foo =>
|
236
|
+
{:bar => :baz}
|
237
|
+
},
|
238
|
+
{
|
239
|
+
:qux => [
|
240
|
+
{:zap => :zam}, {:zip => :boop}
|
241
|
+
]
|
242
|
+
}
|
243
|
+
]
|
244
|
+
fos.bar = deep_array_hash
|
245
|
+
fos.bar[0].foo.bar.should == :baz
|
246
|
+
fos.bar[1].qux[0].zap.should == :zam
|
247
|
+
fos.bar[1].qux[0].zap = :changed1
|
248
|
+
fos.bar[1].qux[0].zap.should == :changed1
|
249
|
+
fos.bar[1].qux[1].zip.should == :boop
|
250
|
+
fos.bar[1].qux[1].zip = :changed2
|
251
|
+
fos.bar[1].qux[1].zip.should == :changed2
|
252
|
+
fos.bar = {:qux_new => [{:zap => :zam}, {:zip => :boop}]}
|
253
|
+
fos.bar.qux.should be_nil
|
254
|
+
fos.bar.qux_new[0].zap.should == :zam
|
255
|
+
fos.bar.qux_new[1].zip.should == :boop
|
256
|
+
end
|
219
257
|
end
|
220
258
|
|
221
259
|
context "when array is in an array" do
|
@@ -285,6 +323,13 @@ describe FancyOpenStruct do
|
|
285
323
|
fossc.one.first.class.should == FancyOpenStructSubClass
|
286
324
|
end
|
287
325
|
|
326
|
+
it 'returns nil for missing keys' do
|
327
|
+
fos = FancyOpenStruct.new {}
|
328
|
+
fos.foo.should be_nil
|
329
|
+
fos['bar'].should be_nil
|
330
|
+
fos[:baz].should be_nil
|
331
|
+
end
|
332
|
+
|
288
333
|
describe 'method aliases' do
|
289
334
|
it 'responds to #to_hash' do
|
290
335
|
FancyOpenStruct.new.respond_to?(:to_hash).should be_true
|