flipper 0.27.1 → 0.28.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog.md +45 -0
- data/Rakefile +3 -3
- data/benchmark/enabled_multiple_actors_ips.rb +20 -0
- data/examples/dsl.rb +3 -3
- data/examples/enabled_for_actor.rb +4 -2
- data/examples/mirroring.rb +59 -0
- data/lib/flipper/adapters/memory.rb +33 -13
- data/lib/flipper/dsl.rb +4 -4
- data/lib/flipper/errors.rb +3 -3
- data/lib/flipper/feature.rb +12 -10
- data/lib/flipper/feature_check_context.rb +8 -4
- data/lib/flipper/gate.rb +12 -11
- data/lib/flipper/gates/actor.rb +11 -8
- data/lib/flipper/gates/group.rb +4 -2
- data/lib/flipper/gates/percentage_of_actors.rb +4 -5
- data/lib/flipper/identifier.rb +2 -2
- data/lib/flipper/instrumentation/log_subscriber.rb +24 -5
- data/lib/flipper/instrumentation/subscriber.rb +0 -1
- data/lib/flipper/poller.rb +1 -1
- data/lib/flipper/types/actor.rb +13 -13
- data/lib/flipper/types/group.rb +4 -4
- data/lib/flipper/version.rb +1 -1
- data/lib/flipper.rb +3 -3
- data/spec/flipper/adapters/memory_spec.rb +11 -2
- data/spec/flipper/dsl_spec.rb +5 -5
- data/spec/flipper/feature_check_context_spec.rb +5 -5
- data/spec/flipper/feature_spec.rb +76 -32
- data/spec/flipper/gates/boolean_spec.rb +1 -1
- data/spec/flipper/gates/group_spec.rb +2 -3
- data/spec/flipper/gates/percentage_of_actors_spec.rb +61 -5
- data/spec/flipper/gates/percentage_of_time_spec.rb +2 -2
- data/spec/flipper/instrumentation/log_subscriber_spec.rb +5 -5
- data/spec/flipper/types/actor_spec.rb +45 -45
- data/spec/flipper/types/group_spec.rb +2 -2
- data/spec/flipper_integration_spec.rb +62 -50
- metadata +4 -2
data/lib/flipper/types/group.rb
CHANGED
@@ -16,16 +16,16 @@ module Flipper
|
|
16
16
|
@block = block
|
17
17
|
@single_argument = call_with_no_context?(@block)
|
18
18
|
else
|
19
|
-
@block = ->(
|
19
|
+
@block = ->(actor, context) { false }
|
20
20
|
@single_argument = false
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
def match?(
|
24
|
+
def match?(actor, context)
|
25
25
|
if @single_argument
|
26
|
-
@block.call(
|
26
|
+
@block.call(actor)
|
27
27
|
else
|
28
|
-
@block.call(
|
28
|
+
@block.call(actor, context)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
data/lib/flipper/version.rb
CHANGED
data/lib/flipper.rb
CHANGED
@@ -72,12 +72,12 @@ module Flipper
|
|
72
72
|
#
|
73
73
|
# name - The Symbol name of the group.
|
74
74
|
# block - The block that should be used to determine if the group matches a
|
75
|
-
# given
|
75
|
+
# given actor.
|
76
76
|
#
|
77
77
|
# Examples
|
78
78
|
#
|
79
|
-
# Flipper.register(:admins) { |
|
80
|
-
#
|
79
|
+
# Flipper.register(:admins) { |actor|
|
80
|
+
# actor.respond_to?(:admin?) && actor.admin?
|
81
81
|
# }
|
82
82
|
#
|
83
83
|
# Returns a Flipper::Group.
|
@@ -1,8 +1,17 @@
|
|
1
1
|
RSpec.describe Flipper::Adapters::Memory do
|
2
2
|
let(:source) { {} }
|
3
|
-
subject { described_class.new(source) }
|
4
3
|
|
5
|
-
|
4
|
+
context 'threadsafe: true' do
|
5
|
+
subject { described_class.new(source, threadsafe: true) }
|
6
|
+
|
7
|
+
it_should_behave_like 'a flipper adapter'
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'threadsafe: false' do
|
11
|
+
subject { described_class.new(source, threadsafe: false) }
|
12
|
+
|
13
|
+
it_should_behave_like 'a flipper adapter'
|
14
|
+
end
|
6
15
|
|
7
16
|
it "can initialize from big hash" do
|
8
17
|
flipper = Flipper.new(subject)
|
data/spec/flipper/dsl_spec.rb
CHANGED
@@ -149,12 +149,12 @@ RSpec.describe Flipper::DSL do
|
|
149
149
|
end
|
150
150
|
|
151
151
|
describe '#actor' do
|
152
|
-
context 'for
|
152
|
+
context 'for an actor' do
|
153
153
|
it 'returns actor instance' do
|
154
|
-
|
155
|
-
|
156
|
-
expect(
|
157
|
-
expect(
|
154
|
+
actor = Flipper::Actor.new(33)
|
155
|
+
flipper_actor = subject.actor(actor)
|
156
|
+
expect(flipper_actor).to be_instance_of(Flipper::Types::Actor)
|
157
|
+
expect(flipper_actor.value).to eq('33')
|
158
158
|
end
|
159
159
|
end
|
160
160
|
|
@@ -1,12 +1,12 @@
|
|
1
1
|
RSpec.describe Flipper::FeatureCheckContext do
|
2
2
|
let(:feature_name) { :new_profiles }
|
3
3
|
let(:values) { Flipper::GateValues.new({}) }
|
4
|
-
let(:
|
4
|
+
let(:actor) { Flipper::Actor.new('5') }
|
5
5
|
let(:options) do
|
6
6
|
{
|
7
7
|
feature_name: feature_name,
|
8
8
|
values: values,
|
9
|
-
|
9
|
+
actors: [actor],
|
10
10
|
}
|
11
11
|
end
|
12
12
|
|
@@ -14,7 +14,7 @@ RSpec.describe Flipper::FeatureCheckContext do
|
|
14
14
|
instance = described_class.new(**options)
|
15
15
|
expect(instance.feature_name).to eq(feature_name)
|
16
16
|
expect(instance.values).to eq(values)
|
17
|
-
expect(instance.
|
17
|
+
expect(instance.actors).to eq([actor])
|
18
18
|
end
|
19
19
|
|
20
20
|
it 'requires feature_name' do
|
@@ -31,8 +31,8 @@ RSpec.describe Flipper::FeatureCheckContext do
|
|
31
31
|
end.to raise_error(ArgumentError)
|
32
32
|
end
|
33
33
|
|
34
|
-
it 'requires
|
35
|
-
options.delete(:
|
34
|
+
it 'requires actors' do
|
35
|
+
options.delete(:actors)
|
36
36
|
expect do
|
37
37
|
described_class.new(**options)
|
38
38
|
end.to raise_error(ArgumentError)
|
@@ -32,6 +32,52 @@ RSpec.describe Flipper::Feature do
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
describe "#enabled?" do
|
36
|
+
context "for an actor" do
|
37
|
+
let(:actor) { Flipper::Actor.new("User;1") }
|
38
|
+
|
39
|
+
it 'returns true if feature is enabled' do
|
40
|
+
subject.enable
|
41
|
+
expect(subject.enabled?(actor)).to be(true)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'returns false if feature is disabled' do
|
45
|
+
subject.disable
|
46
|
+
expect(subject.enabled?(actor)).to be(false)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "for multiple actors" do
|
51
|
+
let(:actors) {
|
52
|
+
[
|
53
|
+
Flipper::Actor.new("User;1"),
|
54
|
+
Flipper::Actor.new("User;2"),
|
55
|
+
Flipper::Actor.new("User;3"),
|
56
|
+
]
|
57
|
+
}
|
58
|
+
|
59
|
+
it 'returns true if feature is enabled' do
|
60
|
+
subject.enable
|
61
|
+
expect(subject.enabled?(actors)).to be(true)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'returns true if feature is enabled for any actor' do
|
65
|
+
subject.enable_actor actors.first
|
66
|
+
expect(subject.enabled?(actors)).to be(true)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'returns true if feature is enabled for any actor with multiple arguments' do
|
70
|
+
subject.enable_actor actors.last
|
71
|
+
expect(subject.enabled?(*actors)).to be(true)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'returns false if feature is disabled for all actors' do
|
75
|
+
subject.disable
|
76
|
+
expect(subject.enabled?(actors)).to be(false)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
35
81
|
describe '#to_s' do
|
36
82
|
it 'returns name as string' do
|
37
83
|
feature = described_class.new(:search, adapter)
|
@@ -148,29 +194,29 @@ RSpec.describe Flipper::Feature do
|
|
148
194
|
end
|
149
195
|
|
150
196
|
it 'is recorded for enable' do
|
151
|
-
|
152
|
-
gate = subject.gate_for(
|
197
|
+
actor = Flipper::Types::Actor.new(Flipper::Actor.new('1'))
|
198
|
+
gate = subject.gate_for(actor)
|
153
199
|
|
154
|
-
subject.enable(
|
200
|
+
subject.enable(actor)
|
155
201
|
|
156
202
|
event = instrumenter.events.last
|
157
203
|
expect(event).not_to be_nil
|
158
204
|
expect(event.name).to eq('feature_operation.flipper')
|
159
205
|
expect(event.payload[:feature_name]).to eq(:search)
|
160
206
|
expect(event.payload[:operation]).to eq(:enable)
|
161
|
-
expect(event.payload[:thing]).to eq(
|
207
|
+
expect(event.payload[:thing]).to eq(actor)
|
162
208
|
expect(event.payload[:result]).not_to be_nil
|
163
209
|
end
|
164
210
|
|
165
211
|
it 'always instruments flipper type instance for enable' do
|
166
|
-
|
167
|
-
gate = subject.gate_for(
|
212
|
+
actor = Flipper::Actor.new('1')
|
213
|
+
gate = subject.gate_for(actor)
|
168
214
|
|
169
|
-
subject.enable(
|
215
|
+
subject.enable(actor)
|
170
216
|
|
171
217
|
event = instrumenter.events.last
|
172
218
|
expect(event).not_to be_nil
|
173
|
-
expect(event.payload[:thing]).to eq(Flipper::Types::Actor.new(
|
219
|
+
expect(event.payload[:thing]).to eq(Flipper::Types::Actor.new(actor))
|
174
220
|
end
|
175
221
|
|
176
222
|
it 'is recorded for disable' do
|
@@ -219,15 +265,15 @@ RSpec.describe Flipper::Feature do
|
|
219
265
|
end
|
220
266
|
|
221
267
|
it 'always instruments flipper type instance for disable' do
|
222
|
-
|
223
|
-
gate = subject.gate_for(
|
268
|
+
actor = Flipper::Actor.new('1')
|
269
|
+
gate = subject.gate_for(actor)
|
224
270
|
|
225
|
-
subject.disable(
|
271
|
+
subject.disable(actor)
|
226
272
|
|
227
273
|
event = instrumenter.events.last
|
228
274
|
expect(event).not_to be_nil
|
229
275
|
expect(event.payload[:operation]).to eq(:disable)
|
230
|
-
expect(event.payload[:thing]).to eq(Flipper::Types::Actor.new(
|
276
|
+
expect(event.payload[:thing]).to eq(Flipper::Types::Actor.new(actor))
|
231
277
|
end
|
232
278
|
|
233
279
|
it 'is recorded for add' do
|
@@ -275,17 +321,15 @@ RSpec.describe Flipper::Feature do
|
|
275
321
|
end
|
276
322
|
|
277
323
|
it 'is recorded for enabled?' do
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
subject.enabled?(thing)
|
324
|
+
actor = Flipper::Types::Actor.new(Flipper::Actor.new('1'))
|
325
|
+
subject.enabled?(actor)
|
282
326
|
|
283
327
|
event = instrumenter.events.last
|
284
328
|
expect(event).not_to be_nil
|
285
329
|
expect(event.name).to eq('feature_operation.flipper')
|
286
330
|
expect(event.payload[:feature_name]).to eq(:search)
|
287
331
|
expect(event.payload[:operation]).to eq(:enabled?)
|
288
|
-
expect(event.payload[:
|
332
|
+
expect(event.payload[:actors]).to eq([actor])
|
289
333
|
expect(event.payload[:result]).to eq(false)
|
290
334
|
end
|
291
335
|
|
@@ -293,8 +337,8 @@ RSpec.describe Flipper::Feature do
|
|
293
337
|
actor = Flipper::Types::Actor.new(user)
|
294
338
|
{
|
295
339
|
nil => nil,
|
296
|
-
user => actor,
|
297
|
-
actor => actor,
|
340
|
+
user => [actor],
|
341
|
+
actor => [actor],
|
298
342
|
}.each do |thing, wrapped_thing|
|
299
343
|
it "always instruments #{thing.inspect} as #{wrapped_thing.class} for enabled?" do
|
300
344
|
subject.enabled?(thing)
|
@@ -302,7 +346,7 @@ RSpec.describe Flipper::Feature do
|
|
302
346
|
event = instrumenter.events.last
|
303
347
|
expect(event).not_to be_nil
|
304
348
|
expect(event.payload[:operation]).to eq(:enabled?)
|
305
|
-
expect(event.payload[:
|
349
|
+
expect(event.payload[:actors]).to eq(wrapped_thing)
|
306
350
|
end
|
307
351
|
end
|
308
352
|
end
|
@@ -428,10 +472,10 @@ RSpec.describe Flipper::Feature do
|
|
428
472
|
|
429
473
|
context 'when one or more groups enabled' do
|
430
474
|
before do
|
431
|
-
@staff = Flipper.register(:staff) { |
|
432
|
-
@preview_features = Flipper.register(:preview_features) { |
|
433
|
-
@not_enabled = Flipper.register(:not_enabled) { |
|
434
|
-
@disabled = Flipper.register(:disabled) { |
|
475
|
+
@staff = Flipper.register(:staff) { |actor| true }
|
476
|
+
@preview_features = Flipper.register(:preview_features) { |actor| true }
|
477
|
+
@not_enabled = Flipper.register(:not_enabled) { |actor| true }
|
478
|
+
@disabled = Flipper.register(:disabled) { |actor| true }
|
435
479
|
subject.enable @staff
|
436
480
|
subject.enable @preview_features
|
437
481
|
subject.disable @disabled
|
@@ -467,10 +511,10 @@ RSpec.describe Flipper::Feature do
|
|
467
511
|
|
468
512
|
context 'when one or more groups enabled' do
|
469
513
|
before do
|
470
|
-
@staff = Flipper.register(:staff) { |
|
471
|
-
@preview_features = Flipper.register(:preview_features) { |
|
472
|
-
@not_enabled = Flipper.register(:not_enabled) { |
|
473
|
-
@disabled = Flipper.register(:disabled) { |
|
514
|
+
@staff = Flipper.register(:staff) { |actor| true }
|
515
|
+
@preview_features = Flipper.register(:preview_features) { |actor| true }
|
516
|
+
@not_enabled = Flipper.register(:not_enabled) { |actor| true }
|
517
|
+
@disabled = Flipper.register(:disabled) { |actor| true }
|
474
518
|
subject.enable @staff
|
475
519
|
subject.enable @preview_features
|
476
520
|
subject.disable @disabled
|
@@ -494,10 +538,10 @@ RSpec.describe Flipper::Feature do
|
|
494
538
|
|
495
539
|
context 'when one or more groups enabled' do
|
496
540
|
before do
|
497
|
-
@staff = Flipper.register(:staff) { |
|
498
|
-
@preview_features = Flipper.register(:preview_features) { |
|
499
|
-
@not_enabled = Flipper.register(:not_enabled) { |
|
500
|
-
@disabled = Flipper.register(:disabled) { |
|
541
|
+
@staff = Flipper.register(:staff) { |actor| true }
|
542
|
+
@preview_features = Flipper.register(:preview_features) { |actor| true }
|
543
|
+
@not_enabled = Flipper.register(:not_enabled) { |actor| true }
|
544
|
+
@disabled = Flipper.register(:disabled) { |actor| true }
|
501
545
|
subject.enable @staff
|
502
546
|
subject.enable @preview_features
|
503
547
|
subject.disable @disabled
|
@@ -9,7 +9,7 @@ RSpec.describe Flipper::Gates::Boolean do
|
|
9
9
|
Flipper::FeatureCheckContext.new(
|
10
10
|
feature_name: feature_name,
|
11
11
|
values: Flipper::GateValues.new(boolean: bool),
|
12
|
-
|
12
|
+
actors: [Flipper::Types::Actor.new(Flipper::Actor.new('1'))]
|
13
13
|
)
|
14
14
|
end
|
15
15
|
|
@@ -9,18 +9,17 @@ RSpec.describe Flipper::Gates::Group do
|
|
9
9
|
Flipper::FeatureCheckContext.new(
|
10
10
|
feature_name: feature_name,
|
11
11
|
values: Flipper::GateValues.new(groups: set),
|
12
|
-
|
12
|
+
actors: [Flipper::Types::Actor.new(Flipper::Actor.new('5'))]
|
13
13
|
)
|
14
14
|
end
|
15
15
|
|
16
16
|
describe '#open?' do
|
17
17
|
context 'with a group in adapter, but not registered' do
|
18
18
|
before do
|
19
|
-
Flipper.register(:staff) { |
|
19
|
+
Flipper.register(:staff) { |actor| true }
|
20
20
|
end
|
21
21
|
|
22
22
|
it 'ignores group' do
|
23
|
-
thing = Flipper::Actor.new('5')
|
24
23
|
expect(subject.open?(context(Set[:newbs, :staff]))).to be(true)
|
25
24
|
end
|
26
25
|
end
|
@@ -5,11 +5,11 @@ RSpec.describe Flipper::Gates::PercentageOfActors do
|
|
5
5
|
described_class.new
|
6
6
|
end
|
7
7
|
|
8
|
-
def context(percentage_of_actors_value, feature = feature_name,
|
8
|
+
def context(percentage_of_actors_value, feature = feature_name, actors = nil)
|
9
9
|
Flipper::FeatureCheckContext.new(
|
10
10
|
feature_name: feature,
|
11
11
|
values: Flipper::GateValues.new(percentage_of_actors: percentage_of_actors_value),
|
12
|
-
|
12
|
+
actors: Array(actors) || [Flipper::Types::Actor.new(Flipper::Actor.new('1'))]
|
13
13
|
)
|
14
14
|
end
|
15
15
|
|
@@ -20,7 +20,7 @@ RSpec.describe Flipper::Gates::PercentageOfActors do
|
|
20
20
|
let(:number_of_actors) { 10_000 }
|
21
21
|
|
22
22
|
let(:actors) do
|
23
|
-
(1..number_of_actors).map { |n| Flipper::Actor.new(n) }
|
23
|
+
(1..number_of_actors).map { |n| Flipper::Types::Actor.new(Flipper::Actor.new(n.to_s)) }
|
24
24
|
end
|
25
25
|
|
26
26
|
let(:feature_one_enabled_actors) do
|
@@ -48,13 +48,69 @@ RSpec.describe Flipper::Gates::PercentageOfActors do
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
+
context "with an array of actors" do
|
52
|
+
let(:percentage) { 0.05 }
|
53
|
+
let(:percentage_as_integer) { percentage * 100 }
|
54
|
+
let(:number_of_actors) { 3_000 }
|
55
|
+
|
56
|
+
let(:user_actors) do
|
57
|
+
(1..number_of_actors).map { |n| Flipper::Types::Actor.new(Flipper::Actor.new("User;#{n}")) }
|
58
|
+
end
|
59
|
+
|
60
|
+
let(:team_actors) do
|
61
|
+
(1..number_of_actors).map { |n| Flipper::Types::Actor.new(Flipper::Actor.new("Team;#{n}")) }
|
62
|
+
end
|
63
|
+
|
64
|
+
let(:org_actors) do
|
65
|
+
(1..number_of_actors).map { |n| Flipper::Types::Actor.new(Flipper::Actor.new("Org;#{n}")) }
|
66
|
+
end
|
67
|
+
|
68
|
+
let(:actors) { user_actors + team_actors + org_actors }
|
69
|
+
|
70
|
+
let(:feature_one_enabled_actors) do
|
71
|
+
actors.each_slice(3).select do |group|
|
72
|
+
context = context(percentage_as_integer, :name_one, group)
|
73
|
+
subject.open?(context)
|
74
|
+
end.flatten
|
75
|
+
end
|
76
|
+
|
77
|
+
let(:feature_two_enabled_actors) do
|
78
|
+
actors.each_slice(3).select do |group|
|
79
|
+
context = context(percentage_as_integer, :name_two, group)
|
80
|
+
subject.open?(context)
|
81
|
+
end.flatten
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'does not enable both features for same set of actors' do
|
85
|
+
expect(feature_one_enabled_actors).not_to eq(feature_two_enabled_actors)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'enables feature for accurate number of actors for each feature' do
|
89
|
+
margin_of_error = 0.02 * actors.size # 2 percent margin of error
|
90
|
+
expected_enabled_size = actors.size * percentage
|
91
|
+
|
92
|
+
[
|
93
|
+
feature_one_enabled_actors.size,
|
94
|
+
feature_two_enabled_actors.size,
|
95
|
+
].each do |size|
|
96
|
+
expect(size).to be_within(margin_of_error).of(expected_enabled_size)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
it "is consistent regardless of order of actors" do
|
101
|
+
actors = user_actors.first(10)
|
102
|
+
results = 100.times.map { |n| subject.open?(context(75, :some_feature, actors.shuffle)) }
|
103
|
+
expect(results.uniq).to eq([true])
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
51
107
|
context 'for fractional percentage' do
|
52
108
|
let(:decimal) { 0.001 }
|
53
109
|
let(:percentage) { decimal * 100 }
|
54
110
|
let(:number_of_actors) { 10_000 }
|
55
111
|
|
56
112
|
let(:actors) do
|
57
|
-
(1..number_of_actors).map { |n| Flipper::Actor.new(n) }
|
113
|
+
(1..number_of_actors).map { |n| Flipper::Types::Actor.new(Flipper::Actor.new(n.to_s)) }
|
58
114
|
end
|
59
115
|
|
60
116
|
subject { described_class.new }
|
@@ -64,7 +120,7 @@ RSpec.describe Flipper::Gates::PercentageOfActors do
|
|
64
120
|
expected_open_count = number_of_actors * decimal
|
65
121
|
|
66
122
|
open_count = actors.select do |actor|
|
67
|
-
context = context(percentage, :feature, actor)
|
123
|
+
context = context(percentage, :feature, [actor])
|
68
124
|
subject.open?(context)
|
69
125
|
end.size
|
70
126
|
|
@@ -5,11 +5,11 @@ RSpec.describe Flipper::Gates::PercentageOfTime do
|
|
5
5
|
described_class.new
|
6
6
|
end
|
7
7
|
|
8
|
-
def context(percentage_of_time_value, feature = feature_name,
|
8
|
+
def context(percentage_of_time_value, feature = feature_name, actors = nil)
|
9
9
|
Flipper::FeatureCheckContext.new(
|
10
10
|
feature_name: feature,
|
11
11
|
values: Flipper::GateValues.new(percentage_of_time: percentage_of_time_value),
|
12
|
-
|
12
|
+
actors: Array(actors) || [Flipper::Types::Actor.new(Flipper::Actor.new('1'))]
|
13
13
|
)
|
14
14
|
end
|
15
15
|
|
@@ -18,8 +18,8 @@ RSpec.describe Flipper::Instrumentation::LogSubscriber do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
before do
|
21
|
-
Flipper.register(:admins) do |
|
22
|
-
|
21
|
+
Flipper.register(:admins) do |actor|
|
22
|
+
actor.respond_to?(:admin?) && actor.admin?
|
23
23
|
end
|
24
24
|
|
25
25
|
@io = StringIO.new
|
@@ -46,7 +46,7 @@ RSpec.describe Flipper::Instrumentation::LogSubscriber do
|
|
46
46
|
|
47
47
|
it 'logs feature calls with result after operation' do
|
48
48
|
feature_line = find_line('Flipper feature(search) enabled? false')
|
49
|
-
expect(feature_line).to include('[
|
49
|
+
expect(feature_line).to include('[ actors=nil ]')
|
50
50
|
end
|
51
51
|
|
52
52
|
it 'logs adapter calls' do
|
@@ -56,7 +56,7 @@ RSpec.describe Flipper::Instrumentation::LogSubscriber do
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
-
context 'feature enabled checks with
|
59
|
+
context 'feature enabled checks with an actor' do
|
60
60
|
let(:user) { Flipper::Types::Actor.new(Flipper::Actor.new('1')) }
|
61
61
|
|
62
62
|
before do
|
@@ -64,7 +64,7 @@ RSpec.describe Flipper::Instrumentation::LogSubscriber do
|
|
64
64
|
flipper[:search].enabled?(user)
|
65
65
|
end
|
66
66
|
|
67
|
-
it 'logs
|
67
|
+
it 'logs actors for feature' do
|
68
68
|
feature_line = find_line('Flipper feature(search) enabled?')
|
69
69
|
expect(feature_line).to include(user.inspect)
|
70
70
|
end
|
@@ -2,11 +2,11 @@ require 'flipper/types/actor'
|
|
2
2
|
|
3
3
|
RSpec.describe Flipper::Types::Actor do
|
4
4
|
subject do
|
5
|
-
|
6
|
-
described_class.new(
|
5
|
+
actor = actor_class.new('2')
|
6
|
+
described_class.new(actor)
|
7
7
|
end
|
8
8
|
|
9
|
-
let(:
|
9
|
+
let(:actor_class) do
|
10
10
|
Class.new do
|
11
11
|
attr_reader :flipper_id
|
12
12
|
|
@@ -22,14 +22,14 @@ RSpec.describe Flipper::Types::Actor do
|
|
22
22
|
|
23
23
|
describe '.wrappable?' do
|
24
24
|
it 'returns true if actor' do
|
25
|
-
|
26
|
-
|
27
|
-
expect(described_class.wrappable?(
|
25
|
+
actor = actor_class.new('1')
|
26
|
+
actor_type_instance = described_class.new(actor)
|
27
|
+
expect(described_class.wrappable?(actor_type_instance)).to eq(true)
|
28
28
|
end
|
29
29
|
|
30
30
|
it 'returns true if responds to flipper_id' do
|
31
|
-
|
32
|
-
expect(described_class.wrappable?(
|
31
|
+
actor = actor_class.new(10)
|
32
|
+
expect(described_class.wrappable?(actor)).to eq(true)
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'returns false if nil' do
|
@@ -38,27 +38,27 @@ RSpec.describe Flipper::Types::Actor do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
describe '.wrap' do
|
41
|
-
context 'for actor' do
|
42
|
-
it 'returns actor' do
|
43
|
-
|
44
|
-
expect(
|
45
|
-
expect(
|
41
|
+
context 'for actor type instance' do
|
42
|
+
it 'returns actor type instance' do
|
43
|
+
actor_type_instance = described_class.wrap(subject)
|
44
|
+
expect(actor_type_instance).to be_instance_of(described_class)
|
45
|
+
expect(actor_type_instance).to be(subject)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
context 'for other
|
50
|
-
it 'returns actor' do
|
51
|
-
|
52
|
-
|
53
|
-
expect(
|
49
|
+
context 'for other object' do
|
50
|
+
it 'returns actor type instance' do
|
51
|
+
actor = actor_class.new('1')
|
52
|
+
actor_type_instance = described_class.wrap(actor)
|
53
|
+
expect(actor_type_instance).to be_instance_of(described_class)
|
54
54
|
end
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
it 'initializes with
|
59
|
-
|
60
|
-
|
61
|
-
expect(
|
58
|
+
it 'initializes with object that responds to flipper_id' do
|
59
|
+
actor = actor_class.new('1')
|
60
|
+
actor_type_instance = described_class.new(actor)
|
61
|
+
expect(actor_type_instance.value).to eq('1')
|
62
62
|
end
|
63
63
|
|
64
64
|
it 'raises error when initialized with nil' do
|
@@ -68,48 +68,48 @@ RSpec.describe Flipper::Types::Actor do
|
|
68
68
|
end
|
69
69
|
|
70
70
|
it 'raises error when initalized with non-wrappable object' do
|
71
|
-
|
71
|
+
unwrappable_object = Struct.new(:id).new(1)
|
72
72
|
expect do
|
73
|
-
described_class.new(
|
73
|
+
described_class.new(unwrappable_object)
|
74
74
|
end.to raise_error(ArgumentError,
|
75
|
-
"#{
|
75
|
+
"#{unwrappable_object.inspect} must respond to flipper_id, but does not")
|
76
76
|
end
|
77
77
|
|
78
78
|
it 'converts id to string' do
|
79
|
-
|
80
|
-
actor = described_class.new(
|
79
|
+
actor = actor_class.new(2)
|
80
|
+
actor = described_class.new(actor)
|
81
81
|
expect(actor.value).to eq('2')
|
82
82
|
end
|
83
83
|
|
84
|
-
it 'proxies everything to
|
85
|
-
|
86
|
-
actor = described_class.new(
|
84
|
+
it 'proxies everything to actor' do
|
85
|
+
actor = actor_class.new(10)
|
86
|
+
actor = described_class.new(actor)
|
87
87
|
expect(actor.admin?).to eq(true)
|
88
88
|
end
|
89
89
|
|
90
|
-
it 'exposes
|
91
|
-
|
92
|
-
|
93
|
-
expect(actor
|
90
|
+
it 'exposes actor' do
|
91
|
+
actor = actor_class.new(10)
|
92
|
+
actor_type_instance = described_class.new(actor)
|
93
|
+
expect(actor_type_instance.actor).to be(actor)
|
94
94
|
end
|
95
95
|
|
96
96
|
describe '#respond_to?' do
|
97
97
|
it 'returns true if responds to method' do
|
98
|
-
|
99
|
-
|
100
|
-
expect(
|
98
|
+
actor = actor_class.new('1')
|
99
|
+
actor_type_instance = described_class.new(actor)
|
100
|
+
expect(actor_type_instance.respond_to?(:value)).to eq(true)
|
101
101
|
end
|
102
102
|
|
103
|
-
it 'returns true if
|
104
|
-
|
105
|
-
|
106
|
-
expect(
|
103
|
+
it 'returns true if actor responds to method' do
|
104
|
+
actor = actor_class.new(10)
|
105
|
+
actor_type_instance = described_class.new(actor)
|
106
|
+
expect(actor_type_instance.respond_to?(:admin?)).to eq(true)
|
107
107
|
end
|
108
108
|
|
109
|
-
it 'returns false if does not respond to method and
|
110
|
-
|
111
|
-
|
112
|
-
expect(
|
109
|
+
it 'returns false if does not respond to method and actor does not respond to method' do
|
110
|
+
actor = actor_class.new(10)
|
111
|
+
actor_type_instance = described_class.new(actor)
|
112
|
+
expect(actor_type_instance.respond_to?(:frankenstein)).to eq(false)
|
113
113
|
end
|
114
114
|
end
|
115
115
|
end
|
@@ -90,7 +90,7 @@ RSpec.describe Flipper::Types::Group do
|
|
90
90
|
context = Flipper::FeatureCheckContext.new(
|
91
91
|
feature_name: :my_feature,
|
92
92
|
values: Flipper::GateValues.new({}),
|
93
|
-
|
93
|
+
actors: [Flipper::Types::Actor.new(Flipper::Actor.new('1'))]
|
94
94
|
)
|
95
95
|
group = Flipper.register(:group_with_context) { |actor| actor }
|
96
96
|
yielded_actor = group.match?(admin_actor, context)
|
@@ -101,7 +101,7 @@ RSpec.describe Flipper::Types::Group do
|
|
101
101
|
context = Flipper::FeatureCheckContext.new(
|
102
102
|
feature_name: :my_feature,
|
103
103
|
values: Flipper::GateValues.new({}),
|
104
|
-
|
104
|
+
actors: [Flipper::Types::Actor.new(Flipper::Actor.new('1'))]
|
105
105
|
)
|
106
106
|
group = Flipper.register(:group_with_context) { |actor, context| [actor, context] }
|
107
107
|
yielded_actor, yielded_context = group.match?(admin_actor, context)
|