flipper 0.20.0.beta1 → 0.20.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b11913845dd63e7156086ce778927c1986694c48472dbb5b779155318c6eeab6
4
- data.tar.gz: bc1bf62134ccd35ef86a5373f8105a9d511e3061e5666e55b0183fd20437a74f
3
+ metadata.gz: df90e63870767caa4d32363658e4515d0822a1453cc3a5325d7dbf932c326b59
4
+ data.tar.gz: 125256e9cdee5bd0e2bfe065203140eb7fa015cac56043711c7eb45bd5421aa8
5
5
  SHA512:
6
- metadata.gz: ec24f067db0fc80d5d3e9f00ce1b87b51d5111f89e9d35842d044347dbbbfc836663e972e7eb0c640713e375938e10a7d70769af27c87aea82270eba29432ca0
7
- data.tar.gz: fb56eb175c3067f1b301d5875818b0c0a372f6e11da7e68c88337ab2660c8a7cfa20c027e8862cfc9aca126c5bd4b424dc962b846e2c812932400cc0f9592855
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
@@ -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
@@ -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/initializer/flipper.rb)
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
- config.middleware.use Flipper::Middleware::Memoizer
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
- config.middleware.use Flipper::Middleware::SetupEnv, -> { Flipper.new(...) }
27
- config.middleware.use Flipper::Middleware::Memoizer
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
- config.middleware.use Flipper::Middleware::Memoizer,
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
- config.middleware.use Flipper::Middleware::Memoizer,
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
- config.middleware.use Flipper::Middleware::Memoizer,
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
- case gate.key
107
- when :percentage_of_actors, :percentage_of_time
108
- @client.post("/features/#{feature.key}/#{gate.key}#{query_string}", body)
109
- else
110
- @client.delete("/features/#{feature.key}/#{gate.key}#{query_string}", body)
111
- end
112
- response.is_a?(Net::HTTPOK)
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
- when :boolean
125
- {}
126
- when :groups
127
- { name: value }
128
- when :actors
129
- { flipper_id: value }
130
- when :percentage_of_actors, :percentage_of_time
131
- { percentage: value }
132
- else
133
- raise "#{gate.key} is not a valid flipper gate key"
134
- end
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 adpater that should serve writes and update
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 milliseconds between invocations 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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Flipper
2
- VERSION = '0.20.0.beta1'.freeze
2
+ VERSION = '0.20.2'.freeze
3
3
  end
@@ -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 do
79
+ expect {
80
80
  adapter.get(flipper[:feature_panel])
81
- end.to raise_error(Flipper::Adapters::Http::Error)
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 do
91
+ expect {
92
92
  adapter.get_multi([flipper[:feature_panel]])
93
- end.to raise_error(Flipper::Adapters::Http::Error)
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 do
103
+ expect {
104
104
  adapter.get_all
105
- end.to raise_error(Flipper::Adapters::Http::Error)
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 do
115
+ expect {
116
116
  adapter.features
117
- end.to raise_error(Flipper::Adapters::Http::Error)
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
@@ -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 errors for OSS' do
221
- expect { described_class.sync }.to raise_error(NoMethodError)
222
- expect { described_class.sync_secret }.to raise_error(NoMethodError)
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.0.beta1
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: 2020-12-11 00:00:00.000000000 Z
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: 1.3.1
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