Skip to content

Commit b04ee64

Browse files
committed
Allow explicitly blocking actors and groups
1 parent 59c1e50 commit b04ee64

7 files changed

Lines changed: 108 additions & 6 deletions

File tree

lib/flipper.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ def instance=(flipper)
5959
:enabled?, :enable, :disable,
6060
:enable_expression, :disable_expression,
6161
:expression, :add_expression, :remove_expression,
62-
:enable_actor, :disable_actor,
63-
:enable_group, :disable_group,
62+
:enable_actor, :disable_actor, :block_actor, :unblock_actor,
63+
:enable_group, :disable_group, :block_group, :unblock_group,
6464
:enable_percentage_of_actors, :disable_percentage_of_actors,
6565
:enable_percentage_of_time, :disable_percentage_of_time,
6666
:features, :feature, :[], :preload, :preload_all,

lib/flipper/dsl.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,22 @@ def disable_percentage_of_actors(name)
181181
feature(name).disable_percentage_of_actors
182182
end
183183

184+
def block_actor(name, actor)
185+
feature(name).block_actor(actor)
186+
end
187+
188+
def block_group(name, group)
189+
feature(name).block_group(group)
190+
end
191+
192+
def unblock_actor(name, actor)
193+
feature(name).unblock_actor(actor)
194+
end
195+
196+
def unblock_group(name, group)
197+
feature(name).unblock_group(group)
198+
end
199+
184200
# Public: Add a feature.
185201
#
186202
# name - The String or Symbol name of the feature.

lib/flipper/feature.rb

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,32 @@ def disable(thing = false)
6868
end
6969
end
7070

71+
def block(thing)
72+
instrument(:block) do |payload|
73+
adapter.add self
74+
75+
blocking_gate = gate_for(thing, blocking: true)
76+
wrapped_thing = blocking_gate.wrap(thing)
77+
payload[:gate_name] = blocking_gate.name
78+
payload[:thing] = wrapped_thing
79+
80+
adapter.enable self, blocking_gate, wrapped_thing
81+
end
82+
end
83+
84+
def unblock(thing)
85+
instrument(:unblock) do |payload|
86+
adapter.add self
87+
88+
blocking_gate = gate_for(thing, blocking: true)
89+
wrapped_thing = blocking_gate.wrap(thing)
90+
payload[:gate_name] = blocking_gate.name
91+
payload[:thing] = wrapped_thing
92+
93+
adapter.disable self, blocking_gate, wrapped_thing
94+
end
95+
end
96+
7197
# Public: Adds this feature.
7298
#
7399
# Returns the result of Adapter#add.
@@ -118,7 +144,10 @@ def enabled?(*actors)
118144
actors: actors
119145
)
120146

121-
if open_gate = gates.detect { |gate| gate.open?(context) }
147+
if blocking_gate = gates.detect { |gate| gate.blocks?(context) }
148+
payload[:gate_name] = blocking_gate.name
149+
false
150+
elsif open_gate = gates.detect { |gate| gate.open?(context) }
122151
payload[:gate_name] = open_gate.name
123152
true
124153
else
@@ -250,6 +279,22 @@ def disable_percentage_of_actors
250279
disable Types::PercentageOfActors.new(0)
251280
end
252281

282+
def block_actor(actor)
283+
block Types::Actor.wrap(actor)
284+
end
285+
286+
def block_group(group)
287+
block Types::Group.wrap(group)
288+
end
289+
290+
def unblock_actor(actor)
291+
unblock Types::Actor.wrap(actor)
292+
end
293+
294+
def unblock_group(group)
295+
unblock Types::Group.wrap(group)
296+
end
297+
253298
# Public: Returns state for feature (:on, :off, or :conditional).
254299
def state
255300
values = gate_values
@@ -413,6 +458,8 @@ def gates_hash
413458
percentage_of_actors: Gates::PercentageOfActors.new,
414459
percentage_of_time: Gates::PercentageOfTime.new,
415460
group: Gates::Group.new,
461+
blocking_actor: Gates::Actor.new({ blocking: true }),
462+
blocking_group: Gates::Group.new({ blocking: true }),
416463
}.freeze
417464
end
418465

@@ -429,8 +476,8 @@ def gate(name)
429476
#
430477
# Returns a Flipper::Gate.
431478
# Raises Flipper::GateNotFound if no gate found for actor
432-
def gate_for(actor)
433-
gates.detect { |gate| gate.protects?(actor) } || raise(GateNotFound, actor)
479+
def gate_for(actor, blocking: false)
480+
gates.detect { |gate| gate.protects?(actor) && gate.blocking == blocking } || raise(GateNotFound, actor)
434481
end
435482

436483
private

lib/flipper/gate.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
module Flipper
22
class Gate
3+
attr_reader :blocking
4+
35
# Public
46
def initialize(options = {})
7+
@blocking = options.fetch(:blocking, false)
58
end
69

710
# Public: The name of the gate. Implemented in subclass.
@@ -30,6 +33,10 @@ def open?(context)
3033
false
3134
end
3235

36+
def blocks?(context)
37+
false
38+
end
39+
3340
# Internal: Check if a gate is protects an actor. Implemented in subclass.
3441
#
3542
# Returns true if gate protects actor, false if not.

lib/flipper/gate_values.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ class GateValues
99
attr_reader :expression
1010
attr_reader :percentage_of_actors
1111
attr_reader :percentage_of_time
12+
attr_reader :blocking_actors
13+
attr_reader :blocking_groups
1214

1315
def initialize(adapter_values)
1416
@boolean = Typecast.to_boolean(adapter_values[:boolean])
@@ -17,6 +19,8 @@ def initialize(adapter_values)
1719
@expression = adapter_values[:expression]
1820
@percentage_of_actors = Typecast.to_number(adapter_values[:percentage_of_actors])
1921
@percentage_of_time = Typecast.to_number(adapter_values[:percentage_of_time])
22+
@blocking_actors = Typecast.to_set(adapter_values[:blocking_actors])
23+
@blocking_groups = Typecast.to_set(adapter_values[:blocking_groups])
2024
end
2125

2226
def eql?(other)
@@ -26,7 +30,9 @@ def eql?(other)
2630
groups == other.groups &&
2731
expression == other.expression &&
2832
percentage_of_actors == other.percentage_of_actors &&
29-
percentage_of_time == other.percentage_of_time
33+
percentage_of_time == other.percentage_of_time &&
34+
blocking_actors == other.blocking_actors &&
35+
blocking_groups == other.blocking_groups
3036
end
3137
alias_method :==, :eql?
3238
end

lib/flipper/gates/actor.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@ module Gates
33
class Actor < Gate
44
# Internal: The name of the gate. Used for instrumentation, etc.
55
def name
6+
return :blocking_actor if blocking
7+
68
:actor
79
end
810

911
# Internal: Name converted to value safe for adapter.
1012
def key
13+
return :blocking_actors if blocking
14+
1115
:actors
1216
end
1317

@@ -30,6 +34,14 @@ def open?(context)
3034
end
3135
end
3236

37+
def blocks?(context)
38+
return false unless context.actors?
39+
40+
context.actors.any? do |actor|
41+
context.values.blocking_actors.include?(actor.value)
42+
end
43+
end
44+
3345
def wrap(actor)
3446
Types::Actor.wrap(actor)
3547
end

lib/flipper/gates/group.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@ module Gates
33
class Group < Gate
44
# Internal: The name of the gate. Used for instrumentation, etc.
55
def name
6+
return :blocking_group if blocking
7+
68
:group
79
end
810

911
# Internal: Name converted to value safe for adapter.
1012
def key
13+
return :blocking_groups if blocking
14+
1115
:groups
1216
end
1317

@@ -32,6 +36,16 @@ def open?(context)
3236
end
3337
end
3438

39+
def blocks?(context)
40+
return false unless context.actors?
41+
42+
context.values.blocking_groups.any? do |name|
43+
context.actors.any? do |actor|
44+
Flipper.group(name).match?(actor, context)
45+
end
46+
end
47+
end
48+
3549
def wrap(thing)
3650
Types::Group.wrap(thing)
3751
end

0 commit comments

Comments
 (0)