flipper 0.20.0 → 0.21.0.rc1
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 +57 -0
- data/Changelog.md +37 -1
- data/Gemfile +1 -0
- data/README.md +103 -47
- data/docs/Adapters.md +1 -0
- data/docs/Gates.md +74 -74
- data/docs/Optimization.md +7 -7
- data/docs/images/banner.jpg +0 -0
- data/examples/basic.rb +1 -12
- data/examples/configuring_default.rb +1 -2
- 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 +1 -2
- 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 +1 -0
- data/lib/flipper/adapters/http.rb +32 -28
- data/lib/flipper/adapters/memory.rb +20 -94
- data/lib/flipper/adapters/sync/interval_synchronizer.rb +1 -1
- data/lib/flipper/configuration.rb +6 -6
- data/lib/flipper/errors.rb +2 -3
- data/lib/flipper/identifier.rb +17 -0
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/adapters/http_spec.rb +74 -8
- data/spec/flipper/adapters/memory_spec.rb +21 -1
- data/spec/flipper/configuration_spec.rb +5 -2
- data/spec/flipper/identifier_spec.rb +14 -0
- data/spec/flipper/middleware/memoizer_spec.rb +1 -1
- data/spec/flipper/middleware/setup_env_spec.rb +0 -16
- data/spec/flipper_spec.rb +0 -1
- data/spec/support/spec_helpers.rb +3 -0
- data/test/test_helper.rb +1 -0
- metadata +9 -5
- data/examples/example_setup.rb +0 -8
data/docs/Optimization.md
CHANGED
@@ -7,7 +7,7 @@ One optimization that flipper provides is a memoizing middleware. The memoizing
|
|
7
7
|
You can use the middleware like so for Rails:
|
8
8
|
|
9
9
|
```ruby
|
10
|
-
# setup default instance (perhaps in config/
|
10
|
+
# setup default instance (perhaps in config/initializers/flipper.rb)
|
11
11
|
Flipper.configure do |config|
|
12
12
|
config.default do
|
13
13
|
Flipper.new(...)
|
@@ -15,7 +15,7 @@ Flipper.configure do |config|
|
|
15
15
|
end
|
16
16
|
|
17
17
|
# This assumes you setup a default flipper instance using configure.
|
18
|
-
|
18
|
+
Rails.configuration.middleware.use Flipper::Middleware::Memoizer
|
19
19
|
```
|
20
20
|
|
21
21
|
**Note**: Be sure that the middleware is high enough up in your stack that all feature checks are wrapped.
|
@@ -23,8 +23,8 @@ config.middleware.use Flipper::Middleware::Memoizer
|
|
23
23
|
**Also Note**: If you haven't setup a default instance, you can pass the instance to `SetupEnv` as `Memoizer` uses whatever is setup in the `env`:
|
24
24
|
|
25
25
|
```ruby
|
26
|
-
|
27
|
-
|
26
|
+
Rails.configuration.middleware.use Flipper::Middleware::SetupEnv, -> { Flipper.new(...) }
|
27
|
+
Rails.configuration.middleware.use Flipper::Middleware::Memoizer
|
28
28
|
```
|
29
29
|
|
30
30
|
### Options
|
@@ -33,18 +33,18 @@ The Memoizer middleware also supports a few options. Use either `preload` or `pr
|
|
33
33
|
|
34
34
|
* **`:preload`** - An `Array` of feature names (`Symbol`) to preload for every request. Useful if you have features that are used on every endpoint. `preload` uses `Adapter#get_multi` to attempt to load the features in one network call instead of N+1 network calls.
|
35
35
|
```ruby
|
36
|
-
|
36
|
+
Rails.configuration.middleware.use Flipper::Middleware::Memoizer,
|
37
37
|
preload: [:stats, :search, :some_feature]
|
38
38
|
```
|
39
39
|
* **`:preload_all`** - A Boolean value (default: false) of whether or not all features should be preloaded. Using this results in a `preload_all` call with the result of `Adapter#get_all`. Any subsequent feature checks will be memoized and perform no network calls. I wouldn't recommend using this unless you have few features (< 100?) and nearly all of them are used on every request.
|
40
40
|
```ruby
|
41
|
-
|
41
|
+
Rails.configuration.middleware.use Flipper::Middleware::Memoizer,
|
42
42
|
preload_all: true
|
43
43
|
```
|
44
44
|
* **`:unless`** - A block that prevents preloading and memoization if it evaluates to true.
|
45
45
|
```ruby
|
46
46
|
# skip preloading and memoizing if path starts with /assets
|
47
|
-
|
47
|
+
Rails.configuration.middleware.use Flipper::Middleware::Memoizer,
|
48
48
|
unless: ->(request) { request.path.start_with?("/assets") }
|
49
49
|
```
|
50
50
|
|
Binary file
|
data/examples/basic.rb
CHANGED
@@ -1,17 +1,6 @@
|
|
1
|
-
require
|
2
|
-
|
1
|
+
require 'bundler/setup'
|
3
2
|
require 'flipper'
|
4
3
|
|
5
|
-
Flipper.configure do |config|
|
6
|
-
config.default do
|
7
|
-
# pick an adapter, this uses memory, any will do
|
8
|
-
adapter = Flipper::Adapters::Memory.new
|
9
|
-
|
10
|
-
# pass adapter to handy DSL instance
|
11
|
-
Flipper.new(adapter)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
4
|
# check if search is enabled
|
16
5
|
if Flipper.enabled?(:search)
|
17
6
|
puts 'Search away!'
|
data/examples/dsl.rb
CHANGED
@@ -1,20 +1,9 @@
|
|
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
|
# create a thing with an identifier
|
9
|
-
class Person
|
10
|
-
|
11
|
-
|
12
|
-
def initialize(id)
|
13
|
-
@id = id
|
14
|
-
end
|
15
|
-
|
16
|
-
# Must respond to flipper_id
|
17
|
-
alias_method :flipper_id, :id
|
5
|
+
class Person < Struct.new(:id)
|
6
|
+
include Flipper::Identifier
|
18
7
|
end
|
19
8
|
|
20
9
|
person = Person.new(1)
|
@@ -22,14 +11,14 @@ person = Person.new(1)
|
|
22
11
|
puts "Stats are disabled by default\n\n"
|
23
12
|
|
24
13
|
# is a feature enabled
|
25
|
-
puts "flipper.enabled? :stats: #{
|
14
|
+
puts "flipper.enabled? :stats: #{Flipper.enabled? :stats}"
|
26
15
|
|
27
16
|
# is a feature on or off for a particular person
|
28
|
-
puts "
|
17
|
+
puts "Flipper.enabled? :stats, person: #{Flipper.enabled? :stats, person}"
|
29
18
|
|
30
19
|
# get at a feature
|
31
|
-
puts "\nYou can also get an individual feature like this:\nstats =
|
32
|
-
stats =
|
20
|
+
puts "\nYou can also get an individual feature like this:\nstats = Flipper[:stats]\n\n"
|
21
|
+
stats = Flipper[:stats]
|
33
22
|
|
34
23
|
# is that feature enabled
|
35
24
|
puts "stats.enabled?: #{stats.enabled?}"
|
@@ -39,7 +28,7 @@ puts "stats.enabled? person: #{stats.enabled? person}"
|
|
39
28
|
|
40
29
|
# enable a feature by name
|
41
30
|
puts "\nEnabling stats\n\n"
|
42
|
-
|
31
|
+
Flipper.enable :stats
|
43
32
|
|
44
33
|
# or, you can use the feature to enable
|
45
34
|
stats.enable
|
@@ -49,7 +38,7 @@ puts "stats.enabled? person: #{stats.enabled? person}"
|
|
49
38
|
|
50
39
|
# oh, no, let's turn this baby off
|
51
40
|
puts "\nDisabling stats\n\n"
|
52
|
-
|
41
|
+
Flipper.disable :stats
|
53
42
|
|
54
43
|
# or we can disable using feature obviously
|
55
44
|
stats.disable
|
@@ -59,18 +48,18 @@ puts "stats.enabled? person: #{stats.enabled? person}"
|
|
59
48
|
puts
|
60
49
|
|
61
50
|
# get an instance of the percentage of time type set to 5
|
62
|
-
puts
|
51
|
+
puts Flipper.time(5).inspect
|
63
52
|
|
64
53
|
# get an instance of the percentage of actors type set to 15
|
65
|
-
puts
|
54
|
+
puts Flipper.actors(15).inspect
|
66
55
|
|
67
56
|
# get an instance of an actor using an object that responds to flipper_id
|
68
57
|
responds_to_flipper_id = Struct.new(:flipper_id).new(10)
|
69
|
-
puts
|
58
|
+
puts Flipper.actor(responds_to_flipper_id).inspect
|
70
59
|
|
71
60
|
# get an instance of an actor using an object
|
72
61
|
thing = Struct.new(:flipper_id).new(22)
|
73
|
-
puts
|
62
|
+
puts Flipper.actor(thing).inspect
|
74
63
|
|
75
64
|
# register a top level group
|
76
65
|
admins = Flipper.register(:admins) { |actor|
|
@@ -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,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
|