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.
- checksums.yaml +7 -15
- data/.travis.yml +8 -7
- data/Gemfile +10 -0
- data/Gemfile.devtools +5 -4
- data/README.md +4 -24
- data/benchmarks/speed.rb +4 -2
- data/config/flay.yml +2 -2
- data/config/flog.yml +1 -1
- data/config/reek.yml +9 -2
- data/config/rubocop.yml +0 -1
- data/ice_nine.gemspec +1 -0
- data/lib/ice_nine.rb +24 -9
- data/lib/ice_nine/core_ext/object.rb +17 -3
- data/lib/ice_nine/freezer.rb +43 -3
- data/lib/ice_nine/freezer/array.rb +6 -8
- data/lib/ice_nine/freezer/false_class.rb +2 -2
- data/lib/ice_nine/freezer/hash.rb +22 -9
- data/lib/ice_nine/freezer/hash/state.rb +3 -3
- data/lib/ice_nine/freezer/module.rb +2 -2
- data/lib/ice_nine/freezer/nil_class.rb +2 -2
- data/lib/ice_nine/freezer/no_freeze.rb +5 -6
- data/lib/ice_nine/freezer/numeric.rb +2 -2
- data/lib/ice_nine/freezer/object.rb +7 -8
- data/lib/ice_nine/freezer/range.rb +7 -8
- data/lib/ice_nine/freezer/rubinius.rb +2 -2
- data/lib/ice_nine/freezer/struct.rb +2 -2
- data/lib/ice_nine/freezer/symbol.rb +2 -2
- data/lib/ice_nine/freezer/true_class.rb +2 -2
- data/lib/ice_nine/support/recursion_guard.rb +48 -28
- data/lib/ice_nine/version.rb +3 -3
- data/spec/integration/ice_nine/class_methods/deep_freeze_bang_spec.rb +21 -0
- data/spec/integration/ice_nine/class_methods/deep_freeze_spec.rb +5 -254
- data/spec/shared/array_deep_freeze_shared_spec.rb +9 -0
- data/spec/shared/hash_deep_freeze_shared_spec.rb +23 -0
- data/spec/shared/ice_nine_deep_freeze_shared_spec.rb +263 -0
- data/spec/shared/no_freeze_deep_freeze_shared_spec.rb +19 -0
- data/spec/shared/object_deep_freeze_shared_spec.rb +19 -0
- data/spec/shared/range_deep_freeze_shared_spec.rb +13 -0
- data/spec/unit/ice_nine/class_methods/deep_freeze_bang_spec.rb +24 -0
- data/spec/unit/ice_nine/class_methods/deep_freeze_spec.rb +6 -19
- data/spec/unit/ice_nine/core_ext/object/deep_freeze_bang_spec.rb +24 -0
- data/spec/unit/ice_nine/core_ext/object/deep_freeze_spec.rb +3 -9
- data/spec/unit/ice_nine/freezer/array/class_methods/deep_freeze_spec.rb +0 -14
- data/spec/unit/ice_nine/freezer/false_class/class_methods/deep_freeze_spec.rb +1 -7
- data/spec/unit/ice_nine/freezer/hash/class_methods/deep_freeze_spec.rb +1 -29
- data/spec/unit/ice_nine/freezer/module/class_methods/deep_freeze_spec.rb +1 -7
- data/spec/unit/ice_nine/freezer/nil_class/class_methods/deep_freeze_spec.rb +1 -7
- data/spec/unit/ice_nine/freezer/no_freeze/class_methods/deep_freeze_spec.rb +3 -23
- data/spec/unit/ice_nine/freezer/numeric/class_methods/deep_freeze_spec.rb +3 -12
- data/spec/unit/ice_nine/freezer/object/class_methods/deep_freeze_spec.rb +0 -18
- data/spec/unit/ice_nine/freezer/range/class_methods/deep_freeze_spec.rb +0 -18
- data/spec/unit/ice_nine/freezer/struct/class_methods/deep_freeze_spec.rb +1 -11
- data/spec/unit/ice_nine/freezer/symbol/class_methods/deep_freeze_spec.rb +1 -7
- data/spec/unit/ice_nine/freezer/true_class/class_methods/deep_freeze_spec.rb +1 -7
- data/spec/unit/ice_nine/recursion_guard/frozen/guard_spec.rb +28 -0
- data/spec/unit/ice_nine/recursion_guard/object_set/guard_spec.rb +31 -0
- data/spec/unit/object/deep_freeze_spec.rb +3 -9
- metadata +54 -41
- 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 #
|
25
|
-
end #
|
26
|
-
end #
|
23
|
+
end # NoFreeze
|
24
|
+
end # Freezer
|
25
|
+
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
|
-
|
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 #
|
43
|
+
end # Object
|
45
44
|
|
46
45
|
BasicObject = Object
|
47
|
-
end #
|
48
|
-
end #
|
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 #
|
29
|
-
end #
|
30
|
-
end #
|
27
|
+
end # Range
|
28
|
+
end # Freezer
|
29
|
+
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
|
-
#
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
@
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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 #
|
57
|
+
end # IceNine
|
data/lib/ice_nine/version.rb
CHANGED
@@ -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
|
13
|
-
let(:value) {
|
12
|
+
context 'with a shallowly frozen value' do
|
13
|
+
let(:value) { ['a', %w[b c]].freeze }
|
14
14
|
|
15
|
-
|
16
|
-
|
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
|
-
|
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
|