flipper 0.20.0.beta1 → 0.20.2
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 +49 -0
- data/Changelog.md +19 -0
- data/docs/Optimization.md +7 -7
- data/lib/flipper/adapters/dual_write.rb +67 -0
- data/lib/flipper/adapters/http.rb +32 -28
- data/lib/flipper/adapters/operation_logger.rb +5 -0
- data/lib/flipper/adapters/sync.rb +1 -1
- data/lib/flipper/adapters/sync/interval_synchronizer.rb +1 -1
- data/lib/flipper/dsl.rb +8 -0
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/adapters/dual_write_spec.rb +71 -0
- data/spec/flipper/adapters/http_spec.rb +74 -8
- data/spec/flipper/adapters/operation_logger_spec.rb +9 -0
- data/spec/flipper_spec.rb +3 -3
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df90e63870767caa4d32363658e4515d0822a1453cc3a5325d7dbf932c326b59
|
4
|
+
data.tar.gz: 125256e9cdee5bd0e2bfe065203140eb7fa015cac56043711c7eb45bd5421aa8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9598a0a2f62b5c160a8a7c81b50ed9d2bd06f6e70898037c8321f9060780875347552b66919c62b5deb40647f734efa6bef1382b0e9fb1caefe2c8f4b5eaec01
|
7
|
+
data.tar.gz: 8bb413f54398cf90e9aafc7feba144a2e15343e8a75e28ca197d52505ae4771659b3fc7bfafec899388e71c76832859396d86fa52438ba22327f6b6522ade3a5
|
@@ -0,0 +1,49 @@
|
|
1
|
+
name: CI
|
2
|
+
on: [push, pull_request]
|
3
|
+
jobs:
|
4
|
+
build:
|
5
|
+
runs-on: ubuntu-latest
|
6
|
+
services:
|
7
|
+
redis:
|
8
|
+
image: redis
|
9
|
+
ports: ['6379:6379']
|
10
|
+
options: >-
|
11
|
+
--health-cmd "redis-cli ping"
|
12
|
+
--health-interval 10s
|
13
|
+
--health-timeout 5s
|
14
|
+
--health-retries 5
|
15
|
+
strategy:
|
16
|
+
matrix:
|
17
|
+
ruby: ['2.5', '2.6', '2.7']
|
18
|
+
env:
|
19
|
+
RAILS_VERSION: 6.0.0
|
20
|
+
SQLITE3_VERSION: 1.4.1
|
21
|
+
REDIS_URL: redis://localhost:6379/0
|
22
|
+
steps:
|
23
|
+
- name: Setup memcached
|
24
|
+
uses: KeisukeYamashita/memcached-actions@v1
|
25
|
+
- name: Start MongoDB
|
26
|
+
uses: supercharge/mongodb-github-action@1.3.0
|
27
|
+
with:
|
28
|
+
mongodb-version: 4.0
|
29
|
+
- name: Check out repository code
|
30
|
+
uses: actions/checkout@v2
|
31
|
+
- name: Do some action caching
|
32
|
+
uses: actions/cache@v1
|
33
|
+
with:
|
34
|
+
path: vendor/bundle
|
35
|
+
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
|
36
|
+
restore-keys: |
|
37
|
+
${{ runner.os }}-gem-
|
38
|
+
- name: Set up Ruby
|
39
|
+
uses: actions/setup-ruby@v1
|
40
|
+
with:
|
41
|
+
ruby-version: ${{ matrix.ruby }}
|
42
|
+
- name: Install libpq-dev
|
43
|
+
run: sudo apt-get -yqq install libpq-dev
|
44
|
+
- name: Install bundler
|
45
|
+
run: gem install bundler
|
46
|
+
- name: Run bundler
|
47
|
+
run: bundle install --jobs 4 --retry 3
|
48
|
+
- name: Run Rake
|
49
|
+
run: bundle exec rake
|
data/Changelog.md
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
## 0.20.2
|
2
|
+
|
3
|
+
### Additions/Changes
|
4
|
+
|
5
|
+
* Http adapter now raises error when enable/disable/add/remove/clear fail.
|
6
|
+
* Cloud adapter sends some extra info like hostname, ruby version, etc. for debugging and decision making.
|
7
|
+
|
8
|
+
## 0.20.1
|
9
|
+
|
10
|
+
### Additions/Changes
|
11
|
+
|
12
|
+
* Just a minor tweak to cloud webhook middleware to provide more debugging information about why a hook wasn't successful.
|
13
|
+
|
14
|
+
## 0.20.0
|
15
|
+
|
16
|
+
### Additions/Changes
|
17
|
+
|
18
|
+
* Add support for webhooks to `Flipper::Cloud` (https://github.com/jnunemaker/flipper/pull/489).
|
19
|
+
|
1
20
|
## 0.19.1
|
2
21
|
|
3
22
|
### Additions/Changes
|
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
|
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Flipper
|
2
|
+
module Adapters
|
3
|
+
class DualWrite
|
4
|
+
include ::Flipper::Adapter
|
5
|
+
|
6
|
+
# Public: The name of the adapter.
|
7
|
+
attr_reader :name
|
8
|
+
|
9
|
+
# Public: Build a new sync instance.
|
10
|
+
#
|
11
|
+
# local - The local flipper adapter that should serve reads.
|
12
|
+
# remote - The remote flipper adapter that writes should go to first (in
|
13
|
+
# addition to the local adapter).
|
14
|
+
def initialize(local, remote, options = {})
|
15
|
+
@name = :dual_write
|
16
|
+
@local = local
|
17
|
+
@remote = remote
|
18
|
+
end
|
19
|
+
|
20
|
+
def features
|
21
|
+
@local.features
|
22
|
+
end
|
23
|
+
|
24
|
+
def get(feature)
|
25
|
+
@local.get(feature)
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_multi(features)
|
29
|
+
@local.get_multi(features)
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_all
|
33
|
+
@local.get_all
|
34
|
+
end
|
35
|
+
|
36
|
+
def add(feature)
|
37
|
+
result = @remote.add(feature)
|
38
|
+
@local.add(feature)
|
39
|
+
result
|
40
|
+
end
|
41
|
+
|
42
|
+
def remove(feature)
|
43
|
+
result = @remote.remove(feature)
|
44
|
+
@local.remove(feature)
|
45
|
+
result
|
46
|
+
end
|
47
|
+
|
48
|
+
def clear(feature)
|
49
|
+
result = @remote.clear(feature)
|
50
|
+
@local.clear(feature)
|
51
|
+
result
|
52
|
+
end
|
53
|
+
|
54
|
+
def enable(feature, gate, thing)
|
55
|
+
result = @remote.enable(feature, gate, thing)
|
56
|
+
@local.enable(feature, gate, thing)
|
57
|
+
result
|
58
|
+
end
|
59
|
+
|
60
|
+
def disable(feature, gate, thing)
|
61
|
+
result = @remote.disable(feature, gate, thing)
|
62
|
+
@local.disable(feature, gate, thing)
|
63
|
+
result
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -35,12 +35,6 @@ module Flipper
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
def add(feature)
|
39
|
-
body = JSON.generate(name: feature.key)
|
40
|
-
response = @client.post('/features', body)
|
41
|
-
response.is_a?(Net::HTTPOK)
|
42
|
-
end
|
43
|
-
|
44
38
|
def get_multi(features)
|
45
39
|
csv_keys = features.map(&:key).join(',')
|
46
40
|
response = @client.get("/features?keys=#{csv_keys}")
|
@@ -87,51 +81,61 @@ module Flipper
|
|
87
81
|
parsed_response['features'].map { |feature| feature['key'] }.to_set
|
88
82
|
end
|
89
83
|
|
84
|
+
def add(feature)
|
85
|
+
body = JSON.generate(name: feature.key)
|
86
|
+
response = @client.post('/features', body)
|
87
|
+
raise Error, response unless response.is_a?(Net::HTTPOK)
|
88
|
+
true
|
89
|
+
end
|
90
|
+
|
90
91
|
def remove(feature)
|
91
92
|
response = @client.delete("/features/#{feature.key}")
|
92
|
-
response.is_a?(Net::HTTPNoContent)
|
93
|
+
raise Error, response unless response.is_a?(Net::HTTPNoContent)
|
94
|
+
true
|
93
95
|
end
|
94
96
|
|
95
97
|
def enable(feature, gate, thing)
|
96
98
|
body = request_body_for_gate(gate, thing.value.to_s)
|
97
99
|
query_string = gate.key == :groups ? "?allow_unregistered_groups=true" : ""
|
98
100
|
response = @client.post("/features/#{feature.key}/#{gate.key}#{query_string}", body)
|
99
|
-
response.is_a?(Net::HTTPOK)
|
101
|
+
raise Error, response unless response.is_a?(Net::HTTPOK)
|
102
|
+
true
|
100
103
|
end
|
101
104
|
|
102
105
|
def disable(feature, gate, thing)
|
103
106
|
body = request_body_for_gate(gate, thing.value.to_s)
|
104
107
|
query_string = gate.key == :groups ? "?allow_unregistered_groups=true" : ""
|
105
|
-
response =
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
108
|
+
response = case gate.key
|
109
|
+
when :percentage_of_actors, :percentage_of_time
|
110
|
+
@client.post("/features/#{feature.key}/#{gate.key}#{query_string}", body)
|
111
|
+
else
|
112
|
+
@client.delete("/features/#{feature.key}/#{gate.key}#{query_string}", body)
|
113
|
+
end
|
114
|
+
raise Error, response unless response.is_a?(Net::HTTPOK)
|
115
|
+
true
|
113
116
|
end
|
114
117
|
|
115
118
|
def clear(feature)
|
116
119
|
response = @client.delete("/features/#{feature.key}/clear")
|
117
|
-
response.is_a?(Net::HTTPNoContent)
|
120
|
+
raise Error, response unless response.is_a?(Net::HTTPNoContent)
|
121
|
+
true
|
118
122
|
end
|
119
123
|
|
120
124
|
private
|
121
125
|
|
122
126
|
def request_body_for_gate(gate, value)
|
123
127
|
data = case gate.key
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
128
|
+
when :boolean
|
129
|
+
{}
|
130
|
+
when :groups
|
131
|
+
{ name: value }
|
132
|
+
when :actors
|
133
|
+
{ flipper_id: value }
|
134
|
+
when :percentage_of_actors, :percentage_of_time
|
135
|
+
{ percentage: value }
|
136
|
+
else
|
137
|
+
raise "#{gate.key} is not a valid flipper gate key"
|
138
|
+
end
|
135
139
|
JSON.generate(data)
|
136
140
|
end
|
137
141
|
|
@@ -117,6 +117,11 @@ module Flipper
|
|
117
117
|
def reset
|
118
118
|
@operations.clear
|
119
119
|
end
|
120
|
+
|
121
|
+
def inspect
|
122
|
+
inspect_id = ::Kernel::format "%x", (object_id * 2)
|
123
|
+
%(#<#{self.class}:0x#{inspect_id} @name=#{name.inspect}, @operations=#{@operations.inspect}, @adapter=#{@adapter.inspect}>)
|
124
|
+
end
|
120
125
|
end
|
121
126
|
end
|
122
127
|
end
|
@@ -17,7 +17,7 @@ module Flipper
|
|
17
17
|
# Public: Build a new sync instance.
|
18
18
|
#
|
19
19
|
# local - The local flipper adapter that should serve reads.
|
20
|
-
# remote - The remote flipper
|
20
|
+
# remote - The remote flipper adapter that should serve writes and update
|
21
21
|
# the local on an interval.
|
22
22
|
# interval - The Float or Integer number of seconds between syncs from
|
23
23
|
# remote to local. Default value is set in IntervalSynchronizer.
|
@@ -19,7 +19,7 @@ module Flipper
|
|
19
19
|
# Public: Initializes a new interval synchronizer.
|
20
20
|
#
|
21
21
|
# synchronizer - The Synchronizer to call when the interval has passed.
|
22
|
-
# interval - The Integer number of
|
22
|
+
# interval - The Integer number of seconds between invocations of
|
23
23
|
# the wrapped synchronizer.
|
24
24
|
def initialize(synchronizer, interval: nil)
|
25
25
|
@synchronizer = synchronizer
|
data/lib/flipper/dsl.rb
CHANGED
@@ -273,5 +273,13 @@ module Flipper
|
|
273
273
|
def import(flipper)
|
274
274
|
adapter.import(flipper.adapter)
|
275
275
|
end
|
276
|
+
|
277
|
+
# Cloud DSL method that does nothing for open source version.
|
278
|
+
def sync
|
279
|
+
end
|
280
|
+
|
281
|
+
# Cloud DSL method that does nothing for open source version.
|
282
|
+
def sync_secret
|
283
|
+
end
|
276
284
|
end
|
277
285
|
end
|
data/lib/flipper/version.rb
CHANGED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'flipper/adapters/dual_write'
|
3
|
+
require 'flipper/adapters/operation_logger'
|
4
|
+
require 'flipper/spec/shared_adapter_specs'
|
5
|
+
require 'active_support/notifications'
|
6
|
+
|
7
|
+
RSpec.describe Flipper::Adapters::DualWrite do
|
8
|
+
let(:local_adapter) do
|
9
|
+
Flipper::Adapters::OperationLogger.new Flipper::Adapters::Memory.new
|
10
|
+
end
|
11
|
+
let(:remote_adapter) do
|
12
|
+
Flipper::Adapters::OperationLogger.new Flipper::Adapters::Memory.new
|
13
|
+
end
|
14
|
+
let(:local) { Flipper.new(local_adapter) }
|
15
|
+
let(:remote) { Flipper.new(remote_adapter) }
|
16
|
+
let(:sync) { Flipper.new(subject) }
|
17
|
+
|
18
|
+
subject do
|
19
|
+
described_class.new(local_adapter, remote_adapter)
|
20
|
+
end
|
21
|
+
|
22
|
+
it_should_behave_like 'a flipper adapter'
|
23
|
+
|
24
|
+
it 'only uses local for #features' do
|
25
|
+
subject.features
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'only uses local for #get' do
|
29
|
+
subject.get sync[:search]
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'only uses local for #get_multi' do
|
33
|
+
subject.get_multi [sync[:search]]
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'only uses local for #get_all' do
|
37
|
+
subject.get_all
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'updates remote and local for #add' do
|
41
|
+
subject.add sync[:search]
|
42
|
+
expect(remote_adapter.count(:add)).to be(1)
|
43
|
+
expect(local_adapter.count(:add)).to be(1)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'updates remote and local for #remove' do
|
47
|
+
subject.remove sync[:search]
|
48
|
+
expect(remote_adapter.count(:remove)).to be(1)
|
49
|
+
expect(local_adapter.count(:remove)).to be(1)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'updates remote and local for #clear' do
|
53
|
+
subject.clear sync[:search]
|
54
|
+
expect(remote_adapter.count(:clear)).to be(1)
|
55
|
+
expect(local_adapter.count(:clear)).to be(1)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'updates remote and local for #enable' do
|
59
|
+
feature = sync[:search]
|
60
|
+
subject.enable feature, feature.gate(:boolean), local.boolean
|
61
|
+
expect(remote_adapter.count(:enable)).to be(1)
|
62
|
+
expect(local_adapter.count(:enable)).to be(1)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'updates remote and local for #disable' do
|
66
|
+
feature = sync[:search]
|
67
|
+
subject.disable feature, feature.gate(:boolean), local.boolean(false)
|
68
|
+
expect(remote_adapter.count(:disable)).to be(1)
|
69
|
+
expect(local_adapter.count(:disable)).to be(1)
|
70
|
+
end
|
71
|
+
end
|
@@ -76,9 +76,9 @@ RSpec.describe Flipper::Adapters::Http do
|
|
76
76
|
.to_return(status: 503, body: "", headers: {})
|
77
77
|
|
78
78
|
adapter = described_class.new(url: 'http://app.com/flipper')
|
79
|
-
expect
|
79
|
+
expect {
|
80
80
|
adapter.get(flipper[:feature_panel])
|
81
|
-
|
81
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
@@ -88,9 +88,9 @@ RSpec.describe Flipper::Adapters::Http do
|
|
88
88
|
.to_return(status: 503, body: "", headers: {})
|
89
89
|
|
90
90
|
adapter = described_class.new(url: 'http://app.com/flipper')
|
91
|
-
expect
|
91
|
+
expect {
|
92
92
|
adapter.get_multi([flipper[:feature_panel]])
|
93
|
-
|
93
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
@@ -100,9 +100,9 @@ RSpec.describe Flipper::Adapters::Http do
|
|
100
100
|
.to_return(status: 503, body: "", headers: {})
|
101
101
|
|
102
102
|
adapter = described_class.new(url: 'http://app.com/flipper')
|
103
|
-
expect
|
103
|
+
expect {
|
104
104
|
adapter.get_all
|
105
|
-
|
105
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
@@ -112,9 +112,75 @@ RSpec.describe Flipper::Adapters::Http do
|
|
112
112
|
.to_return(status: 503, body: "", headers: {})
|
113
113
|
|
114
114
|
adapter = described_class.new(url: 'http://app.com/flipper')
|
115
|
-
expect
|
115
|
+
expect {
|
116
116
|
adapter.features
|
117
|
-
|
117
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "#add" do
|
122
|
+
it "raises error when not successful" do
|
123
|
+
stub_request(:post, /app.com/)
|
124
|
+
.to_return(status: 503, body: "{}", headers: {})
|
125
|
+
|
126
|
+
adapter = described_class.new(url: 'http://app.com/flipper')
|
127
|
+
expect {
|
128
|
+
adapter.add(Flipper::Feature.new(:search, adapter))
|
129
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "#remove" do
|
134
|
+
it "raises error when not successful" do
|
135
|
+
stub_request(:delete, /app.com/)
|
136
|
+
.to_return(status: 503, body: "{}", headers: {})
|
137
|
+
|
138
|
+
adapter = described_class.new(url: 'http://app.com/flipper')
|
139
|
+
expect {
|
140
|
+
adapter.remove(Flipper::Feature.new(:search, adapter))
|
141
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe "#clear" do
|
146
|
+
it "raises error when not successful" do
|
147
|
+
stub_request(:delete, /app.com/)
|
148
|
+
.to_return(status: 503, body: "{}", headers: {})
|
149
|
+
|
150
|
+
adapter = described_class.new(url: 'http://app.com/flipper')
|
151
|
+
expect {
|
152
|
+
adapter.clear(Flipper::Feature.new(:search, adapter))
|
153
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "#enable" do
|
158
|
+
it "raises error when not successful" do
|
159
|
+
stub_request(:post, /app.com/)
|
160
|
+
.to_return(status: 503, body: "{}", headers: {})
|
161
|
+
|
162
|
+
adapter = described_class.new(url: 'http://app.com/flipper')
|
163
|
+
feature = Flipper::Feature.new(:search, adapter)
|
164
|
+
gate = feature.gate(:boolean)
|
165
|
+
thing = gate.wrap(true)
|
166
|
+
expect {
|
167
|
+
adapter.enable(feature, gate, thing)
|
168
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "#disable" do
|
173
|
+
it "raises error when not successful" do
|
174
|
+
stub_request(:delete, /app.com/)
|
175
|
+
.to_return(status: 503, body: "{}", headers: {})
|
176
|
+
|
177
|
+
adapter = described_class.new(url: 'http://app.com/flipper')
|
178
|
+
feature = Flipper::Feature.new(:search, adapter)
|
179
|
+
gate = feature.gate(:boolean)
|
180
|
+
thing = gate.wrap(false)
|
181
|
+
expect {
|
182
|
+
adapter.disable(feature, gate, thing)
|
183
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
118
184
|
end
|
119
185
|
end
|
120
186
|
|
@@ -11,6 +11,15 @@ RSpec.describe Flipper::Adapters::OperationLogger do
|
|
11
11
|
|
12
12
|
it_should_behave_like 'a flipper adapter'
|
13
13
|
|
14
|
+
it 'shows itself when inspect' do
|
15
|
+
subject.features
|
16
|
+
output = subject.inspect
|
17
|
+
expect(output).to match(/OperationLogger/)
|
18
|
+
expect(output).to match(/operation_logger/)
|
19
|
+
expect(output).to match(/@type=:features/)
|
20
|
+
expect(output).to match(/@adapter=#<Flipper::Adapters::Memory/)
|
21
|
+
end
|
22
|
+
|
14
23
|
it 'forwards missing methods to underlying adapter' do
|
15
24
|
adapter = Class.new do
|
16
25
|
def foo
|
data/spec/flipper_spec.rb
CHANGED
@@ -217,9 +217,9 @@ RSpec.describe Flipper do
|
|
217
217
|
expect(described_class.memoizing?).to eq(described_class.adapter.memoizing?)
|
218
218
|
end
|
219
219
|
|
220
|
-
it 'delegates sync stuff to instance and
|
221
|
-
expect
|
222
|
-
expect
|
220
|
+
it 'delegates sync stuff to instance and does nothing' do
|
221
|
+
expect(described_class.sync).to be(nil)
|
222
|
+
expect(described_class.sync_secret).to be(nil)
|
223
223
|
end
|
224
224
|
|
225
225
|
it 'delegates sync stuff to instance for Flipper::Cloud' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flipper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.20.
|
4
|
+
version: 0.20.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Nunemaker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-08 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -18,6 +18,7 @@ extensions: []
|
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
20
|
- ".codeclimate.yml"
|
21
|
+
- ".github/workflows/ci.yml"
|
21
22
|
- CODE_OF_CONDUCT.md
|
22
23
|
- Changelog.md
|
23
24
|
- Dockerfile
|
@@ -55,6 +56,7 @@ files:
|
|
55
56
|
- lib/flipper.rb
|
56
57
|
- lib/flipper/actor.rb
|
57
58
|
- lib/flipper/adapter.rb
|
59
|
+
- lib/flipper/adapters/dual_write.rb
|
58
60
|
- lib/flipper/adapters/http.rb
|
59
61
|
- lib/flipper/adapters/http/client.rb
|
60
62
|
- lib/flipper/adapters/http/error.rb
|
@@ -104,6 +106,7 @@ files:
|
|
104
106
|
- spec/fixtures/feature.json
|
105
107
|
- spec/flipper/actor_spec.rb
|
106
108
|
- spec/flipper/adapter_spec.rb
|
109
|
+
- spec/flipper/adapters/dual_write_spec.rb
|
107
110
|
- spec/flipper/adapters/http_spec.rb
|
108
111
|
- spec/flipper/adapters/instrumented_spec.rb
|
109
112
|
- spec/flipper/adapters/memoizable_spec.rb
|
@@ -166,9 +169,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
166
169
|
version: '0'
|
167
170
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
168
171
|
requirements:
|
169
|
-
- - "
|
172
|
+
- - ">="
|
170
173
|
- !ruby/object:Gem::Version
|
171
|
-
version:
|
174
|
+
version: '0'
|
172
175
|
requirements: []
|
173
176
|
rubygems_version: 3.0.3
|
174
177
|
signing_key:
|
@@ -178,6 +181,7 @@ test_files:
|
|
178
181
|
- spec/fixtures/feature.json
|
179
182
|
- spec/flipper/actor_spec.rb
|
180
183
|
- spec/flipper/adapter_spec.rb
|
184
|
+
- spec/flipper/adapters/dual_write_spec.rb
|
181
185
|
- spec/flipper/adapters/http_spec.rb
|
182
186
|
- spec/flipper/adapters/instrumented_spec.rb
|
183
187
|
- spec/flipper/adapters/memoizable_spec.rb
|