flipper 0.17.1 → 0.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +57 -0
- data/Changelog.md +114 -1
- data/Dockerfile +1 -1
- data/Gemfile +3 -6
- data/README.md +103 -47
- data/Rakefile +1 -4
- data/docs/Adapters.md +9 -9
- data/docs/Caveats.md +2 -2
- data/docs/DockerCompose.md +0 -1
- 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 +35 -0
- 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/flipper.gemspec +3 -4
- data/lib/flipper.rb +7 -3
- data/lib/flipper/adapters/dual_write.rb +67 -0
- data/lib/flipper/adapters/http.rb +32 -28
- data/lib/flipper/adapters/memory.rb +23 -94
- data/lib/flipper/adapters/operation_logger.rb +5 -0
- data/lib/flipper/adapters/pstore.rb +8 -1
- data/lib/flipper/adapters/sync.rb +7 -7
- data/lib/flipper/adapters/sync/interval_synchronizer.rb +1 -1
- data/lib/flipper/adapters/sync/synchronizer.rb +1 -0
- data/lib/flipper/configuration.rb +33 -7
- data/lib/flipper/dsl.rb +8 -0
- data/lib/flipper/errors.rb +2 -3
- data/lib/flipper/feature.rb +2 -2
- data/lib/flipper/identifier.rb +17 -0
- data/lib/flipper/middleware/memoizer.rb +30 -15
- data/lib/flipper/middleware/setup_env.rb +13 -3
- data/lib/flipper/railtie.rb +38 -0
- data/lib/flipper/spec/shared_adapter_specs.rb +15 -0
- data/lib/flipper/test/shared_adapter_test.rb +16 -1
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/adapter_spec.rb +2 -2
- data/spec/flipper/adapters/dual_write_spec.rb +71 -0
- data/spec/flipper/adapters/http_spec.rb +74 -8
- data/spec/flipper/adapters/memory_spec.rb +21 -1
- data/spec/flipper/adapters/operation_logger_spec.rb +9 -0
- data/spec/flipper/adapters/sync_spec.rb +4 -4
- data/spec/flipper/configuration_spec.rb +20 -2
- data/spec/flipper/feature_spec.rb +5 -5
- 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 +23 -3
- data/spec/flipper/railtie_spec.rb +69 -0
- data/spec/{integration_spec.rb → flipper_integration_spec.rb} +0 -0
- data/spec/flipper_spec.rb +26 -0
- data/spec/helper.rb +3 -3
- data/spec/support/descriptions.yml +1 -0
- data/spec/support/spec_helpers.rb +25 -0
- data/test/test_helper.rb +2 -1
- metadata +19 -10
- data/.rubocop.yml +0 -52
- data/.rubocop_todo.yml +0 -562
- data/examples/example_setup.rb +0 -8
data/docs/Caveats.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
1
|
# Caveats
|
2
2
|
|
3
|
-
1. The [individual actor gate](https://github.com/jnunemaker/flipper/blob/master/docs/Gates.md#2-individual-actor) is typically not designed for hundreds or thousands of actors to be enabled. This is an explicit choice to make it easier to batch load data from the adapters instead of performing individual checks for actors over and over. If you need to enable something for more than
|
4
|
-
2. The disable method exists only to clear something that is enabled. If the thing you are disabling is not enabled, the disable is pointless. This means that if you enable one group an actor is in and disable another group, the feature will be enabled for the actor. ([related issue](https://github.com/jnunemaker/flipper/issues/71))
|
3
|
+
1. The [individual actor gate](https://github.com/jnunemaker/flipper/blob/master/docs/Gates.md#2-individual-actor) is typically not designed for hundreds or thousands of actors to be enabled. This is an explicit choice to make it easier to batch load data from the adapters instead of performing individual checks for actors over and over. If you need to enable something for more than 100 individual actors, I would recommend using a [group](https://github.com/jnunemaker/flipper/blob/master/docs/Gates.md#5-group).
|
4
|
+
2. The `disable` method exists only to clear something that is enabled. If the thing you are disabling is not enabled, the disable is pointless. This means that if you enable one group an actor is in and disable another group, the feature will be enabled for the actor. ([related issue](https://github.com/jnunemaker/flipper/issues/71))
|
data/docs/DockerCompose.md
CHANGED
@@ -11,7 +11,6 @@ new contributor could start working on code with a minumum efforts.
|
|
11
11
|
1. Install gems `docker-compose run --rm app bundle install`
|
12
12
|
1. Run specs `docker-compose run --rm app bundle exec rspec`
|
13
13
|
1. Run tests `docker-compose run --rm app bundle exec rake test`
|
14
|
-
1. Clear and check files with Rubocop `docker-compose run --rm app bundle exec rubocop -D`
|
15
14
|
1. Optional: log in to container an using a `bash` shell for running specs
|
16
15
|
```sh
|
17
16
|
docker-compose run --rm app bash
|
data/docs/Gates.md
CHANGED
@@ -7,76 +7,82 @@ Out of the box several types of enabling are supported. They are checked in this
|
|
7
7
|
All on or all off. Think top level things like `:stats`, `:search`, `:logging`, etc. Also, an easy way to release a new feature as once a feature is boolean enabled it is on for every situation.
|
8
8
|
|
9
9
|
```ruby
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
flipper[:stats].enabled? # check
|
10
|
+
Flipper.enable :stats # turn on
|
11
|
+
Flipper.disable :stats # turn off
|
12
|
+
Flipper.enabled? :stats # check
|
14
13
|
```
|
15
14
|
|
16
15
|
## 2. Individual Actor
|
17
16
|
|
18
|
-
Turn feature on for individual thing. Think enable feature for someone to test or for a buddy.
|
17
|
+
Turn feature on for individual thing. Think enable feature for someone to test or for a buddy.
|
19
18
|
|
20
19
|
```ruby
|
21
|
-
|
20
|
+
Flipper.enable_actor :stats, user
|
21
|
+
Flipper.enabled? :stats, user # true
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
flipper[:stats].disable user
|
27
|
-
flipper[:stats].enabled? user # false
|
23
|
+
Flipper.disable_actor :stats, user
|
24
|
+
Flipper.enabled? :stats, user # false
|
28
25
|
|
29
26
|
# you can enable anything, does not need to be user or person
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
# you can also use shortcut methods
|
34
|
-
flipper.enable_actor :search, user
|
35
|
-
flipper.disable_actor :search, user
|
36
|
-
flipper[:search].enable_actor user
|
37
|
-
flipper[:search].disable_actor user
|
38
|
-
```
|
27
|
+
Flipper.enable_actor :search, organization
|
28
|
+
Flipper.enabled? :search, organization
|
39
29
|
|
40
|
-
|
30
|
+
# you can also save a reference to a specific feature
|
31
|
+
feature = Flipper[:search]
|
32
|
+
|
33
|
+
feature.enable_actor user
|
34
|
+
feature.enabled? user # true
|
35
|
+
feature.disable_actor user
|
36
|
+
```
|
41
37
|
|
42
|
-
The
|
38
|
+
The only requirement for an individual actor is that it must have a unique `flipper_id`. Include the `Flipper::Identifier` module for a default implementation which combines the class name and `id` (e.g. `User;6`).
|
43
39
|
|
44
40
|
```ruby
|
45
|
-
class User
|
46
|
-
|
47
|
-
"User;#{id}"
|
48
|
-
end
|
41
|
+
class User < Struct.new(:id)
|
42
|
+
include Flipper::Identifier
|
49
43
|
end
|
50
44
|
|
51
|
-
|
45
|
+
User.new(5).flipper_id # => "User;5"
|
46
|
+
```
|
47
|
+
|
48
|
+
You can also define your own implementation:
|
49
|
+
|
50
|
+
```
|
51
|
+
class Organization < Struct.new(:uuid)
|
52
52
|
def flipper_id
|
53
|
-
|
53
|
+
uuid
|
54
54
|
end
|
55
55
|
end
|
56
|
+
|
57
|
+
Organization.new("DEB3D850-39FB-444B-A1E9-404A990FDBE0").flipper_id
|
58
|
+
# => "DEB3D850-39FB-444B-A1E9-404A990FDBE0"
|
56
59
|
```
|
57
60
|
|
61
|
+
Just make sure each type of object has a unique `flipper_id`.
|
62
|
+
|
58
63
|
## 3. Percentage of Actors
|
59
64
|
|
60
65
|
Turn this on for a percentage of actors (think user, member, account, group, whatever). Consistently on or off for this user as long as percentage increases. Think slow rollout of a new feature to a percentage of things.
|
61
66
|
|
62
67
|
```ruby
|
63
|
-
flipper = Flipper.new(adapter)
|
64
|
-
|
65
|
-
# returns a percentage of actors instance set to 10
|
66
|
-
percentage = flipper.actors(10)
|
67
|
-
|
68
68
|
# turn stats on for 10 percent of users in the system
|
69
|
-
|
69
|
+
Flipper.enable :stats, Flipper.actors(10)
|
70
|
+
# or
|
71
|
+
Flipper.enable_percentage_of_actors :stats, 10
|
70
72
|
|
71
73
|
# checks if actor's flipper_id is in the enabled percentage by hashing
|
72
74
|
# user.flipper_id.to_s to ensure enabled distribution is smooth
|
73
|
-
|
75
|
+
Flipper.enabled? :stats, user
|
74
76
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
77
|
+
Flipper.disable_percentage_of_actors :search # sets to 0
|
78
|
+
# or
|
79
|
+
Flipper.disable :stats, Flipper.actors(0)
|
80
|
+
|
81
|
+
# you can also save a reference to a specific feature
|
82
|
+
feature = Flipper[:search]
|
83
|
+
feature.enable_percentage_of_actors 10
|
84
|
+
feature.enabled? user
|
85
|
+
feature.disable_percentage_of_actors # sets to 0
|
80
86
|
```
|
81
87
|
|
82
88
|
## 4. Percentage of Time
|
@@ -84,22 +90,20 @@ flipper[:search].disable_percentage_of_actors # sets to 0
|
|
84
90
|
Turn this on for a percentage of time. Think load testing new features behind the scenes and such.
|
85
91
|
|
86
92
|
```ruby
|
87
|
-
flipper = Flipper.new(adapter)
|
88
|
-
|
89
|
-
# get percentage of time instance set to 5
|
90
|
-
percentage = flipper.time(5)
|
91
|
-
|
92
93
|
# Register a feature called logging and turn it on for 5 percent of the time.
|
93
94
|
# This could be on during one request and off the next
|
94
95
|
# could even be on first time in request and off second time
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
96
|
+
Flipper.enable_percentage_of_time :logging, 5
|
97
|
+
|
98
|
+
Flipper.enabled? :logging # this will return true 5% of the time.
|
99
|
+
|
100
|
+
Flipper.disable_percentage_of_time :logging # sets to 0
|
101
|
+
|
102
|
+
# you can also save a reference to a specific feature
|
103
|
+
feature = Flipper[:search]
|
104
|
+
feature.enable_percentage_of_time, 5
|
105
|
+
feature.enabled?
|
106
|
+
feature.disable_percentage_of_time
|
103
107
|
```
|
104
108
|
|
105
109
|
Timeness is not a good idea for enabling new features in the UI. Most of the time you want a feature on or off for a user, but there are definitely times when I have found percentage of time to be very useful.
|
@@ -114,52 +118,48 @@ Flipper.register(:admins) do |actor|
|
|
114
118
|
actor.respond_to?(:admin?) && actor.admin?
|
115
119
|
end
|
116
120
|
|
117
|
-
|
118
|
-
|
119
|
-
flipper[:stats].enable flipper.group(:admins) # This registers a stats feature and turns it on for admins (which is anything that returns true from the registered block).
|
120
|
-
flipper[:stats].disable flipper.group(:admins) # turn off the stats feature for admins
|
121
|
+
Flipper.enable_group :stats, :admins # This registers a stats feature and turns it on for admins (which is anything that returns true from the registered block).
|
122
|
+
Flipper.disable_group :stats, :admins # turn off the stats feature for admins
|
121
123
|
|
122
124
|
person = Person.find(params[:id])
|
123
|
-
|
125
|
+
Flipper.enabled? :stats, person # check if enabled, returns true if person.admin? is true
|
124
126
|
|
125
|
-
# you can also use shortcut methods. This also registers a stats feature and turns it on for admins.
|
126
|
-
flipper.enable_group :stats, :admins
|
127
|
-
person = Person.find(params[:id])
|
128
|
-
flipper[:stats].enabled? person # same as above. check if enabled, returns true if person.admin? is true
|
129
127
|
|
130
|
-
|
131
|
-
|
132
|
-
|
128
|
+
# you can also use shortcut methods. This also registers a stats feature and turns it on for admins.
|
129
|
+
feature = Flipper[:search]
|
130
|
+
feature.enable_group :admins
|
131
|
+
feature.enabled? person
|
132
|
+
feature.disable_group :admins
|
133
133
|
```
|
134
134
|
|
135
135
|
Here's a quick explanation of the above code block:
|
136
136
|
|
137
|
-
```
|
137
|
+
```ruby
|
138
138
|
Flipper.register(:admins) do |actor|
|
139
139
|
actor.respond_to?(:admin?) && actor.admin?
|
140
140
|
end
|
141
141
|
```
|
142
|
-
- The above first registers a group called `admins` which essentially saves a [Proc](http://www.eriktrautman.com/posts/ruby-explained-blocks-procs-and-lambdas-aka-closures) to be called later.
|
142
|
+
- The above first registers a group called `admins` which essentially saves a [Proc](http://www.eriktrautman.com/posts/ruby-explained-blocks-procs-and-lambdas-aka-closures) to be called later. The `actor` is an instance of the `Flipper::Types::Actor` that wraps the thing being checked against and `actor.thing` is the original object being checked.
|
143
143
|
|
144
|
-
```
|
145
|
-
|
144
|
+
```ruby
|
145
|
+
Flipper.enable_group :stats, :admins
|
146
146
|
```
|
147
147
|
|
148
148
|
- The above enables the stats feature to any object that returns true from the `:admins` proc.
|
149
149
|
|
150
|
-
```
|
150
|
+
```ruby
|
151
151
|
person = Person.find(params[:id])
|
152
|
-
|
152
|
+
Flipper.enabled? :stats, person # check if person is enabled, returns true if person.admin? is true
|
153
153
|
```
|
154
154
|
|
155
|
-
When the `person` object is passed to the `enabled?` method, it is then passed into the proc. If the proc returns true, the entire statement returns true and so `
|
155
|
+
When the `person` object is passed to the `enabled?` method, it is then passed into the proc. If the proc returns true, the entire statement returns true and so `Flipper[:stats].enabled? person` returns true. Whatever logic follows this conditional check is then executed.
|
156
156
|
|
157
157
|
There is no requirement that the thing yielded to the block be a user model or whatever. It can be anything you want, therefore it is a good idea to check that the thing passed into the group block actually responds to what you are trying to do in the `register` proc.
|
158
158
|
|
159
159
|
In your application code, you can do something like this now:
|
160
160
|
|
161
|
-
```
|
162
|
-
if
|
161
|
+
```ruby
|
162
|
+
if Flipper.enabled? :stats, some_admin
|
163
163
|
# do thing...
|
164
164
|
else
|
165
165
|
# do not do thing
|
data/docs/Optimization.md
CHANGED
@@ -1,52 +1,63 @@
|
|
1
1
|
# Optimization
|
2
2
|
|
3
|
-
##
|
3
|
+
## Memoization
|
4
4
|
|
5
|
-
|
5
|
+
By default, Flipper will preload and memoize all features to ensure one adapter call per request. This means no matter how many times you check features, Flipper will only make one network request to Postgres, MySQL, Redis, Mongo or whatever adapter you are using for the length of the request.
|
6
6
|
|
7
|
-
|
7
|
+
### Preloading
|
8
|
+
|
9
|
+
Flipper will preload all features before each request by default, which is recommended if you have a limited number of features (< 100?) and they are used on most requests. If you have a lot of features, but only a few are used on most requests, you may want to customize preloading:
|
8
10
|
|
9
11
|
```ruby
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
# config/initializers/flipper.rb
|
13
|
+
Rails.application.configure do
|
14
|
+
# Load specific features that are used on most requests
|
15
|
+
config.flipper.preload = [:stats, :search, :some_feature]
|
16
|
+
|
17
|
+
# Or completely disable preloading
|
18
|
+
config.flipper.preload = false
|
15
19
|
end
|
20
|
+
```
|
21
|
+
|
22
|
+
Features that are not preloaded are still memoized, ensuring one adapter call per feature during a request.
|
23
|
+
|
24
|
+
### Skip memoization
|
25
|
+
|
26
|
+
Prevent preloading and memoization on specific requests by setting `memoize` to a proc that evaluates to false.
|
16
27
|
|
17
|
-
|
18
|
-
config.
|
28
|
+
```ruby
|
29
|
+
# config/initializers/flipper.rb
|
30
|
+
Rails.application.configure do
|
31
|
+
config.flipper.memoize = ->(request) { !request.path.start_with?("/assets") }
|
32
|
+
end
|
19
33
|
```
|
20
34
|
|
21
|
-
|
35
|
+
### Disable memoization
|
22
36
|
|
23
|
-
|
37
|
+
To disable memoization entirely:
|
24
38
|
|
25
39
|
```ruby
|
26
|
-
|
27
|
-
config.
|
40
|
+
Rails.application.configure do
|
41
|
+
config.flipper.memoize = false
|
42
|
+
end
|
28
43
|
```
|
29
44
|
|
30
|
-
###
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
# skip preloading and memoizing if path starts with /assets
|
47
|
-
config.middleware.use Flipper::Middleware::Memoizer,
|
48
|
-
unless: ->(request) { request.path.start_with?("/assets") }
|
49
|
-
```
|
45
|
+
### Advanced
|
46
|
+
|
47
|
+
Memoization is implemented as a Rack middleware, which can be used manually in any Ruby app:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
use Flipper::Middleware::Memoizer,
|
51
|
+
preload: true,
|
52
|
+
unless: ->(request) { request.path.start_with?("/assets") }
|
53
|
+
```
|
54
|
+
|
55
|
+
**Also Note**: If you need to customize the instance of Flipper used by the memoizer, you can pass the instance to `SetupEnv`:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
use Flipper::Middleware::SetupEnv, -> { Flipper.new(...) }
|
59
|
+
use Flipper::Middleware::Memoizer
|
60
|
+
```
|
50
61
|
|
51
62
|
## Cache Adapters
|
52
63
|
|
@@ -61,11 +72,15 @@ https://github.com/petergoldstein/dalli
|
|
61
72
|
Example using the Dalli cache adapter with the Memory adapter and a TTL of 600 seconds:
|
62
73
|
|
63
74
|
```ruby
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
75
|
+
Flipper.configure do |config|
|
76
|
+
config.adapter do
|
77
|
+
dalli = Dalli::Client.new('localhost:11211')
|
78
|
+
adapter = Flipper::Adapters::Memory.new
|
79
|
+
Flipper::Adapters::Dalli.new(adapter, dalli, 600)
|
80
|
+
end
|
81
|
+
end
|
68
82
|
```
|
83
|
+
|
69
84
|
### RedisCache
|
70
85
|
|
71
86
|
Applications using [Redis](https://redis.io/) via the [redis-rb](https://github.com/redis/redis-rb) client can take advantage of the RedisCache adapter.
|
@@ -75,12 +90,15 @@ Initialize `RedisCache` with a flipper [adapter](https://github.com/jnunemaker/
|
|
75
90
|
Example using the RedisCache adapter with the Memory adapter and a TTL of 4800 seconds:
|
76
91
|
|
77
92
|
```ruby
|
78
|
-
|
93
|
+
require 'flipper/adapters/redis_cache'
|
79
94
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
95
|
+
Flipper.configure do |config|
|
96
|
+
config.adapter do
|
97
|
+
redis = Redis.new(url: ENV['REDIS_URL'])
|
98
|
+
memory_adapter = Flipper::Adapters::Memory.new
|
99
|
+
Flipper::Adapters::RedisCache.new(memory_adapter, redis, 4800)
|
100
|
+
end
|
101
|
+
end
|
84
102
|
```
|
85
103
|
|
86
104
|
### ActiveSupportCacheStore
|
@@ -105,10 +123,15 @@ Example using the ActiveSupportCacheStore adapter with ActiveSupport's [MemorySt
|
|
105
123
|
require 'active_support/cache'
|
106
124
|
require 'flipper/adapters/active_support_cache_store'
|
107
125
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
126
|
+
Flipper.configure do |config|
|
127
|
+
config.adapter do
|
128
|
+
Flipper::Adapters::ActiveSupportCacheStore.new(
|
129
|
+
Flipper::Adapters::Memory.new,
|
130
|
+
ActiveSupport::Cache::MemoryStore.new # Or Rails.cache,
|
131
|
+
expires_in: 5.minutes
|
132
|
+
)
|
133
|
+
end
|
134
|
+
end
|
112
135
|
```
|
113
136
|
|
114
137
|
Setting `expires_in` is optional and will set an expiration time on Flipper cache keys. If specified, all flipper keys will use this `expires_in` over the `expires_in` passed to your ActiveSupport cache constructor.
|
data/docs/http/README.md
CHANGED
@@ -8,17 +8,18 @@ Initialize the HTTP adapter with a configuration Hash.
|
|
8
8
|
```ruby
|
9
9
|
require 'flipper/adapters/http'
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
11
|
+
Flipper.configure do |config|
|
12
|
+
config.adapter do
|
13
|
+
Flipper::Adapters::Http.new({
|
14
|
+
url: 'http://app.com/mount-point', # required
|
15
|
+
headers: { 'X-Custom-Header' => 'foo' },
|
16
|
+
basic_auth_username: 'user123',
|
17
|
+
basic_auth_password: 'password123'
|
18
|
+
read_timeout: 5,
|
19
|
+
open_timeout: 2,
|
20
|
+
})
|
21
|
+
end
|
22
|
+
end
|
22
23
|
```
|
23
24
|
|
24
25
|
**Required keys**:
|
Binary file
|
data/docs/read-only/README.md
CHANGED
@@ -5,17 +5,20 @@ A [read-only](https://github.com/jnunemaker/flipper/blob/master/lib/flipper/adap
|
|
5
5
|
Use this adapter to wrap another adapter and raise an exception for any writes.
|
6
6
|
|
7
7
|
Any attempted write raises `Flipper::Adapters::ReadOnly::WriteAttempted` with message `'write attempted while in read only mode'`
|
8
|
+
|
8
9
|
## Usage
|
10
|
+
|
9
11
|
```ruby
|
10
12
|
# example wrapping memory adapter
|
11
13
|
require 'flipper/adapters/read_only'
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
Flipper.configure do |config|
|
16
|
+
config.adapter do
|
17
|
+
Flipper::Adapters::ReadOnly.new(Flipper::Adapters::Memory.new)
|
18
|
+
end
|
19
|
+
end
|
17
20
|
|
18
21
|
# Enabling a feature
|
19
|
-
>
|
22
|
+
> Flipper[:dashboard_panel].enable
|
20
23
|
=> Flipper::Adapters::ReadOnly::WriteAttempted: write attempted while in read only mode
|
21
24
|
```
|