fancy-open-struct 0.3.0 → 0.4.0

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.
@@ -8,4 +8,3 @@ rvm:
8
8
 
9
9
  # Rubies 1.9.x
10
10
  - 1.9.3
11
- - jruby-19mode # JRuby in 1.9 mode
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
@@ -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
- @table[key.to_sym]
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
- modifiable[new_ostruct_member(key.to_sym)] = value
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
@@ -1,5 +1,5 @@
1
1
  require 'ostruct'
2
2
 
3
3
  class FancyOpenStruct < OpenStruct
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
@@ -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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fancy-open-struct
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: