Class: Flipper::Feature

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

Constant Summary collapse

InstrumentationName =

Private: The name of instrumentation events.

"feature_operation.#{InstrumentationNamespace}"

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.


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

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

Instance Attribute Details

#adapterObject (readonly)

Private: The adapter this feature should use.



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

def adapter
  @adapter
end

#instrumenterObject (readonly)

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



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

def instrumenter
  @instrumenter
end

#keyObject (readonly)

Internal: Name converted to value safe for adapter.



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

def key
  @key
end

#nameObject (readonly)

Public: The name of the feature.



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

def name
  @name
end

Instance Method Details

#boolean_gateObject

Private



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

def boolean_gate
  @boolean_gate ||= gate(:boolean)
end

#conditional_gates(gate_values) ⇒ Object

Private



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

def conditional_gates(gate_values)
  @conditional_gates ||= non_boolean_gates.select { |gate|
    value = gate_values[gate.key]
    gate.enabled?(value)
  }
end

#descriptionObject

Public



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/flipper/feature.rb', line 105

def description
  gate_values = adapter.get(self)
  boolean_value = gate_values[:boolean]
  conditional_gates = conditional_gates(gate_values)

  if boolean_gate.enabled?(boolean_value)
    boolean_gate.description(boolean_value).capitalize
  elsif conditional_gates.any?
    fragments = conditional_gates.map { |gate|
      value = gate_values[gate.key]
      gate.description(value)
    }

    "Enabled for #{fragments.join(', ')}"
  else
    boolean_gate.description(boolean_value).capitalize
  end
end

#disable(thing = Types::Boolean.new(false)) ⇒ Object

Public: Disable this feature for something.

Returns the result of Flipper::Gate#disable.



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

def disable(thing = Types::Boolean.new(false))
  instrument(:disable, thing) { |payload|
    adapter.add self

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

    if gate.is_a?(Gates::Boolean)
      adapter.clear self
    else
      adapter.disable self, gate, gate.wrap(thing)
    end
  }
end

#enable(thing = Types::Boolean.new(true)) ⇒ Object

Public: Enable this feature for something.

Returns the result of Flipper::Gate#enable.



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

def enable(thing = Types::Boolean.new(true))
  instrument(:enable, thing) { |payload|
    adapter.add self

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

    adapter.enable self, gate, gate.wrap(thing)
  }
end

#enabled?(thing = nil) ⇒ Boolean

Public: Check if a feature is enabled for a thing.

Returns true if enabled, false if not.

Returns:

  • (Boolean)


73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/flipper/feature.rb', line 73

def enabled?(thing = nil)
  instrument(:enabled?, thing) { |payload|
    gate_values = adapter.get(self)

    gate = gates.detect { |gate|
      gate.open?(thing, gate_values[gate.key])
    }

    if gate.nil?
      false
    else
      payload[:gate_name] = gate.name
      true
    end
  }
end

#gate(name) ⇒ Object

Internal: Finds a gate by name.

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



151
152
153
# File 'lib/flipper/feature.rb', line 151

def gate(name)
  gates.detect { |gate| gate.name == name.to_sym }
end

#gate_for(thing) ⇒ Object

Internal: Find the gate that protects a thing.

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

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



161
162
163
164
# File 'lib/flipper/feature.rb', line 161

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

#gatesObject

Internal: Gates to check to see if feature is enabled/disabled

Returns an array of gates



138
139
140
141
142
143
144
145
146
# File 'lib/flipper/feature.rb', line 138

def gates
  @gates ||= [
    Gates::Boolean.new(@name, :instrumenter => @instrumenter),
    Gates::Group.new(@name, :instrumenter => @instrumenter),
    Gates::Actor.new(@name, :instrumenter => @instrumenter),
    Gates::PercentageOfActors.new(@name, :instrumenter => @instrumenter),
    Gates::PercentageOfRandom.new(@name, :instrumenter => @instrumenter),
  ]
end

#inspectObject

Public: Pretty string version for debugging.



125
126
127
128
129
130
131
132
133
# File 'lib/flipper/feature.rb', line 125

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

#instrument(operation, thing) ⇒ Object

Private



185
186
187
188
189
190
191
192
193
194
195
# File 'lib/flipper/feature.rb', line 185

def instrument(operation, thing)
  payload = {
    :feature_name => name,
    :operation => operation,
    :thing => thing,
  }

  @instrumenter.instrument(InstrumentationName, payload) {
    payload[:result] = yield(payload) if block_given?
  }
end

#non_boolean_gatesObject

Private



172
173
174
# File 'lib/flipper/feature.rb', line 172

def non_boolean_gates
  @non_boolean_gates ||= gates - [boolean_gate]
end

#stateObject

Public



91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/flipper/feature.rb', line 91

def state
  gate_values = adapter.get(self)
  boolean_value = gate_values[:boolean]

  if boolean_gate.enabled?(boolean_value)
    :on
  elsif conditional_gates(gate_values).any?
    :conditional
  else
    :off
  end
end