flipper 1.2.2 → 1.3.0.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -20,6 +20,8 @@ module Flipper
20
20
  instances.each {|_, instance| instance.stop }.clear
21
21
  end
22
22
 
23
+ MINIMUM_POLL_INTERVAL = 10
24
+
23
25
  def initialize(options = {})
24
26
  @thread = nil
25
27
  @pid = Process.pid
@@ -30,9 +32,9 @@ module Flipper
30
32
  @last_synced_at = Concurrent::AtomicFixnum.new(0)
31
33
  @adapter = Adapters::Memory.new(nil, threadsafe: true)
32
34
 
33
- if @interval < 1
34
- warn "Flipper::Cloud poll interval must be greater than or equal to 1 but was #{@interval}. Setting @interval to 1."
35
- @interval = 1
35
+ if @interval < MINIMUM_POLL_INTERVAL
36
+ warn "Flipper::Cloud poll interval must be greater than or equal to #{MINIMUM_POLL_INTERVAL} but was #{@interval}. Setting @interval to #{MINIMUM_POLL_INTERVAL}."
37
+ @interval = MINIMUM_POLL_INTERVAL
36
38
  end
37
39
 
38
40
  @start_automatically = options.fetch(:start_automatically, true)
@@ -64,8 +66,7 @@ module Flipper
64
66
  # you can instrument these using poller.flipper
65
67
  end
66
68
 
67
- sleep_interval = interval - (Concurrent.monotonic_time - start)
68
- sleep sleep_interval if sleep_interval.positive?
69
+ sleep interval
69
70
  end
70
71
  end
71
72
 
@@ -3,10 +3,8 @@ require "stringio"
3
3
 
4
4
  module Flipper
5
5
  module Serializers
6
- module Gzip
7
- module_function
8
-
9
- def serialize(source)
6
+ class Gzip
7
+ def self.serialize(source)
10
8
  return if source.nil?
11
9
  output = StringIO.new
12
10
  gz = Zlib::GzipWriter.new(output)
@@ -15,7 +13,7 @@ module Flipper
15
13
  output.string
16
14
  end
17
15
 
18
- def deserialize(source)
16
+ def self.deserialize(source)
19
17
  return if source.nil?
20
18
  Zlib::GzipReader.wrap(StringIO.new(source), &:read)
21
19
  end
@@ -2,15 +2,13 @@ require "json"
2
2
 
3
3
  module Flipper
4
4
  module Serializers
5
- module Json
6
- module_function
7
-
8
- def serialize(source)
5
+ class Json
6
+ def self.serialize(source)
9
7
  return if source.nil?
10
8
  JSON.generate(source)
11
9
  end
12
10
 
13
- def deserialize(source)
11
+ def self.deserialize(source)
14
12
  return if source.nil?
15
13
  JSON.parse(source)
16
14
  end
@@ -108,19 +108,19 @@ RSpec.shared_examples_for 'a flipper adapter' do
108
108
  actor22 = Flipper::Actor.new('22')
109
109
  actor_asdf = Flipper::Actor.new('asdf')
110
110
 
111
- expect(subject.enable(feature, actor_gate, Flipper::Types::Actor.new(actor22))).to eq(true)
112
- expect(subject.enable(feature, actor_gate, Flipper::Types::Actor.new(actor_asdf))).to eq(true)
111
+ expect(feature.enable(actor22)).to be(true)
112
+ expect(feature.enable(actor_asdf)).to be(true)
113
113
 
114
- result = subject.get(feature)
115
- expect(result[:actors]).to eq(Set['22', 'asdf'])
114
+ expect(feature).to be_enabled(actor22)
115
+ expect(feature).to be_enabled(actor_asdf)
116
116
 
117
- expect(subject.disable(feature, actor_gate, Flipper::Types::Actor.new(actor22))).to eq(true)
118
- result = subject.get(feature)
119
- expect(result[:actors]).to eq(Set['asdf'])
117
+ expect(feature.disable(actor22)).to be(true)
118
+ expect(feature).not_to be_enabled(actor22)
119
+ expect(feature).to be_enabled(actor_asdf)
120
120
 
121
- expect(subject.disable(feature, actor_gate, Flipper::Types::Actor.new(actor_asdf))).to eq(true)
122
- result = subject.get(feature)
123
- expect(result[:actors]).to eq(Set.new)
121
+ expect(feature.disable(actor_asdf)).to eq(true)
122
+ expect(feature).not_to be_enabled(actor22)
123
+ expect(feature).not_to be_enabled(actor_asdf)
124
124
  end
125
125
 
126
126
  it 'can enable, disable and get value for percentage of actors gate' do
@@ -182,9 +182,10 @@ RSpec.shared_examples_for 'a flipper adapter' do
182
182
  end
183
183
 
184
184
  it 'converts the actor value to a string' do
185
- expect(subject.enable(feature, actor_gate, Flipper::Types::Actor.new(Flipper::Actor.new(22)))).to eq(true)
186
- result = subject.get(feature)
187
- expect(result[:actors]).to eq(Set['22'])
185
+ actor = Flipper::Actor.new(22)
186
+ expect(feature).not_to be_enabled(actor)
187
+ feature.enable_actor actor
188
+ expect(feature).to be_enabled(actor)
188
189
  end
189
190
 
190
191
  it 'converts group value to a string' do
@@ -295,9 +296,9 @@ RSpec.shared_examples_for 'a flipper adapter' do
295
296
 
296
297
  it 'can double enable an actor without error' do
297
298
  actor = Flipper::Actor.new('Flipper::Actor;22')
298
- expect(subject.enable(feature, actor_gate, Flipper::Types::Actor.new(actor))).to eq(true)
299
- expect(subject.enable(feature, actor_gate, Flipper::Types::Actor.new(actor))).to eq(true)
300
- expect(subject.get(feature).fetch(:actors)).to eq(Set['Flipper::Actor;22'])
299
+ expect(feature.enable(actor)).to be(true)
300
+ expect(feature.enable(actor)).to be(true)
301
+ expect(feature).to be_enabled(actor)
301
302
  end
302
303
 
303
304
  it 'can double enable a group without error' do
@@ -104,19 +104,19 @@ module Flipper
104
104
  actor22 = Flipper::Actor.new('22')
105
105
  actor_asdf = Flipper::Actor.new('asdf')
106
106
 
107
- assert_equal true, @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(actor22))
108
- assert_equal true, @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(actor_asdf))
107
+ assert_equal true, @feature.enable(actor22)
108
+ assert_equal true, @feature.enable(actor_asdf)
109
109
 
110
- result = @adapter.get(@feature)
111
- assert_equal Set['22', 'asdf'], result[:actors]
110
+ assert @feature.enabled?(actor22)
111
+ assert @feature.enabled?(actor_asdf)
112
112
 
113
- assert true, @adapter.disable(@feature, @actor_gate, Flipper::Types::Actor.new(actor22))
114
- result = @adapter.get(@feature)
115
- assert_equal Set['asdf'], result[:actors]
113
+ assert_equal true, @feature.disable(actor22)
114
+ refute @feature.enabled?(actor22)
115
+ assert @feature.enabled?(actor_asdf)
116
116
 
117
- assert_equal true, @adapter.disable(@feature, @actor_gate, Flipper::Types::Actor.new(actor_asdf))
118
- result = @adapter.get(@feature)
119
- assert_equal Set.new, result[:actors]
117
+ assert_equal true, @feature.disable(actor_asdf)
118
+ refute @feature.enabled?(actor22)
119
+ refute @feature.enabled?(actor_asdf)
120
120
  end
121
121
 
122
122
  def test_can_enable_disable_get_value_for_percentage_of_actors_gate
@@ -178,10 +178,10 @@ module Flipper
178
178
  end
179
179
 
180
180
  def test_converts_the_actor_value_to_a_string
181
- assert_equal true,
182
- @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(Flipper::Actor.new(22)))
183
- result = @adapter.get(@feature)
184
- assert_equal Set['22'], result[:actors]
181
+ actor = Flipper::Actor.new(22)
182
+ refute @feature.enabled?(actor)
183
+ @feature.enable_actor actor
184
+ assert @feature.enabled?(actor)
185
185
  end
186
186
 
187
187
  def test_converts_group_value_to_a_string
@@ -292,9 +292,9 @@ module Flipper
292
292
 
293
293
  def test_can_double_enable_an_actor_without_error
294
294
  actor = Flipper::Actor.new('Flipper::Actor;22')
295
- assert_equal true, @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(actor))
296
- assert_equal true, @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(actor))
297
- assert_equal Set['Flipper::Actor;22'], @adapter.get(@feature).fetch(:actors)
295
+ assert_equal true, @feature.enable(actor)
296
+ assert_equal true, @feature.enable(actor)
297
+ assert @feature.enabled?(actor)
298
298
  end
299
299
 
300
300
  def test_can_double_enable_a_group_without_error
@@ -3,8 +3,8 @@ require "flipper/serializers/json"
3
3
  require "flipper/serializers/gzip"
4
4
 
5
5
  module Flipper
6
- module Typecast
7
- TruthMap = {
6
+ class Typecast
7
+ TRUTH_MAP = {
8
8
  true => true,
9
9
  1 => true,
10
10
  'true' => true,
@@ -15,7 +15,7 @@ module Flipper
15
15
  #
16
16
  # Returns true or false.
17
17
  def self.to_boolean(value)
18
- !!TruthMap[value]
18
+ !!TRUTH_MAP[value]
19
19
  end
20
20
 
21
21
  # Internal: Convert value to an integer.
@@ -1,5 +1,5 @@
1
1
  module Flipper
2
- VERSION = '1.2.2'.freeze
2
+ VERSION = '1.3.0.pre'.freeze
3
3
 
4
4
  REQUIRED_RUBY_VERSION = '2.6'.freeze
5
5
  NEXT_REQUIRED_RUBY_VERSION = '3.0'.freeze
data/lib/flipper.rb CHANGED
@@ -174,6 +174,7 @@ end
174
174
 
175
175
  require 'flipper/actor'
176
176
  require 'flipper/adapter'
177
+ require 'flipper/adapters/wrapper'
177
178
  require 'flipper/adapters/memoizable'
178
179
  require 'flipper/adapters/memory'
179
180
  require 'flipper/adapters/strict'
data/package-lock.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "flipper",
3
+ "lockfileVersion": 3,
4
+ "requires": true,
5
+ "packages": {
6
+ "": {
7
+ "hasInstallScript": true,
8
+ "dependencies": {
9
+ "@popperjs/core": "^2.11.8",
10
+ "bootstrap": "^5.3.3"
11
+ }
12
+ },
13
+ "node_modules/@popperjs/core": {
14
+ "version": "2.11.8",
15
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
16
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
17
+ "funding": {
18
+ "type": "opencollective",
19
+ "url": "https://opencollective.com/popperjs"
20
+ }
21
+ },
22
+ "node_modules/bootstrap": {
23
+ "version": "5.3.3",
24
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
25
+ "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
26
+ "funding": [
27
+ {
28
+ "type": "github",
29
+ "url": "https://github.com/sponsors/twbs"
30
+ },
31
+ {
32
+ "type": "opencollective",
33
+ "url": "https://opencollective.com/bootstrap"
34
+ }
35
+ ],
36
+ "peerDependencies": {
37
+ "@popperjs/core": "^2.11.8"
38
+ }
39
+ }
40
+ }
41
+ }
data/package.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "private": true,
3
+ "dependencies": {
4
+ "@popperjs/core": "^2.11.8",
5
+ "bootstrap": "^5.3.3"
6
+ },
7
+ "scripts": {
8
+ "postinstall": "script/vendor-assets"
9
+ }
10
+ }
@@ -1,6 +1,15 @@
1
1
  require 'flipper/adapters/http'
2
2
  require 'flipper/adapters/pstore'
3
- require 'rack/handler/webrick'
3
+
4
+ rack_handler = begin
5
+ # Rack 3+
6
+ require 'rackup/handler/webrick'
7
+ Rackup::Handler::WEBrick
8
+ rescue LoadError
9
+ require 'rack/handler/webrick'
10
+ Rack::Handler::WEBrick
11
+ end
12
+
4
13
 
5
14
  FLIPPER_SPEC_API_PORT = ENV.fetch('FLIPPER_SPEC_API_PORT', 9001).to_i
6
15
 
@@ -36,7 +45,7 @@ RSpec.describe Flipper::Adapters::Http do
36
45
  ],
37
46
  }
38
47
  @server = WEBrick::HTTPServer.new(server_options)
39
- @server.mount '/', Rack::Handler::WEBrick, app
48
+ @server.mount '/', rack_handler, app
40
49
 
41
50
  Thread.new { @server.start }
42
51
  Timeout.timeout(1) { :wait until @started }
@@ -1,13 +1,33 @@
1
1
  require "flipper/cli"
2
2
 
3
3
  RSpec.describe Flipper::CLI do
4
+ let(:stdout) { StringIO.new }
5
+ let(:stderr) { StringIO.new }
6
+ let(:cli) { Flipper::CLI.new(stdout: stdout, stderr: stderr) }
7
+
8
+ before do
9
+ # Prentend stdout/stderr a TTY to test colorization
10
+ allow(stdout).to receive(:tty?).and_return(true)
11
+ allow(stderr).to receive(:tty?).and_return(true)
12
+ end
13
+
4
14
  # Infer the command from the description
5
15
  subject(:argv) do
6
16
  descriptions = self.class.parent_groups.map {|g| g.metadata[:description_args] }.reverse.flatten.drop(1)
7
17
  descriptions.map { |arg| Shellwords.split(arg) }.flatten
8
18
  end
9
19
 
10
- subject { run argv }
20
+ subject do
21
+ status = 0
22
+
23
+ begin
24
+ cli.run(argv)
25
+ rescue SystemExit => e
26
+ status = e.status
27
+ end
28
+
29
+ OpenStruct.new(status: status, stdout: stdout.string, stderr: stderr.string)
30
+ end
11
31
 
12
32
  before do
13
33
  ENV["FLIPPER_REQUIRE"] = "./spec/fixtures/environment"
@@ -141,49 +161,4 @@ RSpec.describe Flipper::CLI do
141
161
  it { should have_attributes(status: 0, stdout: /enabled.*admins/m) }
142
162
  end
143
163
  end
144
-
145
- context "bundler is not installed" do
146
- let(:argv) { "list" }
147
-
148
- around do |example|
149
- original_bundler = Bundler
150
- begin
151
- Object.send(:remove_const, :Bundler)
152
- example.run
153
- ensure
154
- Object.const_set(:Bundler, original_bundler)
155
- end
156
- end
157
-
158
- it "should not raise an error" do
159
- Flipper.enable(:enabled_feature)
160
- Flipper.enable_group(:enabled_groups, :admins)
161
- Flipper.add(:disabled_feature)
162
-
163
- expect(subject).to have_attributes(status: 0, stdout: /enabled_feature.*enabled_groups.*disabled_feature/m)
164
- end
165
- end
166
-
167
- def run(argv)
168
- original_stdout = $stdout
169
- original_stderr = $stderr
170
-
171
- $stdout = StringIO.new
172
- $stderr = StringIO.new
173
- status = 0
174
-
175
- # Prentend this a TTY so we can test colorization
176
- allow($stdout).to receive(:tty?).and_return(true)
177
-
178
- begin
179
- Flipper::CLI.run(argv)
180
- rescue SystemExit => e
181
- status = e.status
182
- end
183
-
184
- OpenStruct.new(status: status, stdout: $stdout.string, stderr: $stderr.string)
185
- ensure
186
- $stdout = original_stdout
187
- $stderr = original_stderr
188
- end
189
164
  end
@@ -20,7 +20,8 @@ RSpec.describe Flipper::Cloud::Configuration do
20
20
  it "can set instrumenter" do
21
21
  instrumenter = Object.new
22
22
  instance = described_class.new(required_options.merge(instrumenter: instrumenter))
23
- expect(instance.instrumenter).to be(instrumenter)
23
+ expect(instance.instrumenter).to be_a(Flipper::Cloud::Telemetry::Instrumenter)
24
+ expect(instance.instrumenter.instrumenter).to be(instrumenter)
24
25
  end
25
26
 
26
27
  it "can set read_timeout" do
@@ -2,6 +2,12 @@ require 'flipper/cloud/telemetry'
2
2
  require 'flipper/cloud/configuration'
3
3
 
4
4
  RSpec.describe Flipper::Cloud::Telemetry do
5
+ before do
6
+ # Stub polling for features.
7
+ stub_request(:get, "https://www.flippercloud.io/adapter/features?exclude_gate_names=true").
8
+ to_return(status: 200, body: "{}")
9
+ end
10
+
5
11
  it "phones home and does not update telemetry interval if missing" do
6
12
  stub = stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
7
13
  to_return(status: 200, body: "{}")
@@ -42,6 +48,52 @@ RSpec.describe Flipper::Cloud::Telemetry do
42
48
  expect(stub).to have_been_requested
43
49
  end
44
50
 
51
+ it "phones home and requests shutdown if telemetry-shutdown header is true" do
52
+ stub = stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
53
+ to_return(status: 404, body: "{}", headers: {"telemetry-shutdown" => "true"})
54
+
55
+ output = StringIO.new
56
+ cloud_configuration = Flipper::Cloud::Configuration.new(
57
+ token: "test",
58
+ logger: Logger.new(output),
59
+ logging_enabled: true,
60
+ )
61
+
62
+ # Record some telemetry and stop the threads so we submit a response.
63
+ telemetry = described_class.new(cloud_configuration)
64
+ telemetry.record(Flipper::Feature::InstrumentationName, {
65
+ operation: :enabled?,
66
+ feature_name: :foo,
67
+ result: true,
68
+ })
69
+ telemetry.stop
70
+ expect(stub).to have_been_requested
71
+ expect(output.string).to match(/action=telemetry_shutdown message=The server has requested that telemetry be shut down./)
72
+ end
73
+
74
+ it "phones home and does not shutdown if telemetry shutdown header is missing" do
75
+ stub = stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
76
+ to_return(status: 404, body: "{}", headers: {})
77
+
78
+ output = StringIO.new
79
+ cloud_configuration = Flipper::Cloud::Configuration.new(
80
+ token: "test",
81
+ logger: Logger.new(output),
82
+ logging_enabled: true,
83
+ )
84
+
85
+ # Record some telemetry and stop the threads so we submit a response.
86
+ telemetry = described_class.new(cloud_configuration)
87
+ telemetry.record(Flipper::Feature::InstrumentationName, {
88
+ operation: :enabled?,
89
+ feature_name: :foo,
90
+ result: true,
91
+ })
92
+ telemetry.stop
93
+ expect(stub).to have_been_requested
94
+ expect(output.string).not_to match(/action=telemetry_shutdown message=The server has requested that telemetry be shut down./)
95
+ end
96
+
45
97
  it "can update telemetry interval from error" do
46
98
  stub = stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
47
99
  to_return(status: 500, body: "{}", headers: {"telemetry-interval" => "120"})
@@ -36,7 +36,8 @@ RSpec.describe Flipper::Cloud do
36
36
  expect(client.uri.host).to eq('www.flippercloud.io')
37
37
  expect(client.uri.path).to eq('/adapter')
38
38
  expect(client.headers["flipper-cloud-token"]).to eq(token)
39
- expect(@instance.instrumenter).to be(Flipper::Instrumenters::Noop)
39
+ expect(@instance.instrumenter).to be_a(Flipper::Cloud::Telemetry::Instrumenter)
40
+ expect(@instance.instrumenter.instrumenter).to be(Flipper::Instrumenters::Noop)
40
41
  end
41
42
  end
42
43
 
@@ -62,7 +63,8 @@ RSpec.describe Flipper::Cloud do
62
63
  it 'can set instrumenter' do
63
64
  instrumenter = Flipper::Instrumenters::Memory.new
64
65
  instance = described_class.new(token: 'asdf', instrumenter: instrumenter)
65
- expect(instance.instrumenter).to be(instrumenter)
66
+ expect(instance.instrumenter).to be_a(Flipper::Cloud::Telemetry::Instrumenter)
67
+ expect(instance.instrumenter.instrumenter).to be(instrumenter)
66
68
  end
67
69
 
68
70
  it 'allows wrapping adapter with another adapter like the instrumenter' do
@@ -4,8 +4,10 @@ require 'flipper/engine'
4
4
  RSpec.describe Flipper::Engine do
5
5
  let(:application) do
6
6
  Class.new(Rails::Application) do
7
+ config.load_defaults Rails::VERSION::STRING.to_f
7
8
  config.eager_load = false
8
9
  config.logger = ActiveSupport::Logger.new($stdout)
10
+ config.active_support.remove_deprecated_time_with_zone_name = false
9
11
  end.instance
10
12
  end
11
13
 
@@ -234,7 +236,8 @@ RSpec.describe Flipper::Engine do
234
236
  application.initialize!
235
237
 
236
238
  expect(Flipper.instance).to be_a(Flipper::Cloud::DSL)
237
- expect(Flipper.instance.instrumenter).to be(ActiveSupport::Notifications)
239
+ expect(Flipper.instance.instrumenter).to be_a(Flipper::Cloud::Telemetry::Instrumenter)
240
+ expect(Flipper.instance.instrumenter.instrumenter).to be(ActiveSupport::Notifications)
238
241
  end
239
242
 
240
243
  context "with CLOUD_SYNC_SECRET" do
@@ -458,7 +458,7 @@ RSpec.describe Flipper::Middleware::Memoizer do
458
458
  logged_memory = Flipper::Adapters::OperationLogger.new(memory)
459
459
  cache = ActiveSupport::Cache::MemoryStore.new
460
460
  cache.clear
461
- cached = Flipper::Adapters::ActiveSupportCacheStore.new(logged_memory, cache, expires_in: 10)
461
+ cached = Flipper::Adapters::ActiveSupportCacheStore.new(logged_memory, cache)
462
462
  logged_cached = Flipper::Adapters::OperationLogger.new(cached)
463
463
  memo = {}
464
464
  flipper = Flipper.new(logged_cached)
@@ -471,15 +471,18 @@ RSpec.describe Flipper::Middleware::Memoizer do
471
471
 
472
472
  get '/', {}, 'flipper' => flipper
473
473
  expect(logged_cached.count(:get_all)).to be(1)
474
- expect(logged_memory.count(:get_all)).to be(1)
474
+ expect(logged_memory.count(:features)).to be(1)
475
+ expect(logged_memory.count(:get_multi)).to be(1)
475
476
 
476
477
  get '/', {}, 'flipper' => flipper
477
478
  expect(logged_cached.count(:get_all)).to be(2)
478
- expect(logged_memory.count(:get_all)).to be(1)
479
+ expect(logged_memory.count(:features)).to be(1)
480
+ expect(logged_memory.count(:get_multi)).to be(1)
479
481
 
480
482
  get '/', {}, 'flipper' => flipper
481
483
  expect(logged_cached.count(:get_all)).to be(3)
482
- expect(logged_memory.count(:get_all)).to be(1)
484
+ expect(logged_memory.count(:features)).to be(1)
485
+ expect(logged_memory.count(:get_multi)).to be(1)
483
486
  end
484
487
  end
485
488
  end
@@ -0,0 +1,8 @@
1
+ if ENV["CI"] || ENV["FAIL_ON_OUTPUT"]
2
+ RSpec.configure do |config|
3
+ config.around do |example|
4
+ output = silence { example.run }
5
+ fail "Use `silence { }` to avoid printing to STDOUT/STDERR\n#{output}" unless output.empty?
6
+ end
7
+ end
8
+ end
@@ -1,6 +1,7 @@
1
1
  require 'ice_age'
2
2
  require 'json'
3
3
  require 'rack/test'
4
+ require 'rack/session'
4
5
 
5
6
  module SpecHelpers
6
7
  extend self
@@ -12,7 +13,7 @@ module SpecHelpers
12
13
 
13
14
  def build_app(flipper, options = {})
14
15
  Flipper::UI.app(flipper, options) do |builder|
15
- builder.use Rack::Session::Cookie, secret: 'test'
16
+ builder.use Rack::Session::Cookie, secret: 'test' * 16 # Rack 3+ wants a 64-character secret
16
17
  end
17
18
  end
18
19
 
@@ -29,7 +29,7 @@ end
29
29
 
30
30
  class TestHelpTest < ActionDispatch::SystemTestCase
31
31
  # Any driver that runs the app in a separate thread will test what we want here.
32
- driven_by :cuprite, options: { process_timeout: 30 }
32
+ driven_by :cuprite, options: { process_timeout: 60 }
33
33
 
34
34
  setup do
35
35
  # Reconfigure Flipper since other tests change the adapter.
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: 1.2.2
4
+ version: 1.3.0.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Nunemaker
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-01-19 00:00:00.000000000 Z
11
+ date: 2024-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -90,6 +90,7 @@ files:
90
90
  - lib/flipper/actor.rb
91
91
  - lib/flipper/adapter.rb
92
92
  - lib/flipper/adapter_builder.rb
93
+ - lib/flipper/adapters/cache_base.rb
93
94
  - lib/flipper/adapters/dual_write.rb
94
95
  - lib/flipper/adapters/failover.rb
95
96
  - lib/flipper/adapters/failsafe.rb
@@ -109,6 +110,7 @@ files:
109
110
  - lib/flipper/adapters/sync/feature_synchronizer.rb
110
111
  - lib/flipper/adapters/sync/interval_synchronizer.rb
111
112
  - lib/flipper/adapters/sync/synchronizer.rb
113
+ - lib/flipper/adapters/wrapper.rb
112
114
  - lib/flipper/cli.rb
113
115
  - lib/flipper/cloud.rb
114
116
  - lib/flipper/cloud/configuration.rb
@@ -193,6 +195,8 @@ files:
193
195
  - lib/generators/flipper/templates/update/migrations/01_create_flipper_tables.rb.erb
194
196
  - lib/generators/flipper/templates/update/migrations/02_change_flipper_gates_value_to_text.rb.erb
195
197
  - lib/generators/flipper/update_generator.rb
198
+ - package-lock.json
199
+ - package.json
196
200
  - spec/fixtures/environment.rb
197
201
  - spec/fixtures/feature.json
198
202
  - spec/fixtures/flipper_pstore_1679087600.json
@@ -287,6 +291,7 @@ files:
287
291
  - spec/spec_helper.rb
288
292
  - spec/support/actor_names.yml
289
293
  - spec/support/descriptions.yml
294
+ - spec/support/fail_on_output.rb
290
295
  - spec/support/fake_backoff_policy.rb
291
296
  - spec/support/fake_udp_socket.rb
292
297
  - spec/support/skippable.rb
@@ -306,7 +311,7 @@ metadata:
306
311
  homepage_uri: https://www.flippercloud.io
307
312
  source_code_uri: https://github.com/flippercloud/flipper
308
313
  bug_tracker_uri: https://github.com/flippercloud/flipper/issues
309
- changelog_uri: https://github.com/flippercloud/flipper/releases/tag/v1.2.2
314
+ changelog_uri: https://github.com/flippercloud/flipper/releases/tag/v1.3.0.pre
310
315
  post_install_message:
311
316
  rdoc_options: []
312
317
  require_paths:
@@ -421,6 +426,7 @@ test_files:
421
426
  - spec/spec_helper.rb
422
427
  - spec/support/actor_names.yml
423
428
  - spec/support/descriptions.yml
429
+ - spec/support/fail_on_output.rb
424
430
  - spec/support/fake_backoff_policy.rb
425
431
  - spec/support/fake_udp_socket.rb
426
432
  - spec/support/skippable.rb