Module: TalkToYourApp::Plugins::Flipper
- Defined in:
- lib/talk_to_your_app/plugins/flipper/plugin.rb,
lib/talk_to_your_app/plugins/flipper/tools/read_flag.rb,
lib/talk_to_your_app/plugins/flipper/tools/list_flags.rb,
lib/talk_to_your_app/plugins/flipper/tools/enable_flag.rb,
lib/talk_to_your_app/plugins/flipper/tools/disable_flag.rb,
lib/talk_to_your_app/plugins/flipper/tools/enabled_flags.rb
Overview
The Flipper plugin: list flags, read a flag's state (globally or for an actor), and enable/disable flags globally or per actor. All operations run through the connection the operator wires in (config.plugin :flipper, connection: :your_writer); boot fails unless that connection is declared role: :writing, since Flipper writes flag state.
Defined Under Namespace
Modules: Tools Classes: Actor, Plugin
Class Method Summary collapse
-
.active?(gates) ⇒ Boolean
A flag is "enabled" if any gate is active.
-
.actor_for(actor_class, actor_id) ⇒ Object
Builds an actor from a class name + id, or nil when none was supplied.
-
.apply(operation, name, gate) ⇒ Object
Applies :enable or :disable across the resolved gate.
-
.gate_conflict?(args) ⇒ Boolean
True when a call names more than one gate dimension (actor, group, percentage).
-
.gate_from(args) ⇒ Object
Resolves which Flipper gate a tool call targets from its arguments.
-
.gate_values(name) ⇒ Object
The full per-gate configuration of a flag, for read_flag.
- .invalid_gate_selector_message(args) ⇒ Object
-
.preload_timestamps(names) ⇒ Object
Last-change timestamps for many flags in a single query, keyed by flag name, read from the flipper_features table when the ActiveRecord adapter is in use.
-
.state(name, actor) ⇒ Object
Reads a flag's effective state, globally or for an actor.
Class Method Details
.active?(gates) ⇒ Boolean
A flag is "enabled" if any gate is active.
110 111 112 113 114 115 116 |
# File 'lib/talk_to_your_app/plugins/flipper/plugin.rb', line 110 def active?(gates) gates[:boolean] || gates[:actors].any? || gates[:groups].any? || gates[:percentage_of_actors].to_i.positive? || gates[:percentage_of_time].to_i.positive? end |
.actor_for(actor_class, actor_id) ⇒ Object
Builds an actor from a class name + id, or nil when none was supplied.
27 28 29 30 31 |
# File 'lib/talk_to_your_app/plugins/flipper/plugin.rb', line 27 def actor_for(actor_class, actor_id) return nil if actor_class.nil? || actor_id.nil? Actor.new("#{actor_class};#{actor_id}") end |
.apply(operation, name, gate) ⇒ Object
Applies :enable or :disable across the resolved gate. Every branch is an explicit named call so the dispatch is statically obvious and an unknown gate type fails fast rather than silently toggling the boolean gate.
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/talk_to_your_app/plugins/flipper/plugin.rb', line 73 def apply(operation, name, gate) enabling = operation == :enable case gate[:type] when :boolean enabling ? ::Flipper.enable(name) : ::Flipper.disable(name) when :actor enabling ? ::Flipper.enable(name, gate[:actor]) : ::Flipper.disable(name, gate[:actor]) when :group enabling ? ::Flipper.enable_group(name, gate[:group]) : ::Flipper.disable_group(name, gate[:group]) when :percentage_of_actors enabling ? ::Flipper.enable_percentage_of_actors(name, gate[:percentage]) : ::Flipper.disable_percentage_of_actors(name) when :percentage_of_time enabling ? ::Flipper.enable_percentage_of_time(name, gate[:percentage]) : ::Flipper.disable_percentage_of_time(name) else raise ArgumentError, "unknown Flipper gate type: #{gate[:type].inspect}" end end |
.gate_conflict?(args) ⇒ Boolean
True when a call names more than one gate dimension (actor, group, percentage). gate_from would silently pick one by precedence and drop the rest, so the tools reject the call instead.
36 37 38 39 40 41 42 |
# File 'lib/talk_to_your_app/plugins/flipper/plugin.rb', line 36 def gate_conflict?(args) selectors = [] selectors << :actor if args[:actor_class] && args[:actor_id] selectors << :group if args[:group] selectors << :percentage unless args[:percentage].nil? selectors.size > 1 end |
.gate_from(args) ⇒ Object
Resolves which Flipper gate a tool call targets from its arguments. In precedence order: a specific actor, a named group, a percentage, else the global boolean gate.
57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/talk_to_your_app/plugins/flipper/plugin.rb', line 57 def gate_from(args) if (actor = actor_for(args[:actor_class], args[:actor_id])) { type: :actor, actor: actor } elsif args[:group] { type: :group, group: args[:group].to_sym } elsif !args[:percentage].nil? type = args[:percentage_type] == "time" ? :percentage_of_time : :percentage_of_actors { type: type, percentage: args[:percentage].to_i } else { type: :boolean } end end |
.gate_values(name) ⇒ Object
The full per-gate configuration of a flag, for read_flag.
98 99 100 101 102 103 104 105 106 107 |
# File 'lib/talk_to_your_app/plugins/flipper/plugin.rb', line 98 def gate_values(name) values = ::Flipper[name].gate_values { boolean: values.boolean, actors: values.actors.to_a, groups: values.groups.to_a, percentage_of_actors: values.percentage_of_actors, percentage_of_time: values.percentage_of_time, } end |
.invalid_gate_selector_message(args) ⇒ Object
44 45 46 47 48 49 50 51 52 |
# File 'lib/talk_to_your_app/plugins/flipper/plugin.rb', line 44 def (args) if args.key?(:actor_class) != args.key?(:actor_id) "Specify both actor_class and actor_id when targeting an actor." elsif args.key?(:percentage_type) && args[:percentage].nil? "Specify percentage when using percentage_type." elsif gate_conflict?(args) "Specify exactly one gate: an actor, a group, or a percentage." end end |
.preload_timestamps(names) ⇒ Object
Last-change timestamps for many flags in a single query, keyed by flag name, read from the flipper_features table when the ActiveRecord adapter is in use. Flipper records no enable/disable history, so updated_at is the last time the feature row changed. Returns an empty hash (callers fall back to nil timestamps) for non-ActiveRecord adapters. Batched to avoid an N+1 across all flags.
124 125 126 127 128 129 130 131 132 |
# File 'lib/talk_to_your_app/plugins/flipper/plugin.rb', line 124 def (names) return {} unless defined?(::Flipper::Adapters::ActiveRecord::Feature) ::Flipper::Adapters::ActiveRecord::Feature.where(key: names.map(&:to_s)).each_with_object({}) do |row, acc| acc[row.key] = { created_at: row.created_at&.utc&.iso8601, updated_at: row.updated_at&.utc&.iso8601 } end rescue StandardError {} end |
.state(name, actor) ⇒ Object
Reads a flag's effective state, globally or for an actor. Unknown flags read false.
93 94 95 |
# File 'lib/talk_to_your_app/plugins/flipper/plugin.rb', line 93 def state(name, actor) actor ? ::Flipper.enabled?(name, actor) : ::Flipper.enabled?(name) end |