Class: Flipper::Feature

Inherits:
Object
  • Object
show all
Defined in:
lib/flipper/feature.rb

Constant Summary collapse

InstrumentationName =

Private: The name of feature instrumentation events.

"feature_operation.#{InstrumentationNamespace}".freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, adapter, options = {}) ⇒ Feature

Internal: Initializes a new feature instance.

name - The Symbol or String name of the feature. adapter - The adapter that will be used to store details about this feature.

options - The Hash of options.

:instrumenter - What to use to instrument all the things.


32
33
34
35
36
37
# File 'lib/flipper/feature.rb', line 32

def initialize(name, adapter, options = {})
  @name = name
  @key = name.to_s
  @instrumenter = options.fetch(:instrumenter, Instrumenters::Noop)
  @adapter = adapter
end

Instance Attribute Details

#adapterObject (readonly)

Private: The adapter this feature should use.



19
20
21
# File 'lib/flipper/feature.rb', line 19

def adapter
  @adapter
end

#instrumenterObject (readonly)

Private: What is being used to instrument all the things.



22
23
24
# File 'lib/flipper/feature.rb', line 22

def instrumenter
  @instrumenter
end

#keyObject (readonly)

Public: Name converted to value safe for adapter.



16
17
18
# File 'lib/flipper/feature.rb', line 16

def key
  @key
end

#nameObject (readonly)

Public: The name of the feature.



13
14
15
# File 'lib/flipper/feature.rb', line 13

def name
  @name
end

Instance Method Details

#actors_valueObject

Public: Get the adapter value for the actors gate.

Returns Set of String flipper_id’s.



326
327
328
# File 'lib/flipper/feature.rb', line 326

def actors_value
  gate_values.actors
end

#addObject

Public: Adds this feature.

Returns the result of Adapter#add.



74
75
76
# File 'lib/flipper/feature.rb', line 74

def add
  instrument(:add) { adapter.add(self) }
end

#add_expression(expression_to_add) ⇒ Object

Public: Add an expression for a feature.

expression_to_add - an expression or Hash that can be converted to an expression.

Returns result of enable.



144
145
146
147
148
149
150
# File 'lib/flipper/feature.rb', line 144

def add_expression(expression_to_add)
  if (current_expression = expression)
    enable current_expression.add(expression_to_add)
  else
    enable expression_to_add
  end
end

#boolean_valueObject

Public: Get the adapter value for the boolean gate.

Returns true or false.



333
334
335
# File 'lib/flipper/feature.rb', line 333

def boolean_value
  gate_values.boolean
end

#clearObject

Public: Clears all gate values for this feature.

Returns the result of Adapter#clear.



95
96
97
# File 'lib/flipper/feature.rb', line 95

def clear
  instrument(:clear) { adapter.clear(self) }
end

#conditional?Boolean

Public: Is the feature conditionally enabled for a given actor, group, percentage of actors or percentage of the time.



280
281
282
# File 'lib/flipper/feature.rb', line 280

def conditional?
  state == :conditional
end

#disable(thing = false) ⇒ Object

Public: Disable this feature for something.

Returns the result of Adapter#disable.



58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/flipper/feature.rb', line 58

def disable(thing = false)
  instrument(:disable) do |payload|
    adapter.add self

    gate = gate_for(thing)
    wrapped_thing = gate.wrap(thing)
    payload[:gate_name] = gate.name
    payload[:thing] = wrapped_thing

    adapter.disable self, gate, wrapped_thing
  end
end

#disable_actor(actor) ⇒ Object

Public: Disables a feature for an actor.

actor - a Flipper::Types::Actor instance or an object that responds

to flipper_id.

Returns result of disable.



219
220
221
# File 'lib/flipper/feature.rb', line 219

def disable_actor(actor)
  disable Types::Actor.wrap(actor)
end

#disable_expressionObject

Public: Disables an expression for a feature.

expression - an expression or Hash that can be converted to an expression.

Returns result of disable.



197
198
199
# File 'lib/flipper/feature.rb', line 197

def disable_expression
  disable Flipper.all # just need an expression to clear
end

#disable_group(group) ⇒ Object

Public: Disables a feature for a group.

group - a Flipper::Types::Group instance or a String or Symbol name of a

registered group.

Returns result of disable.



229
230
231
# File 'lib/flipper/feature.rb', line 229

def disable_group(group)
  disable Types::Group.wrap(group)
end

#disable_percentage_of_actorsObject

Public: Disables a feature for a percentage of actors.

percentage - a Flipper::Types::PercentageOfTime instance or an object that

responds to to_i.

Returns result of disable.



249
250
251
# File 'lib/flipper/feature.rb', line 249

def disable_percentage_of_actors
  disable Types::PercentageOfActors.new(0)
end

#disable_percentage_of_timeObject

Public: Disables a feature a percentage of time.

percentage - a Flipper::Types::PercentageOfTime instance or an object that

responds to to_i.

Returns result of disable.



239
240
241
# File 'lib/flipper/feature.rb', line 239

def disable_percentage_of_time
  disable Types::PercentageOfTime.new(0)
end

#disabled_gate_namesObject

Public: Get the names of the disabled gates.

Returns an Array of gate names.



376
377
378
# File 'lib/flipper/feature.rb', line 376

def disabled_gate_names
  disabled_gates.map(&:name)
end

#disabled_gatesObject

Public: Get the gates that have not been enabled for the feature.

Returns an Array of Flipper::Gate instances.



369
370
371
# File 'lib/flipper/feature.rb', line 369

def disabled_gates
  gates - enabled_gates
end

#disabled_groupsObject

Public: Get groups not enabled for this feature.

Returns Set of Flipper::Types::Group instances.



301
302
303
# File 'lib/flipper/feature.rb', line 301

def disabled_groups
  Flipper.groups - enabled_groups
end

#enable(thing = true) ⇒ Object

Public: Enable this feature for something.

Returns the result of Adapter#enable.



42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/flipper/feature.rb', line 42

def enable(thing = true)
  instrument(:enable) do |payload|
    adapter.add self

    gate = gate_for(thing)
    wrapped_thing = gate.wrap(thing)
    payload[:gate_name] = gate.name
    payload[:thing] = wrapped_thing

    adapter.enable self, gate, wrapped_thing
  end
end

#enable_actor(actor) ⇒ Object

Public: Enables a feature for an actor.

actor - a Flipper::Types::Actor instance or an object that responds

to flipper_id.

Returns result of enable.



158
159
160
# File 'lib/flipper/feature.rb', line 158

def enable_actor(actor)
  enable Types::Actor.wrap(actor)
end

#enable_expression(expression) ⇒ Object

Public: Enables an expression_to_add for a feature.

expression - an Expression or Hash that can be converted to an expression.

Returns result of enable.



135
136
137
# File 'lib/flipper/feature.rb', line 135

def enable_expression(expression)
  enable Expression.build(expression)
end

#enable_group(group) ⇒ Object

Public: Enables a feature for a group.

group - a Flipper::Types::Group instance or a String or Symbol name of a

registered group.

Returns result of enable.



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

def enable_group(group)
  enable Types::Group.wrap(group)
end

#enable_percentage_of_actors(percentage) ⇒ Object

Public: Enables a feature for a percentage of actors.

percentage - a Flipper::Types::PercentageOfTime instance or an object that

responds to to_i.

Returns result of enable.



188
189
190
# File 'lib/flipper/feature.rb', line 188

def enable_percentage_of_actors(percentage)
  enable Types::PercentageOfActors.wrap(percentage)
end

#enable_percentage_of_time(percentage) ⇒ Object

Public: Enables a feature a percentage of time.

percentage - a Flipper::Types::PercentageOfTime instance or an object that

responds to to_i.

Returns result of enable.



178
179
180
# File 'lib/flipper/feature.rb', line 178

def enable_percentage_of_time(percentage)
  enable Types::PercentageOfTime.wrap(percentage)
end

#enabled?(*actors) ⇒ Boolean

Public: Check if a feature is enabled for zero or more actors.

Returns true if enabled, false if not.



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/flipper/feature.rb', line 102

def enabled?(*actors)
  actors = Array(actors).
    # Avoids to_ary warning that happens when passing DelegateClass of an 
    # ActiveRecord object and using flatten here. This is tested in 
    # spec/flipper/model/active_record_spec.rb.
    flat_map { |actor| actor.is_a?(Array) ? actor : [actor] }. 
    # Allows null object pattern. See PR for more. https://github.com/flippercloud/flipper/pull/887
    reject(&:nil?). 
    map { |actor| Types::Actor.wrap(actor) }
  actors = nil if actors.empty?

  # thing is left for backwards compatibility
  instrument(:enabled?, thing: actors&.first, actors: actors) do |payload|
    context = FeatureCheckContext.new(
      feature_name: @name,
      values: gate_values,
      actors: actors
    )

    if open_gate = gates.detect { |gate| gate.open?(context) }
      payload[:gate_name] = open_gate.name
      true
    else
      false
    end
  end
end

#enabled_gate_namesObject

Public: Get the names of the enabled gates.

Returns an Array of gate names.



362
363
364
# File 'lib/flipper/feature.rb', line 362

def enabled_gate_names
  enabled_gates.map(&:name)
end

#enabled_gatesObject

Public: Get the gates that have been enabled for the feature.

Returns an Array of Flipper::Gate instances.



354
355
356
357
# File 'lib/flipper/feature.rb', line 354

def enabled_gates
  values = gate_values
  gates.select { |gate| gate.enabled?(values.send(gate.key)) }
end

#enabled_groupsObject Also known as: groups

Public: Get groups enabled for this feature.

Returns Set of Flipper::Types::Group instances.



293
294
295
# File 'lib/flipper/feature.rb', line 293

def enabled_groups
  groups_value.map { |name| Flipper.group(name) }.to_set
end

#exist?Boolean

Public: Does this feature exist in the adapter.

Returns true if exists in adapter else false.



81
82
83
# File 'lib/flipper/feature.rb', line 81

def exist?
  instrument(:exist?) { adapter.features.include?(key) }
end

#expressionObject



305
306
307
# File 'lib/flipper/feature.rb', line 305

def expression
  Flipper::Expression.build(expression_value) if expression_value
end

#expression_valueObject

Public: Get the adapter value for the expression gate.

Returns expression.



319
320
321
# File 'lib/flipper/feature.rb', line 319

def expression_value
  gate_values.expression
end

#gate(name) ⇒ Object

Public: Find a gate by name.

Returns a Flipper::Gate if found, nil if not.



422
423
424
# File 'lib/flipper/feature.rb', line 422

def gate(name)
  gates_hash[name.to_sym]
end

#gate_for(actor) ⇒ Object

Public: Find the gate that protects an actor.

actor - The object for which you would like to find a gate

Returns a Flipper::Gate. Raises Flipper::GateNotFound if no gate found for actor



432
433
434
# File 'lib/flipper/feature.rb', line 432

def gate_for(actor)
  gates.detect { |gate| gate.protects?(actor) } || raise(GateNotFound, actor)
end

#gate_valuesObject

Public: Returns the raw gate values stored by the adapter.



285
286
287
288
# File 'lib/flipper/feature.rb', line 285

def gate_values
  adapter_values = adapter.get(self)
  GateValues.new(adapter_values)
end

#gatesObject

Public: Get all the gates used to determine enabled/disabled for the feature.

Returns an array of gates



404
405
406
# File 'lib/flipper/feature.rb', line 404

def gates
  @gates ||= gates_hash.values.freeze
end

#gates_hashObject



408
409
410
411
412
413
414
415
416
417
# File 'lib/flipper/feature.rb', line 408

def gates_hash
  @gates_hash ||= {
    boolean: Gates::Boolean.new,
    expression: Gates::Expression.new,
    actor: Gates::Actor.new,
    percentage_of_actors: Gates::PercentageOfActors.new,
    percentage_of_time: Gates::PercentageOfTime.new,
    group: Gates::Group.new,
  }.freeze
end

#groups_valueObject

Public: Get the adapter value for the groups gate.

Returns Set of String group names.



312
313
314
# File 'lib/flipper/feature.rb', line 312

def groups_value
  gate_values.groups
end

#inspectObject

Public: Pretty string version for debugging.



391
392
393
394
395
396
397
398
399
# File 'lib/flipper/feature.rb', line 391

def inspect
  attributes = [
    "name=#{name.inspect}",
    "state=#{state.inspect}",
    "enabled_gate_names=#{enabled_gate_names.inspect}",
    "adapter=#{adapter.name.inspect}",
  ]
  "#<#{self.class.name}:#{object_id} #{attributes.join(', ')}>"
end

#off?Boolean

Public: Is the feature fully disabled.



274
275
276
# File 'lib/flipper/feature.rb', line 274

def off?
  state == :off
end

#on?Boolean

Public: Is the feature fully enabled.



269
270
271
# File 'lib/flipper/feature.rb', line 269

def on?
  state == :on
end

#percentage_of_actors_valueObject

Public: Get the adapter value for the percentage of actors gate.

Returns Integer greater than or equal to 0 and less than or equal to 100.



340
341
342
# File 'lib/flipper/feature.rb', line 340

def percentage_of_actors_value
  gate_values.percentage_of_actors
end

#percentage_of_time_valueObject

Public: Get the adapter value for the percentage of time gate.

Returns Integer greater than or equal to 0 and less than or equal to 100.



347
348
349
# File 'lib/flipper/feature.rb', line 347

def percentage_of_time_value
  gate_values.percentage_of_time
end

#removeObject

Public: Removes this feature.

Returns the result of Adapter#remove.



88
89
90
# File 'lib/flipper/feature.rb', line 88

def remove
  instrument(:remove) { adapter.remove(self) }
end

#remove_expression(expression_to_remove) ⇒ Object

Public: Remove an expression from a feature. Does nothing if no expression is currently enabled.

expression - an Expression or Hash that can be converted to an expression.

Returns result of enable or nil (if no expression enabled).



207
208
209
210
211
# File 'lib/flipper/feature.rb', line 207

def remove_expression(expression_to_remove)
  if (current_expression = expression)
    enable current_expression.remove(expression_to_remove)
  end
end

#stateObject

Public: Returns state for feature (:on, :off, or :conditional).



254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/flipper/feature.rb', line 254

def state
  values = gate_values
  boolean = gate(:boolean)
  non_boolean_gates = gates - [boolean]

  if values.boolean || values.percentage_of_time == 100
    :on
  elsif non_boolean_gates.detect { |gate| gate.enabled?(values.send(gate.key)) }
    :conditional
  else
    :off
  end
end

#to_paramObject

Public: Identifier to be used in the url (a rails-ism).



386
387
388
# File 'lib/flipper/feature.rb', line 386

def to_param
  to_s
end

#to_sObject

Public: Returns the string representation of the feature.



381
382
383
# File 'lib/flipper/feature.rb', line 381

def to_s
  name.to_s
end