ice_nine 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -15
  2. data/.travis.yml +8 -7
  3. data/Gemfile +10 -0
  4. data/Gemfile.devtools +5 -4
  5. data/README.md +4 -24
  6. data/benchmarks/speed.rb +4 -2
  7. data/config/flay.yml +2 -2
  8. data/config/flog.yml +1 -1
  9. data/config/reek.yml +9 -2
  10. data/config/rubocop.yml +0 -1
  11. data/ice_nine.gemspec +1 -0
  12. data/lib/ice_nine.rb +24 -9
  13. data/lib/ice_nine/core_ext/object.rb +17 -3
  14. data/lib/ice_nine/freezer.rb +43 -3
  15. data/lib/ice_nine/freezer/array.rb +6 -8
  16. data/lib/ice_nine/freezer/false_class.rb +2 -2
  17. data/lib/ice_nine/freezer/hash.rb +22 -9
  18. data/lib/ice_nine/freezer/hash/state.rb +3 -3
  19. data/lib/ice_nine/freezer/module.rb +2 -2
  20. data/lib/ice_nine/freezer/nil_class.rb +2 -2
  21. data/lib/ice_nine/freezer/no_freeze.rb +5 -6
  22. data/lib/ice_nine/freezer/numeric.rb +2 -2
  23. data/lib/ice_nine/freezer/object.rb +7 -8
  24. data/lib/ice_nine/freezer/range.rb +7 -8
  25. data/lib/ice_nine/freezer/rubinius.rb +2 -2
  26. data/lib/ice_nine/freezer/struct.rb +2 -2
  27. data/lib/ice_nine/freezer/symbol.rb +2 -2
  28. data/lib/ice_nine/freezer/true_class.rb +2 -2
  29. data/lib/ice_nine/support/recursion_guard.rb +48 -28
  30. data/lib/ice_nine/version.rb +3 -3
  31. data/spec/integration/ice_nine/class_methods/deep_freeze_bang_spec.rb +21 -0
  32. data/spec/integration/ice_nine/class_methods/deep_freeze_spec.rb +5 -254
  33. data/spec/shared/array_deep_freeze_shared_spec.rb +9 -0
  34. data/spec/shared/hash_deep_freeze_shared_spec.rb +23 -0
  35. data/spec/shared/ice_nine_deep_freeze_shared_spec.rb +263 -0
  36. data/spec/shared/no_freeze_deep_freeze_shared_spec.rb +19 -0
  37. data/spec/shared/object_deep_freeze_shared_spec.rb +19 -0
  38. data/spec/shared/range_deep_freeze_shared_spec.rb +13 -0
  39. data/spec/unit/ice_nine/class_methods/deep_freeze_bang_spec.rb +24 -0
  40. data/spec/unit/ice_nine/class_methods/deep_freeze_spec.rb +6 -19
  41. data/spec/unit/ice_nine/core_ext/object/deep_freeze_bang_spec.rb +24 -0
  42. data/spec/unit/ice_nine/core_ext/object/deep_freeze_spec.rb +3 -9
  43. data/spec/unit/ice_nine/freezer/array/class_methods/deep_freeze_spec.rb +0 -14
  44. data/spec/unit/ice_nine/freezer/false_class/class_methods/deep_freeze_spec.rb +1 -7
  45. data/spec/unit/ice_nine/freezer/hash/class_methods/deep_freeze_spec.rb +1 -29
  46. data/spec/unit/ice_nine/freezer/module/class_methods/deep_freeze_spec.rb +1 -7
  47. data/spec/unit/ice_nine/freezer/nil_class/class_methods/deep_freeze_spec.rb +1 -7
  48. data/spec/unit/ice_nine/freezer/no_freeze/class_methods/deep_freeze_spec.rb +3 -23
  49. data/spec/unit/ice_nine/freezer/numeric/class_methods/deep_freeze_spec.rb +3 -12
  50. data/spec/unit/ice_nine/freezer/object/class_methods/deep_freeze_spec.rb +0 -18
  51. data/spec/unit/ice_nine/freezer/range/class_methods/deep_freeze_spec.rb +0 -18
  52. data/spec/unit/ice_nine/freezer/struct/class_methods/deep_freeze_spec.rb +1 -11
  53. data/spec/unit/ice_nine/freezer/symbol/class_methods/deep_freeze_spec.rb +1 -7
  54. data/spec/unit/ice_nine/freezer/true_class/class_methods/deep_freeze_spec.rb +1 -7
  55. data/spec/unit/ice_nine/recursion_guard/frozen/guard_spec.rb +28 -0
  56. data/spec/unit/ice_nine/recursion_guard/object_set/guard_spec.rb +31 -0
  57. data/spec/unit/object/deep_freeze_spec.rb +3 -9
  58. metadata +54 -41
  59. data/spec/unit/ice_nine/recursion_guard/class_methods/guard_spec.rb +0 -33
@@ -13,14 +13,13 @@ module IceNine
13
13
  # object.frozen? # => false
14
14
  #
15
15
  # @param [Object] object
16
+ # @param [RecursionGuard] _recursion_guard
16
17
  #
17
18
  # @return [Object]
18
- #
19
- # @api public
20
- def self.deep_freeze(object, *)
19
+ def self.guarded_deep_freeze(object, _recursion_guard)
21
20
  object
22
21
  end
23
22
 
24
- end # class NoFreeze
25
- end # class Freezer
26
- end # module IceNine
23
+ end # NoFreeze
24
+ end # Freezer
25
+ end # IceNine
@@ -6,5 +6,5 @@ module IceNine
6
6
  # Skip freezing Numeric objects
7
7
  class Numeric < NoFreeze; end
8
8
 
9
- end # class Freezer
10
- end # module IceNine
9
+ end # Freezer
10
+ end # IceNine
@@ -15,11 +15,10 @@ module IceNine
15
15
  # @param [RecursionGuard] recursion_guard
16
16
  #
17
17
  # @return [Object]
18
- #
19
- # @api public
20
- def self.deep_freeze(object, recursion_guard = RecursionGuard.new)
21
- freeze_instance_variables(object, recursion_guard)
18
+ def self.guarded_deep_freeze(object, recursion_guard)
22
19
  object.freeze
20
+ freeze_instance_variables(object, recursion_guard)
21
+ object
23
22
  end
24
23
 
25
24
  # Handle freezing the object's instance variables
@@ -32,7 +31,7 @@ module IceNine
32
31
  # @api private
33
32
  def self.freeze_instance_variables(object, recursion_guard)
34
33
  object.instance_variables.each do |ivar_name|
35
- IceNine.deep_freeze(
34
+ Freezer.guarded_deep_freeze(
36
35
  object.instance_variable_get(ivar_name),
37
36
  recursion_guard
38
37
  )
@@ -41,8 +40,8 @@ module IceNine
41
40
 
42
41
  private_class_method :freeze_instance_variables
43
42
 
44
- end # class Object
43
+ end # Object
45
44
 
46
45
  BasicObject = Object
47
- end # class Freezer
48
- end # module IceNine
46
+ end # Freezer
47
+ end # IceNine
@@ -17,14 +17,13 @@ module IceNine
17
17
  # @param [RecursionGuard] recursion_guard
18
18
  #
19
19
  # @return [Range]
20
- #
21
- # @api public
22
- def self.deep_freeze(range, recursion_guard = RecursionGuard.new)
23
- IceNine.deep_freeze(range.begin, recursion_guard)
24
- IceNine.deep_freeze(range.end, recursion_guard)
20
+ def self.guarded_deep_freeze(range, recursion_guard)
25
21
  super
22
+ Freezer.guarded_deep_freeze(range.begin, recursion_guard)
23
+ Freezer.guarded_deep_freeze(range.end, recursion_guard)
24
+ range
26
25
  end
27
26
 
28
- end # class Range
29
- end # class Freezer
30
- end # module IceNine
27
+ end # Range
28
+ end # Freezer
29
+ end # IceNine
@@ -6,5 +6,5 @@ module IceNine
6
6
  # Skip freezing Rubinius objects
7
7
  class Rubinius < NoFreeze; end
8
8
 
9
- end # class Freezer
10
- end # module IceNine
9
+ end # Freezer
10
+ end # IceNine
@@ -6,5 +6,5 @@ module IceNine
6
6
  # A freezer class for handling Struct objects
7
7
  class Struct < Array; end
8
8
 
9
- end # class Freezer
10
- end # module IceNine
9
+ end # Freezer
10
+ end # IceNine
@@ -6,5 +6,5 @@ module IceNine
6
6
  # Skip freezing Symbol objects
7
7
  class Symbol < NoFreeze; end
8
8
 
9
- end # class Freezer
10
- end # module IceNine
9
+ end # Freezer
10
+ end # IceNine
@@ -6,5 +6,5 @@ module IceNine
6
6
  # Skip freezing true objects
7
7
  class TrueClass < NoFreeze; end
8
8
 
9
- end # class Freezer
10
- end # module IceNine
9
+ end # Freezer
10
+ end # IceNine
@@ -3,35 +3,55 @@
3
3
  module IceNine
4
4
 
5
5
  # Protect against infinite recursion
6
+ #
7
+ # @private
6
8
  class RecursionGuard
7
9
 
8
- # Initialize a recursion guard
9
- #
10
- # @return [undefined]
11
- #
12
- # @api public
13
- def initialize
14
- @object_ids = {}
15
- end
16
-
17
- # Guard against recursively calling a block with the same object
18
- #
19
- # @example
20
- # recursion_guard = IceNine::RecursionGuard.new
21
- # recursion_guard.guard(object_id) do
22
- # logic_which_may_be_recursively_called_with_object_id(recursion_guard)
23
- # end
24
- #
25
- # @param [Integer] caller_object_id
26
- #
27
- # @return [Object]
28
- #
29
- # @api public
30
- def guard(caller_object_id)
31
- return if @object_ids.key?(caller_object_id)
32
- @object_ids[caller_object_id] = nil
33
- yield
34
- end
10
+ # Protects against infinite recursion by never yielding with the same
11
+ # object more than once.
12
+ class ObjectSet < self
13
+
14
+ # Initialize a recursion guard
15
+ #
16
+ # @return [undefined]
17
+ def initialize
18
+ @object_ids = {}
19
+ end
20
+
21
+ # Guard against recursively calling a block with the same object
22
+ #
23
+ # @example
24
+ # recursion_guard = IceNine::RecursionGuard::ObjectSet.new
25
+ # recursion_guard.guard(object) do
26
+ # logic_which_may_be_recursively_called_with_object(recursion_guard)
27
+ # end
28
+ #
29
+ # @param [Object] object
30
+ #
31
+ # @return [Object]
32
+ def guard(object)
33
+ caller_object_id = object.__id__
34
+ return object if @object_ids.key?(caller_object_id)
35
+ @object_ids[caller_object_id] = nil
36
+ yield
37
+ end
38
+
39
+ end # ObjectSet
40
+
41
+ # Protects against infinite recursion by not yielding with frozen objects
42
+ class Frozen < self
43
+
44
+ # Guard against recursively calling a block with the same frozen object
45
+ #
46
+ # @param [Object] object
47
+ #
48
+ # @return [Object]
49
+ def guard(object)
50
+ return object if object.frozen?
51
+ yield
52
+ end
53
+
54
+ end # Frozen
35
55
 
36
56
  end # RecursionGuard
37
- end # module IceNine
57
+ end # IceNine
@@ -2,7 +2,7 @@
2
2
 
3
3
  module IceNine
4
4
 
5
- # Unreleased gem version
6
- VERSION = '0.10.0'.freeze
5
+ # Gem version
6
+ VERSION = '0.11.0'.freeze
7
7
 
8
- end # module IceNine
8
+ end # IceNine
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'ice_nine'
5
+ require 'delegate'
6
+
7
+ describe IceNine, '.deep_freeze!' do
8
+ subject { object.deep_freeze!(value) }
9
+
10
+ let(:object) { IceNine }
11
+
12
+ context 'with a shallowly frozen value' do
13
+ let(:value) { %w[a b].freeze }
14
+
15
+ it 'does not deep freeze' do
16
+ expect(subject.select(&:frozen?)).to be_empty
17
+ end
18
+ end
19
+
20
+ it_should_behave_like 'IceNine.deep_freeze'
21
+ end
@@ -9,262 +9,13 @@ describe IceNine, '.deep_freeze' do
9
9
 
10
10
  let(:object) { IceNine }
11
11
 
12
- context 'with an Object' do
13
- let(:value) { Object.new }
12
+ context 'with a shallowly frozen value' do
13
+ let(:value) { ['a', %w[b c]].freeze }
14
14
 
15
- before do
16
- value.instance_eval { @a = '1' }
17
- end
18
-
19
- it 'returns the object' do
20
- should be(value)
21
- end
22
-
23
- it 'freezes the object' do
24
- expect { subject }.to change(value, :frozen?).from(false).to(true)
25
- end
26
-
27
- it 'freezes the instance variables in the Object' do
28
- expect(subject.instance_variable_get(:@a)).to be_frozen
29
- end
30
-
31
- context 'with a circular reference' do
32
- before do
33
- value.instance_eval { @self = self }
34
- end
35
-
36
- it 'returns the object' do
37
- should be(value)
38
- end
39
-
40
- it 'freezes the object' do
41
- expect { subject }.to change(value, :frozen?).from(false).to(true)
42
- end
43
-
44
- it 'freezes the instance variables in the Object' do
45
- expect(subject.instance_variable_get(:@a)).to be_frozen
46
- end
15
+ it 'does a deep freeze' do
16
+ expect(subject.select(&:frozen?)).to eql(value)
47
17
  end
48
18
  end
49
19
 
50
- context 'with an Array' do
51
- let(:value) { %w[a] }
52
-
53
- it 'returns the object' do
54
- should be(value)
55
- end
56
-
57
- it 'freezes the object' do
58
- expect { subject }.to change(value, :frozen?).from(false).to(true)
59
- end
60
-
61
- it 'freezes each element in the Array' do
62
- expect(subject.select(&:frozen?)).to eql(subject)
63
- end
64
-
65
- context 'with a circular reference' do
66
- before do
67
- value << value
68
- end
69
-
70
- it 'returns the object' do
71
- should be(value)
72
- end
73
-
74
- it 'freezes the object' do
75
- expect { subject }.to change(value, :frozen?).from(false).to(true)
76
- end
77
-
78
- it 'freezes each element in the Array' do
79
- expect(subject.select(&:frozen?)).to eql(subject)
80
- end
81
- end
82
- end
83
-
84
- context 'with a Hash' do
85
- let(:value) { { Object.new => Object.new } }
86
-
87
- it 'returns the object' do
88
- should be(value)
89
- end
90
-
91
- it 'freezes the object' do
92
- expect { subject }.to change(value, :frozen?).from(false).to(true)
93
- end
94
-
95
- it 'freezes each key in the Hash' do
96
- expect(subject.keys.select(&:frozen?)).to eql(subject.keys)
97
- end
98
-
99
- it 'freezes each value in the Hash' do
100
- expect(subject.values.select(&:frozen?)).to eql(subject.values)
101
- end
102
-
103
- context 'with a circular reference' do
104
- before do
105
- value[value] = value
106
- end
107
-
108
- it 'returns the object' do
109
- should be(value)
110
- end
111
-
112
- it 'freezes the object' do
113
- expect { subject }.to change(value, :frozen?).from(false).to(true)
114
- end
115
-
116
- it 'freezes each key in the Hash' do
117
- expect(subject.keys.select(&:frozen?)).to eql(subject.keys)
118
- end
119
-
120
- it 'freezes each value in the Hash' do
121
- expect(subject.values.select(&:frozen?)).to eql(subject.values)
122
- end
123
- end
124
- end
125
-
126
- context 'with a Range' do
127
- let(:value) { 'a'..'z' }
128
-
129
- it 'returns the object' do
130
- should be(value)
131
- end
132
-
133
- it 'freezes the object' do
134
- expect { subject }.to change(value, :frozen?).from(false).to(true)
135
- end
136
-
137
- it 'freeze the first object in the Range' do
138
- expect(subject.begin).to be_frozen
139
- end
140
-
141
- it 'freeze the last object in the Range' do
142
- expect(subject.end).to be_frozen
143
- end
144
- end
145
-
146
- context 'with a String' do
147
- let(:value) { '' }
148
-
149
- before do
150
- value.instance_eval { @a = '1' }
151
- end
152
-
153
- it 'returns the object' do
154
- should be(value)
155
- end
156
-
157
- it 'freezes the object' do
158
- expect { subject }.to change(value, :frozen?).from(false).to(true)
159
- end
160
-
161
- it 'freezes the instance variables in the String' do
162
- expect(subject.instance_variable_get(:@a)).to be_frozen
163
- end
164
-
165
- context 'with a circular reference' do
166
- before do
167
- value.instance_eval { @self = self }
168
- end
169
-
170
- it 'returns the object' do
171
- should be(value)
172
- end
173
-
174
- it 'freezes the object' do
175
- expect { subject }.to change(value, :frozen?).from(false).to(true)
176
- end
177
-
178
- it 'freezes the instance variables in the String' do
179
- expect(subject.instance_variable_get(:@a)).to be_frozen
180
- end
181
- end
182
- end
183
-
184
- context 'with a Struct' do
185
- let(:value) { klass.new(%w[ 1 2 ]) }
186
- let(:klass) { Struct.new(:a) }
187
-
188
- it 'returns the object' do
189
- should be(value)
190
- end
191
-
192
- it 'freezes the object' do
193
- expect { subject }.to change(value, :frozen?).from(false).to(true)
194
- end
195
-
196
- it 'freezes each value in the Struct' do
197
- expect(subject.values.select(&:frozen?)).to eql(subject.values)
198
- end
199
-
200
- context 'with a circular reference' do
201
- before do
202
- value.a = value
203
- end
204
-
205
- it 'returns the object' do
206
- should be(value)
207
- end
208
-
209
- it 'freezes the object' do
210
- expect { subject }.to change(value, :frozen?).from(false).to(true)
211
- end
212
-
213
- it 'freezes each value in the Struct' do
214
- expect(subject.values.select(&:frozen?)).to eql(subject.values)
215
- end
216
- end
217
- end
218
-
219
- context 'with an SimpleDelegator' do
220
- let(:value) { SimpleDelegator.new(nil) }
221
-
222
- before do
223
- value.instance_eval { @a = '1' }
224
- end
225
-
226
- it 'returns the object' do
227
- should be(value)
228
- end
229
-
230
- it 'freezes the object' do
231
- expect { subject }.to change(value, :frozen?).from(false).to(true)
232
- end
233
-
234
- it 'freezes the instance variables in the SimpleDelegator' do
235
- expect(subject.instance_variable_get(:@a)).to be_frozen
236
- end
237
-
238
- context 'with a circular reference' do
239
- before do
240
- value.instance_eval { @self = self }
241
- end
242
-
243
- it 'returns the object' do
244
- should be(value)
245
- end
246
-
247
- it 'freezes the object' do
248
- expect { subject }.to change(value, :frozen?).from(false).to(true)
249
- end
250
-
251
- it 'freezes the instance variables in the SimpleDelegator' do
252
- expect(subject.instance_variable_get(:@a)).to be_frozen
253
- end
254
- end
255
- end
256
-
257
- [0.0, 0, 0x7fffffffffffffff, true, false, nil, :symbol].each do |value|
258
- context "with a #{value.class}" do
259
- let(:value) { value }
260
-
261
- it 'returns the object' do
262
- should be(value)
263
- end
264
-
265
- it 'does not freeze the object' do
266
- expect { subject }.to_not change(value, :frozen?).from(false)
267
- end
268
- end
269
- end
20
+ it_should_behave_like 'IceNine.deep_freeze'
270
21
  end