Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/flipper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ def instance=(flipper)
:enabled?, :enable, :disable,
:enable_expression, :disable_expression,
:expression, :add_expression, :remove_expression,
:enable_actor, :disable_actor,
:enable_group, :disable_group,
:enable_actor, :disable_actor, :block_actor, :unblock_actor,
:enable_group, :disable_group, :block_group, :unblock_group,
:enable_percentage_of_actors, :disable_percentage_of_actors,
:enable_percentage_of_time, :disable_percentage_of_time,
:features, :feature, :[], :preload, :preload_all,
Expand Down
16 changes: 16 additions & 0 deletions lib/flipper/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,22 @@ def disable_percentage_of_actors(name)
feature(name).disable_percentage_of_actors
end

def block_actor(name, actor)
feature(name).block_actor(actor)
end

def block_group(name, group)
feature(name).block_group(group)
end

def unblock_actor(name, actor)
feature(name).unblock_actor(actor)
end

def unblock_group(name, group)
feature(name).unblock_group(group)
end

# Public: Add a feature.
#
# name - The String or Symbol name of the feature.
Expand Down
53 changes: 50 additions & 3 deletions lib/flipper/feature.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,32 @@ def disable(thing = false)
end
end

def block(thing)
instrument(:block) do |payload|
adapter.add self

blocking_gate = gate_for(thing, blocking: true)
wrapped_thing = blocking_gate.wrap(thing)
payload[:gate_name] = blocking_gate.name
payload[:thing] = wrapped_thing

adapter.enable self, blocking_gate, wrapped_thing
end
end

def unblock(thing)
instrument(:unblock) do |payload|
adapter.add self

blocking_gate = gate_for(thing, blocking: true)
wrapped_thing = blocking_gate.wrap(thing)
payload[:gate_name] = blocking_gate.name
payload[:thing] = wrapped_thing

adapter.disable self, blocking_gate, wrapped_thing
end
end

# Public: Adds this feature.
#
# Returns the result of Adapter#add.
Expand Down Expand Up @@ -118,7 +144,10 @@ def enabled?(*actors)
actors: actors
)

if open_gate = gates.detect { |gate| gate.open?(context) }
if blocking_gate = gates.detect { |gate| gate.blocks?(context) }
payload[:gate_name] = blocking_gate.name
false
elsif open_gate = gates.detect { |gate| gate.open?(context) }
payload[:gate_name] = open_gate.name
true
else
Expand Down Expand Up @@ -250,6 +279,22 @@ def disable_percentage_of_actors
disable Types::PercentageOfActors.new(0)
end

def block_actor(actor)
block Types::Actor.wrap(actor)
end

def block_group(group)
block Types::Group.wrap(group)
end

def unblock_actor(actor)
unblock Types::Actor.wrap(actor)
end

def unblock_group(group)
unblock Types::Group.wrap(group)
end

# Public: Returns state for feature (:on, :off, or :conditional).
def state
values = gate_values
Expand Down Expand Up @@ -413,6 +458,8 @@ def gates_hash
percentage_of_actors: Gates::PercentageOfActors.new,
percentage_of_time: Gates::PercentageOfTime.new,
group: Gates::Group.new,
blocking_actor: Gates::Actor.new({ blocking: true }),
blocking_group: Gates::Group.new({ blocking: true }),
}.freeze
end

Expand All @@ -429,8 +476,8 @@ def gate(name)
#
# Returns a Flipper::Gate.
# Raises Flipper::GateNotFound if no gate found for actor
def gate_for(actor)
gates.detect { |gate| gate.protects?(actor) } || raise(GateNotFound, actor)
def gate_for(actor, blocking: false)
gates.detect { |gate| gate.protects?(actor) && gate.blocking == blocking } || raise(GateNotFound, actor)
end

private
Expand Down
7 changes: 7 additions & 0 deletions lib/flipper/gate.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
module Flipper
class Gate
attr_reader :blocking

# Public
def initialize(options = {})
@blocking = options.fetch(:blocking, false)
end

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

def blocks?(context)
false
end

# Internal: Check if a gate is protects an actor. Implemented in subclass.
#
# Returns true if gate protects actor, false if not.
Expand Down
8 changes: 7 additions & 1 deletion lib/flipper/gate_values.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ class GateValues
attr_reader :expression
attr_reader :percentage_of_actors
attr_reader :percentage_of_time
attr_reader :blocking_actors
attr_reader :blocking_groups

def initialize(adapter_values)
@boolean = Typecast.to_boolean(adapter_values[:boolean])
Expand All @@ -17,6 +19,8 @@ def initialize(adapter_values)
@expression = adapter_values[:expression]
@percentage_of_actors = Typecast.to_number(adapter_values[:percentage_of_actors])
@percentage_of_time = Typecast.to_number(adapter_values[:percentage_of_time])
@blocking_actors = Typecast.to_set(adapter_values[:blocking_actors])
@blocking_groups = Typecast.to_set(adapter_values[:blocking_groups])
end

def eql?(other)
Expand All @@ -26,7 +30,9 @@ def eql?(other)
groups == other.groups &&
expression == other.expression &&
percentage_of_actors == other.percentage_of_actors &&
percentage_of_time == other.percentage_of_time
percentage_of_time == other.percentage_of_time &&
blocking_actors == other.blocking_actors &&
blocking_groups == other.blocking_groups
end
alias_method :==, :eql?
end
Expand Down
12 changes: 12 additions & 0 deletions lib/flipper/gates/actor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ module Gates
class Actor < Gate
# Internal: The name of the gate. Used for instrumentation, etc.
def name
return :blocking_actor if blocking

:actor
end

# Internal: Name converted to value safe for adapter.
def key
return :blocking_actors if blocking

:actors
end

Expand All @@ -30,6 +34,14 @@ def open?(context)
end
end

def blocks?(context)
return false unless context.actors?

context.actors.any? do |actor|
context.values.blocking_actors.include?(actor.value)
end
end

def wrap(actor)
Types::Actor.wrap(actor)
end
Expand Down
14 changes: 14 additions & 0 deletions lib/flipper/gates/group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ module Gates
class Group < Gate
# Internal: The name of the gate. Used for instrumentation, etc.
def name
return :blocking_group if blocking

:group
end

# Internal: Name converted to value safe for adapter.
def key
return :blocking_groups if blocking

:groups
end

Expand All @@ -32,6 +36,16 @@ def open?(context)
end
end

def blocks?(context)
return false unless context.actors?

context.values.blocking_groups.any? do |name|
context.actors.any? do |actor|
Flipper.group(name).match?(actor, context)
end
end
end

def wrap(thing)
Types::Group.wrap(thing)
end
Expand Down
Loading