flipper 0.20.2 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +9 -1
- data/Changelog.md +59 -0
- data/Gemfile +1 -0
- data/README.md +103 -47
- data/docs/Adapters.md +9 -9
- data/docs/Caveats.md +2 -2
- data/docs/Gates.md +74 -74
- data/docs/Optimization.md +70 -47
- data/docs/http/README.md +12 -11
- data/docs/images/banner.jpg +0 -0
- data/docs/read-only/README.md +8 -5
- data/examples/basic.rb +1 -12
- data/examples/configuring_default.rb +2 -5
- data/examples/dsl.rb +13 -24
- data/examples/enabled_for_actor.rb +8 -15
- data/examples/group.rb +3 -6
- data/examples/group_dynamic_lookup.rb +5 -19
- data/examples/group_with_members.rb +4 -14
- data/examples/importing.rb +1 -1
- data/examples/individual_actor.rb +2 -5
- data/examples/instrumentation.rb +1 -2
- data/examples/memoizing.rb +3 -7
- data/examples/percentage_of_actors.rb +6 -16
- data/examples/percentage_of_actors_enabled_check.rb +7 -10
- data/examples/percentage_of_actors_group.rb +5 -18
- data/examples/percentage_of_time.rb +3 -6
- data/lib/flipper.rb +4 -1
- data/lib/flipper/adapters/memory.rb +20 -94
- data/lib/flipper/adapters/pstore.rb +4 -0
- data/lib/flipper/configuration.rb +33 -7
- data/lib/flipper/errors.rb +2 -3
- data/lib/flipper/identifier.rb +17 -0
- data/lib/flipper/middleware/memoizer.rb +29 -14
- data/lib/flipper/railtie.rb +38 -0
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/adapters/memory_spec.rb +21 -1
- data/spec/flipper/configuration_spec.rb +20 -2
- data/spec/flipper/identifier_spec.rb +14 -0
- data/spec/flipper/middleware/memoizer_spec.rb +95 -35
- data/spec/flipper/middleware/setup_env_spec.rb +0 -16
- data/spec/flipper/railtie_spec.rb +69 -0
- data/spec/flipper_spec.rb +0 -1
- data/spec/helper.rb +2 -2
- data/spec/support/spec_helpers.rb +20 -0
- data/test/test_helper.rb +1 -0
- metadata +9 -3
- data/examples/example_setup.rb +0 -8
data/examples/importing.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
|
-
require
|
2
|
-
|
1
|
+
require 'bundler/setup'
|
3
2
|
require 'flipper'
|
4
3
|
|
5
|
-
|
6
|
-
flipper = Flipper.new(adapter)
|
7
|
-
stats = flipper[:stats]
|
4
|
+
stats = Flipper[:stats]
|
8
5
|
|
9
6
|
# Some class that represents what will be trying to do something
|
10
7
|
class User
|
data/examples/instrumentation.rb
CHANGED
data/examples/memoizing.rb
CHANGED
@@ -1,16 +1,12 @@
|
|
1
|
-
require
|
2
|
-
|
1
|
+
require 'bundler/setup'
|
3
2
|
require 'flipper'
|
4
3
|
require 'flipper/adapters/operation_logger'
|
5
4
|
require 'flipper/instrumentation/log_subscriber'
|
6
5
|
|
7
6
|
Flipper.configure do |config|
|
8
|
-
config.
|
7
|
+
config.adapter do
|
9
8
|
# pick an adapter, this uses memory, any will do
|
10
|
-
|
11
|
-
|
12
|
-
# pass adapter to handy DSL instance
|
13
|
-
Flipper.new(adapter)
|
9
|
+
Flipper::Adapters::OperationLogger.new(Flipper::Adapters::Memory.new)
|
14
10
|
end
|
15
11
|
end
|
16
12
|
|
@@ -1,21 +1,11 @@
|
|
1
|
-
require
|
2
|
-
|
1
|
+
require 'bundler/setup'
|
3
2
|
require 'flipper'
|
4
3
|
|
5
|
-
|
6
|
-
flipper = Flipper.new(adapter)
|
7
|
-
stats = flipper[:stats]
|
4
|
+
stats = Flipper[:stats]
|
8
5
|
|
9
6
|
# Some class that represents what will be trying to do something
|
10
|
-
class User
|
11
|
-
|
12
|
-
|
13
|
-
def initialize(id)
|
14
|
-
@id = id
|
15
|
-
end
|
16
|
-
|
17
|
-
# Must respond to flipper_id
|
18
|
-
alias_method :flipper_id, :id
|
7
|
+
class User < Struct.new(:id)
|
8
|
+
include Flipper::Identifier
|
19
9
|
end
|
20
10
|
|
21
11
|
total = 100_000
|
@@ -24,10 +14,10 @@ total = 100_000
|
|
24
14
|
users = (1..total).map { |n| User.new(n) }
|
25
15
|
|
26
16
|
perform_test = lambda { |number|
|
27
|
-
|
17
|
+
Flipper.enable_percentage_of_actors :stats, number
|
28
18
|
|
29
19
|
enabled = users.map { |user|
|
30
|
-
|
20
|
+
Flipper.enabled?(:stats, user) ? true : nil
|
31
21
|
}.compact
|
32
22
|
|
33
23
|
actual = (enabled.size / total.to_f * 100).round(3)
|
@@ -1,10 +1,6 @@
|
|
1
|
-
require
|
2
|
-
|
1
|
+
require 'bundler/setup'
|
3
2
|
require 'flipper'
|
4
3
|
|
5
|
-
adapter = Flipper::Adapters::Memory.new
|
6
|
-
flipper = Flipper.new(adapter)
|
7
|
-
|
8
4
|
# Some class that represents what will be trying to do something
|
9
5
|
class User
|
10
6
|
attr_reader :id
|
@@ -18,15 +14,16 @@ class User
|
|
18
14
|
end
|
19
15
|
|
20
16
|
# checking a bunch
|
21
|
-
gate = Flipper::Gates::PercentageOfActors.new
|
22
|
-
feature_name = "data_migration"
|
23
|
-
percentage_enabled = 10
|
24
17
|
total = 20_000
|
25
18
|
enabled = []
|
19
|
+
percentage_enabled = 10
|
20
|
+
|
21
|
+
feature = Flipper[:data_migration]
|
22
|
+
feature.enable_percentage_of_actors 10
|
26
23
|
|
27
24
|
(1..total).each do |id|
|
28
25
|
user = User.new(id)
|
29
|
-
if
|
26
|
+
if feature.enabled? user
|
30
27
|
enabled << user
|
31
28
|
end
|
32
29
|
end
|
@@ -35,4 +32,4 @@ p actual: enabled.size, expected: total * (percentage_enabled * 0.01)
|
|
35
32
|
|
36
33
|
# checking one
|
37
34
|
user = User.new(1)
|
38
|
-
p user_1_enabled:
|
35
|
+
p user_1_enabled: feature.enabled?(user)
|
@@ -3,25 +3,12 @@
|
|
3
3
|
# feature for actors in a particular location or on a particular plan, but only
|
4
4
|
# for a percentage of them. The percentage is a constant, but could easily be
|
5
5
|
# plucked from memcached, redis, mysql or whatever.
|
6
|
-
require
|
6
|
+
require 'bundler/setup'
|
7
7
|
require 'flipper'
|
8
8
|
|
9
|
-
adapter = Flipper::Adapters::Memory.new
|
10
|
-
flipper = Flipper.new(adapter)
|
11
|
-
stats = flipper[:stats]
|
12
|
-
|
13
9
|
# Some class that represents what will be trying to do something
|
14
|
-
class User
|
15
|
-
|
16
|
-
|
17
|
-
def initialize(id)
|
18
|
-
@id = id
|
19
|
-
end
|
20
|
-
|
21
|
-
# Must respond to flipper_id
|
22
|
-
def flipper_id
|
23
|
-
"User;#{@id}"
|
24
|
-
end
|
10
|
+
class User < Struct.new(:id)
|
11
|
+
include Flipper::Identifier
|
25
12
|
end
|
26
13
|
|
27
14
|
PERCENTAGE = 50
|
@@ -34,13 +21,13 @@ Flipper.register(:experimental) do |actor|
|
|
34
21
|
end
|
35
22
|
|
36
23
|
# enable the experimental group
|
37
|
-
|
24
|
+
Flipper.enable_group :stats, :experimental
|
38
25
|
|
39
26
|
# create a bunch of fake users and see how many are enabled
|
40
27
|
total = 10_000
|
41
28
|
users = (1..total).map { |n| User.new(n) }
|
42
29
|
enabled = users.map { |user|
|
43
|
-
|
30
|
+
Flipper.enabled?(:stats, user) ? true : nil
|
44
31
|
}.compact
|
45
32
|
|
46
33
|
# show the results
|
@@ -1,13 +1,10 @@
|
|
1
|
-
require
|
2
|
-
|
1
|
+
require 'bundler/setup'
|
3
2
|
require 'flipper'
|
4
3
|
|
5
|
-
|
6
|
-
flipper = Flipper.new(adapter)
|
7
|
-
logging = flipper[:logging]
|
4
|
+
logging = Flipper[:logging]
|
8
5
|
|
9
6
|
perform_test = lambda do |number|
|
10
|
-
logging.
|
7
|
+
logging.enable_percentage_of_time number
|
11
8
|
|
12
9
|
total = 100_000
|
13
10
|
enabled = []
|
data/lib/flipper.rb
CHANGED
@@ -16,7 +16,7 @@ module Flipper
|
|
16
16
|
# Public: Configure flipper.
|
17
17
|
#
|
18
18
|
# Flipper.configure do |config|
|
19
|
-
# config.
|
19
|
+
# config.adapter { ... }
|
20
20
|
# end
|
21
21
|
#
|
22
22
|
# Yields Flipper::Configuration instance.
|
@@ -152,6 +152,7 @@ require 'flipper/feature'
|
|
152
152
|
require 'flipper/gate'
|
153
153
|
require 'flipper/instrumenters/memory'
|
154
154
|
require 'flipper/instrumenters/noop'
|
155
|
+
require 'flipper/identifier'
|
155
156
|
require 'flipper/middleware/memoizer'
|
156
157
|
require 'flipper/middleware/setup_env'
|
157
158
|
require 'flipper/registry'
|
@@ -163,3 +164,5 @@ require 'flipper/types/percentage'
|
|
163
164
|
require 'flipper/types/percentage_of_actors'
|
164
165
|
require 'flipper/types/percentage_of_time'
|
165
166
|
require 'flipper/typecast'
|
167
|
+
|
168
|
+
require "flipper/railtie" if defined?(Rails::Railtie)
|
@@ -20,58 +20,57 @@ module Flipper
|
|
20
20
|
|
21
21
|
# Public: The set of known features.
|
22
22
|
def features
|
23
|
-
|
23
|
+
@source.keys.to_set
|
24
24
|
end
|
25
25
|
|
26
26
|
# Public: Adds a feature to the set of known features.
|
27
27
|
def add(feature)
|
28
|
-
|
28
|
+
@source[feature.key] ||= default_config
|
29
29
|
true
|
30
30
|
end
|
31
31
|
|
32
32
|
# Public: Removes a feature from the set of known features and clears
|
33
33
|
# all the values for the feature.
|
34
34
|
def remove(feature)
|
35
|
-
|
36
|
-
clear(feature)
|
35
|
+
@source.delete(feature.key)
|
37
36
|
true
|
38
37
|
end
|
39
38
|
|
40
39
|
# Public: Clears all the gate values for a feature.
|
41
40
|
def clear(feature)
|
42
|
-
feature.
|
43
|
-
delete key(feature, gate)
|
44
|
-
end
|
41
|
+
@source[feature.key] = default_config
|
45
42
|
true
|
46
43
|
end
|
47
44
|
|
48
45
|
# Public
|
49
46
|
def get(feature)
|
50
|
-
|
47
|
+
@source[feature.key] || default_config
|
51
48
|
end
|
52
49
|
|
53
50
|
def get_multi(features)
|
54
|
-
|
51
|
+
result = {}
|
52
|
+
features.each do |feature|
|
53
|
+
result[feature.key] = @source[feature.key] || default_config
|
54
|
+
end
|
55
|
+
result
|
55
56
|
end
|
56
57
|
|
57
58
|
def get_all
|
58
|
-
|
59
|
-
Flipper::Feature.new(key, self)
|
60
|
-
end
|
61
|
-
|
62
|
-
read_many_features(features)
|
59
|
+
@source
|
63
60
|
end
|
64
61
|
|
65
62
|
# Public
|
66
63
|
def enable(feature, gate, thing)
|
64
|
+
@source[feature.key] ||= default_config
|
65
|
+
|
67
66
|
case gate.data_type
|
68
67
|
when :boolean
|
69
68
|
clear(feature)
|
70
|
-
|
69
|
+
@source[feature.key][gate.key] = thing.value.to_s
|
71
70
|
when :integer
|
72
|
-
|
71
|
+
@source[feature.key][gate.key] = thing.value.to_s
|
73
72
|
when :set
|
74
|
-
|
73
|
+
@source[feature.key][gate.key] << thing.value.to_s
|
75
74
|
else
|
76
75
|
raise "#{gate} is not supported by this adapter yet"
|
77
76
|
end
|
@@ -81,13 +80,15 @@ module Flipper
|
|
81
80
|
|
82
81
|
# Public
|
83
82
|
def disable(feature, gate, thing)
|
83
|
+
@source[feature.key] ||= default_config
|
84
|
+
|
84
85
|
case gate.data_type
|
85
86
|
when :boolean
|
86
87
|
clear(feature)
|
87
88
|
when :integer
|
88
|
-
|
89
|
+
@source[feature.key][gate.key] = thing.value.to_s
|
89
90
|
when :set
|
90
|
-
|
91
|
+
@source[feature.key][gate.key].delete thing.value.to_s
|
91
92
|
else
|
92
93
|
raise "#{gate} is not supported by this adapter yet"
|
93
94
|
end
|
@@ -103,81 +104,6 @@ module Flipper
|
|
103
104
|
]
|
104
105
|
"#<#{self.class.name}:#{object_id} #{attributes.join(', ')}>"
|
105
106
|
end
|
106
|
-
|
107
|
-
private
|
108
|
-
|
109
|
-
def read_feature_keys
|
110
|
-
set_members(FeaturesKey)
|
111
|
-
end
|
112
|
-
|
113
|
-
# Private
|
114
|
-
def key(feature, gate)
|
115
|
-
"feature/#{feature.key}/#{gate.key}"
|
116
|
-
end
|
117
|
-
|
118
|
-
def read_many_features(features)
|
119
|
-
result = {}
|
120
|
-
features.each do |feature|
|
121
|
-
result[feature.key] = read_feature(feature)
|
122
|
-
end
|
123
|
-
result
|
124
|
-
end
|
125
|
-
|
126
|
-
def read_feature(feature)
|
127
|
-
result = {}
|
128
|
-
|
129
|
-
feature.gates.each do |gate|
|
130
|
-
result[gate.key] =
|
131
|
-
case gate.data_type
|
132
|
-
when :boolean, :integer
|
133
|
-
read key(feature, gate)
|
134
|
-
when :set
|
135
|
-
set_members key(feature, gate)
|
136
|
-
else
|
137
|
-
raise "#{gate} is not supported by this adapter yet"
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
result
|
142
|
-
end
|
143
|
-
|
144
|
-
# Private
|
145
|
-
def read(key)
|
146
|
-
@source[key.to_s]
|
147
|
-
end
|
148
|
-
|
149
|
-
# Private
|
150
|
-
def write(key, value)
|
151
|
-
@source[key.to_s] = value.to_s
|
152
|
-
end
|
153
|
-
|
154
|
-
# Private
|
155
|
-
def delete(key)
|
156
|
-
@source.delete(key.to_s)
|
157
|
-
end
|
158
|
-
|
159
|
-
# Private
|
160
|
-
def set_add(key, value)
|
161
|
-
ensure_set_initialized(key)
|
162
|
-
@source[key.to_s].add(value.to_s)
|
163
|
-
end
|
164
|
-
|
165
|
-
# Private
|
166
|
-
def set_delete(key, value)
|
167
|
-
ensure_set_initialized(key)
|
168
|
-
@source[key.to_s].delete(value.to_s)
|
169
|
-
end
|
170
|
-
|
171
|
-
# Private
|
172
|
-
def set_members(key)
|
173
|
-
ensure_set_initialized(key)
|
174
|
-
@source[key.to_s]
|
175
|
-
end
|
176
|
-
|
177
|
-
# Private
|
178
|
-
def ensure_set_initialized(key)
|
179
|
-
@source[key.to_s] ||= Set.new
|
180
|
-
end
|
181
107
|
end
|
182
108
|
end
|
183
109
|
end
|
@@ -1,7 +1,33 @@
|
|
1
1
|
module Flipper
|
2
2
|
class Configuration
|
3
|
-
def initialize
|
4
|
-
@default = -> {
|
3
|
+
def initialize(options = {})
|
4
|
+
@default = -> { Flipper.new(adapter) }
|
5
|
+
@adapter = -> { Flipper::Adapters::Memory.new }
|
6
|
+
end
|
7
|
+
|
8
|
+
# The default adapter to use.
|
9
|
+
#
|
10
|
+
# Pass a block to assign the adapter, and invoke without a block to
|
11
|
+
# return the configured adapter instance.
|
12
|
+
#
|
13
|
+
# Flipper.configure do |config|
|
14
|
+
# config.adapter # => instance of default Memory adapter
|
15
|
+
#
|
16
|
+
# # Configure it to use the ActiveRecord adapter
|
17
|
+
# config.adapter do
|
18
|
+
# require "flipper/adapters/active_record"
|
19
|
+
# Flipper::Adapters::ActiveRecord.new
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# config.adapter # => instance of ActiveRecord adapter
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
def adapter(&block)
|
26
|
+
if block_given?
|
27
|
+
@adapter = block
|
28
|
+
else
|
29
|
+
@adapter.call
|
30
|
+
end
|
5
31
|
end
|
6
32
|
|
7
33
|
# Controls the default instance for flipper. When used with a block it
|
@@ -9,15 +35,15 @@ module Flipper
|
|
9
35
|
# without a block, it performs a block invocation and returns the result.
|
10
36
|
#
|
11
37
|
# configuration = Flipper::Configuration.new
|
12
|
-
# configuration.default # =>
|
38
|
+
# configuration.default # => Flipper::DSL instance using Memory adapter
|
13
39
|
#
|
14
|
-
# # sets the default block to generate a new instance using
|
40
|
+
# # sets the default block to generate a new instance using ActiveRecord adapter
|
15
41
|
# configuration.default do
|
16
|
-
# require "flipper/adapters/
|
17
|
-
# Flipper.new(Flipper::Adapters::
|
42
|
+
# require "flipper/adapters/active_record"
|
43
|
+
# Flipper.new(Flipper::Adapters::ActiveRecord.new)
|
18
44
|
# end
|
19
45
|
#
|
20
|
-
# configuration.default # => Flipper::DSL instance using
|
46
|
+
# configuration.default # => Flipper::DSL instance using ActiveRecord adapter
|
21
47
|
#
|
22
48
|
# Returns result of default block invocation if called without block. If
|
23
49
|
# called with block, assigns the default block.
|