flipper 0.20.3 → 0.22.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 +18 -9
- data/Changelog.md +62 -0
- data/Gemfile +2 -0
- data/README.md +104 -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/api/README.md +5 -5
- data/docs/http/README.md +12 -11
- data/docs/images/banner.jpg +0 -0
- data/docs/read-only/README.md +8 -5
- data/examples/api/basic.ru +19 -0
- data/examples/api/custom_memoized.ru +37 -0
- data/examples/api/memoized.ru +43 -0
- 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/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 +30 -15
- data/lib/flipper/railtie.rb +46 -0
- data/lib/flipper/version.rb +1 -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 +12 -3
- data/examples/example_setup.rb +0 -8
@@ -1,5 +1,4 @@
|
|
1
|
-
require
|
2
|
-
|
1
|
+
require 'bundler/setup'
|
3
2
|
require 'flipper'
|
4
3
|
|
5
4
|
# Some class that represents what will be trying to do something
|
@@ -22,21 +21,15 @@ end
|
|
22
21
|
user1 = User.new(1, true)
|
23
22
|
user2 = User.new(2, false)
|
24
23
|
|
25
|
-
# pick an adapter
|
26
|
-
adapter = Flipper::Adapters::Memory.new
|
27
|
-
|
28
|
-
# get a handy dsl instance
|
29
|
-
flipper = Flipper.new(adapter)
|
30
|
-
|
31
24
|
Flipper.register :admins do |actor|
|
32
25
|
actor.admin?
|
33
26
|
end
|
34
27
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
28
|
+
Flipper.enable :search
|
29
|
+
Flipper.enable_actor :stats, user1
|
30
|
+
Flipper.enable_percentage_of_actors :pro_stats, 50
|
31
|
+
Flipper.enable_group :tweets, :admins
|
32
|
+
Flipper.enable_actor :posts, user2
|
40
33
|
|
41
|
-
pp
|
42
|
-
pp
|
34
|
+
pp Flipper.features.select { |feature| feature.enabled?(user1) }.map(&:name)
|
35
|
+
pp Flipper.features.select { |feature| feature.enabled?(user2) }.map(&:name)
|
data/examples/group.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
|
# Register group
|
10
7
|
Flipper.register(:admins) do |actor|
|
@@ -35,7 +32,7 @@ puts "Stats for admin: #{stats.enabled?(admin)}"
|
|
35
32
|
puts "Stats for non_admin: #{stats.enabled?(non_admin)}"
|
36
33
|
|
37
34
|
puts "\nEnabling Stats for admins...\n\n"
|
38
|
-
stats.
|
35
|
+
stats.enable_group :admins
|
39
36
|
|
40
37
|
puts "Stats for admin: #{stats.enabled?(admin)}"
|
41
38
|
puts "Stats for non_admin: #{stats.enabled?(non_admin)}"
|
@@ -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
|
# Register group
|
10
7
|
Flipper.register(:enabled_team_member) do |actor, context|
|
@@ -15,19 +12,12 @@ Flipper.register(:enabled_team_member) do |actor, context|
|
|
15
12
|
end
|
16
13
|
|
17
14
|
# Some class that represents actor that will be trying to do something
|
18
|
-
class User
|
19
|
-
|
20
|
-
|
21
|
-
def initialize(id)
|
22
|
-
@id = id
|
23
|
-
end
|
24
|
-
|
25
|
-
def flipper_id
|
26
|
-
"User;#{@id}"
|
27
|
-
end
|
15
|
+
class User < Struct.new(:id)
|
16
|
+
include Flipper::Identifier
|
28
17
|
end
|
29
18
|
|
30
19
|
class Team
|
20
|
+
include Flipper::Identifier
|
31
21
|
attr_reader :name
|
32
22
|
|
33
23
|
def self.all
|
@@ -51,10 +41,6 @@ class Team
|
|
51
41
|
def member?(actor)
|
52
42
|
@members.map(&:id).include?(actor.id)
|
53
43
|
end
|
54
|
-
|
55
|
-
def flipper_id
|
56
|
-
"Team:#{@name}"
|
57
|
-
end
|
58
44
|
end
|
59
45
|
|
60
46
|
jnunemaker = User.new("jnunemaker")
|
@@ -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
|
# Register group
|
10
7
|
Flipper.register(:team_actor) do |actor|
|
@@ -12,15 +9,8 @@ Flipper.register(:team_actor) do |actor|
|
|
12
9
|
end
|
13
10
|
|
14
11
|
# Some class that represents actor that will be trying to do something
|
15
|
-
class User
|
16
|
-
|
17
|
-
|
18
|
-
def initialize(id)
|
19
|
-
@id = id
|
20
|
-
end
|
21
|
-
|
22
|
-
# Must respond to flipper_id
|
23
|
-
alias_method :flipper_id, :id
|
12
|
+
class User < Struct.new(:id)
|
13
|
+
include Flipper::Identifier
|
24
14
|
end
|
25
15
|
|
26
16
|
class Team
|
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)
|
@@ -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.
|
data/lib/flipper/errors.rb
CHANGED
@@ -16,9 +16,8 @@ module Flipper
|
|
16
16
|
# use it.
|
17
17
|
class DefaultNotSet < Flipper::Error
|
18
18
|
def initialize(message = nil)
|
19
|
-
|
20
|
-
|
21
|
-
super(message || default)
|
19
|
+
warn "Flipper::DefaultNotSet is deprecated and will be removed in 1.0"
|
20
|
+
super
|
22
21
|
end
|
23
22
|
end
|
24
23
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Flipper
|
2
|
+
# A default implementation of `#flipper_id` for actors.
|
3
|
+
#
|
4
|
+
# class User < Struct.new(:id)
|
5
|
+
# include Flipper::Identifier
|
6
|
+
# end
|
7
|
+
#
|
8
|
+
# user = User.new(99)
|
9
|
+
# Flipper.enable :thing, user
|
10
|
+
# Flipper.enabled? :thing, user #=> true
|
11
|
+
#
|
12
|
+
module Identifier
|
13
|
+
def flipper_id
|
14
|
+
"#{self.class.name};#{id}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|