Module: Flagstack

Defined in:
lib/flagstack.rb,
lib/flagstack/client.rb,
lib/flagstack/poller.rb,
lib/flagstack/railtie.rb,
lib/flagstack/version.rb,
lib/flagstack/telemetry.rb,
lib/flagstack/synchronizer.rb,
lib/flagstack/configuration.rb,
lib/flagstack/telemetry/metric.rb,
lib/flagstack/telemetry/submitter.rb,
lib/flagstack/telemetry/metric_storage.rb,
lib/generators/flagstack/install_generator.rb

Defined Under Namespace

Modules: Generators Classes: APIError, Actor, Client, Configuration, ConfigurationError, Error, Instance, Poller, Railtie, Synchronizer, Telemetry, TelemetryInstrumenter

Constant Summary collapse

VERSION =
"0.1.1"

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.configurationObject (readonly)

Returns the value of attribute configuration.



14
15
16
# File 'lib/flagstack.rb', line 14

def configuration
  @configuration
end

Class Method Details

.[](feature) ⇒ Object

Access a feature by name (returns a Feature object)

Flagstack[:new_feature].enabled?
Flagstack[:new_feature].enable


209
210
211
# File 'lib/flagstack.rb', line 209

def [](feature)
  @instance&.flipper&.[](feature)
end

.configure(options = {}) {|@configuration| ... } ⇒ Object

Configure the default Flagstack instance Returns a Flipper instance that reads from local adapter synced with Flagstack

Flagstack.configure do |config|
  config.token = ENV["FLAGSTACK_TOKEN"]
end

# Now use Flipper as normal
Flipper.enabled?(:feature)

Yields:



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/flagstack.rb', line 40

def configure(options = {})
  @configuration = Configuration.new(options)
  yield @configuration if block_given?
  @configuration.validate!

  @instance = Instance.new(@configuration)

  # Set as default Flipper instance so Flipper.enabled? uses our adapter
  Flipper.configure do |config|
    config.default { @instance.flipper }
  end

  # Initial sync
  @instance.sync

  # Start background polling
  @instance.start_poller if @configuration.sync_method == :poll

  # Start telemetry
  @instance.start_telemetry

  @instance.flipper
end

.disable(feature) ⇒ Object

Disable a feature globally

Flagstack.disable(:new_feature)


136
137
138
# File 'lib/flagstack.rb', line 136

def disable(feature)
  @instance&.flipper&.[](feature)&.disable
end

.disable_actor(feature, actor) ⇒ Object

Disable a feature for a specific actor

Flagstack.disable_actor(:new_feature, current_user)


152
153
154
# File 'lib/flagstack.rb', line 152

def disable_actor(feature, actor)
  @instance&.flipper&.[](feature)&.disable_actor(actor)
end

.disable_group(feature, group) ⇒ Object

Disable a feature for a group

Flagstack.disable_group(:new_feature, :admins)


168
169
170
# File 'lib/flagstack.rb', line 168

def disable_group(feature, group)
  @instance&.flipper&.[](feature)&.disable_group(group)
end

.disable_percentage_of_actors(feature) ⇒ Object

Disable percentage of actors gate

Flagstack.disable_percentage_of_actors(:new_feature)


184
185
186
# File 'lib/flagstack.rb', line 184

def disable_percentage_of_actors(feature)
  @instance&.flipper&.[](feature)&.disable_percentage_of_actors
end

.disable_percentage_of_time(feature) ⇒ Object

Disable percentage of time gate

Flagstack.disable_percentage_of_time(:new_feature)


200
201
202
# File 'lib/flagstack.rb', line 200

def disable_percentage_of_time(feature)
  @instance&.flipper&.[](feature)&.disable_percentage_of_time
end

.disabled?(feature, actor = nil) ⇒ Boolean

Check if a feature is disabled

Flagstack.disabled?(:new_feature)
Flagstack.disabled?(:new_feature, current_user)

Returns:

  • (Boolean)


120
121
122
# File 'lib/flagstack.rb', line 120

def disabled?(feature, actor = nil)
  !enabled?(feature, actor)
end

.enable(feature) ⇒ Object

Enable a feature globally

Flagstack.enable(:new_feature)


128
129
130
# File 'lib/flagstack.rb', line 128

def enable(feature)
  @instance&.flipper&.[](feature)&.enable
end

.enable_actor(feature, actor) ⇒ Object

Enable a feature for a specific actor

Flagstack.enable_actor(:new_feature, current_user)


144
145
146
# File 'lib/flagstack.rb', line 144

def enable_actor(feature, actor)
  @instance&.flipper&.[](feature)&.enable_actor(actor)
end

.enable_group(feature, group) ⇒ Object

Enable a feature for a group

Flagstack.enable_group(:new_feature, :admins)


160
161
162
# File 'lib/flagstack.rb', line 160

def enable_group(feature, group)
  @instance&.flipper&.[](feature)&.enable_group(group)
end

.enable_percentage_of_actors(feature, percentage) ⇒ Object

Enable a feature for a percentage of actors

Flagstack.enable_percentage_of_actors(:new_feature, 25)


176
177
178
# File 'lib/flagstack.rb', line 176

def enable_percentage_of_actors(feature, percentage)
  @instance&.flipper&.[](feature)&.enable_percentage_of_actors(percentage)
end

.enable_percentage_of_time(feature, percentage) ⇒ Object

Enable a feature for a percentage of time

Flagstack.enable_percentage_of_time(:new_feature, 50)


192
193
194
# File 'lib/flagstack.rb', line 192

def enable_percentage_of_time(feature, percentage)
  @instance&.flipper&.[](feature)&.enable_percentage_of_time(percentage)
end

.enabled?(feature, actor = nil) ⇒ Boolean

Check if a feature is enabled

Flagstack.enabled?(:new_feature)
Flagstack.enabled?(:new_feature, current_user)

Returns:

  • (Boolean)


105
106
107
108
109
110
111
112
113
# File 'lib/flagstack.rb', line 105

def enabled?(feature, actor = nil)
  return false unless @instance&.flipper

  if actor
    @instance.flipper.enabled?(feature, actor)
  else
    @instance.flipper.enabled?(feature)
  end
end

.featuresObject

List all features

Flagstack.features


217
218
219
# File 'lib/flagstack.rb', line 217

def features
  @instance&.flipper&.features || []
end

.flipperObject

Get the Flipper instance



65
66
67
# File 'lib/flagstack.rb', line 65

def flipper
  @instance&.flipper
end

.health_checkObject

Check connectivity to Flagstack server Returns { ok: boolean, message: string }



90
91
92
# File 'lib/flagstack.rb', line 90

def health_check
  @instance&.client&.health_check || { ok: false, message: "Not configured" }
end

.new(options = {}) {|config| ... } ⇒ Object

Create a new Flagstack-backed Flipper instance

flipper = Flagstack.new(token: "fs_live_xxx")
flipper.enabled?(:feature)
flipper.enabled?(:feature, user)

Yields:

  • (config)


22
23
24
25
26
27
28
# File 'lib/flagstack.rb', line 22

def new(options = {})
  config = Configuration.new(options)
  yield config if block_given?
  config.validate!

  Instance.new(config).flipper
end

.register(group, &block) ⇒ Object

Register a group for use with enable_group

Flagstack.register(:admins) { |actor| actor.admin? }


225
226
227
# File 'lib/flagstack.rb', line 225

def register(group, &block)
  Flipper.register(group, &block)
end

.reset!Object

Reset everything (useful for testing)



75
76
77
78
79
80
# File 'lib/flagstack.rb', line 75

def reset!
  @instance&.stop_poller
  @instance&.stop_telemetry
  @instance = nil
  @configuration = nil
end

.set_defaultObject

Auto-configure when FLAGSTACK_TOKEN is present



230
231
232
233
234
235
236
237
238
# File 'lib/flagstack.rb', line 230

def set_default
  return unless ENV["FLAGSTACK_TOKEN"]
  return if @configuration # Already configured

  configure
rescue => e
  # Don't fail app boot if Flagstack is misconfigured
  warn "[Flagstack] Auto-configuration failed: #{e.message}"
end

.shutdownObject

Shutdown telemetry and polling gracefully



83
84
85
86
# File 'lib/flagstack.rb', line 83

def shutdown
  @instance&.stop_telemetry
  @instance&.stop_poller
end

.syncObject

Force sync from Flagstack to local adapter



70
71
72
# File 'lib/flagstack.rb', line 70

def sync
  @instance&.sync
end