ice_nine 0.10.0 → 0.11.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.
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