flipper 1.3.0.pre → 1.3.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b71c902902ad6e2d1b19483496c257b82a67b0bdf37784c986ac24b41068000e
4
- data.tar.gz: df8f571c809de456976c5b8f2adae3e1c2dc443589211a4c83a89130620d8219
3
+ metadata.gz: 29d129fae3234365cc1a38cbe05d41ee04ce2d7bd350ae05f2a3b1546ec757b2
4
+ data.tar.gz: 184b524b7d42865e223d6018a4ff366553e54f1395723ded4ff1804d6e27f325
5
5
  SHA512:
6
- metadata.gz: d38bf2b382419e985d9a8bd4f6f650b27d880952f1cecfe2943e3cc2ce72e09843a8bf45b4cd833af40865b5438a3486d7dba72d6c02d197c7a98423d33dd12e
7
- data.tar.gz: c67f1b896463545dd60582652d6eaffb7cf4b7e544a7d231bb83a2cc91e2c674c9de13f8a8a4a2e7966fa889792b6a34338782718c49c84a38ace66d7bde9deb
6
+ metadata.gz: a5776ffb099fb4908635146bccd533bdae4cafe708946d1d8f9f3e0876f9bbc94d29971fd938fd471fc463dab41c2deb11c1b2f56e873fdbe0151b235974e42e
7
+ data.tar.gz: 649abbcce3d804cdc141fbd74d6279ddb0a54d4ed506b6c594182adcf44ce459b4e9c3aa74005dc772da54bfcb9bcf8f324a3565c06493d090885c1f53339ed6
@@ -70,7 +70,7 @@ jobs:
70
70
  - name: Setup memcached
71
71
  uses: KeisukeYamashita/memcached-actions@v1
72
72
  - name: Start MongoDB
73
- uses: supercharge/mongodb-github-action@v1.10.0
73
+ uses: supercharge/mongodb-github-action@1.11.0
74
74
  with:
75
75
  mongodb-version: 4.0
76
76
  - name: Check out repository code
@@ -52,7 +52,7 @@ jobs:
52
52
  - name: Setup memcached
53
53
  uses: KeisukeYamashita/memcached-actions@v1
54
54
  - name: Start MongoDB
55
- uses: supercharge/mongodb-github-action@v1.10.0
55
+ uses: supercharge/mongodb-github-action@1.11.0
56
56
  with:
57
57
  mongodb-version: 4.0
58
58
  - name: Check out repository code
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  [![Flipper Mark](docs/images/banner.jpg)](https://www.flippercloud.io)
2
2
 
3
- [Website](https://flippercloud.io?utm_source=oss&utm_medium=readme&utm_campaign=website_link) | [Documentation](https://flippercloud.io/docs?utm_source=oss&utm_medium=readme&utm_campaign=docs_link) | [Examples](examples) | [Twitter](https://twitter.com/flipper_cloud) | [Ruby.social](https://ruby.social/@flipper)
3
+ [Website](https://flippercloud.io?utm_source=oss&utm_medium=readme&utm_campaign=website_link) | [Documentation](https://flippercloud.io/docs?utm_source=oss&utm_medium=readme&utm_campaign=docs_link) | [Examples](examples) | [Chat](https://chat.flippercloud.io/join/xjHq-aJsA-BeZH) | [Twitter](https://twitter.com/flipper_cloud) | [Ruby.social](https://ruby.social/@flipper)
4
4
 
5
5
  # Flipper
6
6
 
@@ -112,3 +112,4 @@ We also have a [free plan](https://www.flippercloud.io?utm_source=oss&utm_medium
112
112
  | ![@thetimbanks](https://avatars1.githubusercontent.com/u/471801?s=64) | [@thetimbanks](https://github.com/thetimbanks) | ui |
113
113
  | ![@lazebny](https://avatars1.githubusercontent.com/u/6276766?s=64) | [@lazebny](https://github.com/lazebny) | docker |
114
114
  | ![@pagertree](https://avatars.githubusercontent.com/u/24941240?s=64) | [@pagertree](https://github.com/pagertree) | sponsor |
115
+ | ![@kdaigle](https://avatars.githubusercontent.com/u/2501?s=64) | [@kdaigle](https://github.com/kdaigle) | sponsor |
data/Rakefile CHANGED
@@ -17,7 +17,7 @@ end
17
17
 
18
18
  desc 'Tags version, pushes to remote, and pushes gem'
19
19
  task release: :build do
20
- sh 'git', 'tag', "v#{Flipper::VERSION}"
20
+ # sh 'git', 'tag', "v#{Flipper::VERSION}"
21
21
  sh 'git push origin main'
22
22
  sh "git push origin v#{Flipper::VERSION}"
23
23
  puts "\nWhat OTP code should be used?"
Binary file
@@ -3,7 +3,7 @@ module Flipper
3
3
  #
4
4
  # adapter = Flipper::AdapterBuilder.new do
5
5
  # use Flipper::Adapters::Strict
6
- # use Flipper::Adapters::Memoizer
6
+ # use Flipper::Adapters::Memoizable
7
7
  # store Flipper::Adapters::Memory
8
8
  # end.to_adapter
9
9
  #
@@ -0,0 +1,28 @@
1
+ module Flipper
2
+ module Adapters
3
+ class ActorLimit < Wrapper
4
+ LimitExceeded = Class.new(Flipper::Error)
5
+
6
+ attr_reader :limit
7
+
8
+ def initialize(adapter, limit = 100)
9
+ super(adapter)
10
+ @limit = limit
11
+ end
12
+
13
+ def enable(feature, gate, resource)
14
+ if gate.is_a?(Flipper::Gates::Actor) && over_limit?(feature)
15
+ raise LimitExceeded, "Actor limit of #{@limit} exceeded for feature #{feature.key}. See https://www.flippercloud.io/docs/features/actors#limitations"
16
+ else
17
+ super
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def over_limit?(feature)
24
+ feature.actors_value.size >= @limit
25
+ end
26
+ end
27
+ end
28
+ end
@@ -25,6 +25,7 @@ module Flipper
25
25
  log: ENV.fetch('FLIPPER_LOG', 'true').casecmp('true').zero?,
26
26
  cloud_path: "_flipper",
27
27
  strict: default_strict_value,
28
+ actor_limit: ENV["FLIPPER_ACTOR_LIMIT"]&.to_i || 100,
28
29
  test_help: Flipper::Typecast.to_boolean(ENV["FLIPPER_TEST_HELP"] || Rails.env.test?),
29
30
  )
30
31
  end
@@ -65,13 +66,12 @@ module Flipper
65
66
  end
66
67
  end
67
68
 
68
- initializer "flipper.strict", after: :load_config_initializers do |app|
69
+ initializer "flipper.adapters", after: :load_config_initializers do |app|
69
70
  flipper = app.config.flipper
70
71
 
71
- if flipper.strict
72
- Flipper.configure do |config|
73
- config.use Flipper::Adapters::Strict, flipper.strict
74
- end
72
+ Flipper.configure do |config|
73
+ config.use Flipper::Adapters::Strict, flipper.strict if flipper.strict
74
+ config.use Flipper::Adapters::ActorLimit, flipper.actor_limit if flipper.actor_limit
75
75
  end
76
76
  end
77
77
 
@@ -1,5 +1,5 @@
1
1
  module Flipper
2
- VERSION = '1.3.0.pre'.freeze
2
+ VERSION = '1.3.1'.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
@@ -175,10 +175,11 @@ end
175
175
  require 'flipper/actor'
176
176
  require 'flipper/adapter'
177
177
  require 'flipper/adapters/wrapper'
178
+ require 'flipper/adapters/actor_limit'
179
+ require 'flipper/adapters/instrumented'
178
180
  require 'flipper/adapters/memoizable'
179
181
  require 'flipper/adapters/memory'
180
182
  require 'flipper/adapters/strict'
181
- require 'flipper/adapters/instrumented'
182
183
  require 'flipper/adapter_builder'
183
184
  require 'flipper/configuration'
184
185
  require 'flipper/dsl'
@@ -0,0 +1,20 @@
1
+ require "flipper/adapters/actor_limit"
2
+
3
+ RSpec.describe Flipper::Adapters::ActorLimit do
4
+ it_should_behave_like 'a flipper adapter' do
5
+ let(:limit) { 5 }
6
+ let(:adapter) { Flipper::Adapters::ActorLimit.new(Flipper::Adapters::Memory.new, limit) }
7
+
8
+ subject { adapter }
9
+
10
+ describe '#enable' do
11
+ it "fails when limit exceeded" do
12
+ 5.times { |i| feature.enable Flipper::Actor.new("User;#{i}") }
13
+
14
+ expect {
15
+ feature.enable Flipper::Actor.new("User;6")
16
+ }.to raise_error(Flipper::Adapters::ActorLimit::LimitExceeded)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -29,13 +29,13 @@ RSpec.describe Flipper::Adapters::Strict do
29
29
 
30
30
  context "#get" do
31
31
  it "raises an error for unknown feature" do
32
- expect(silence { subject.get(feature) }).to match(/Could not find feature "unknown"/)
32
+ expect(capture_output { subject.get(feature) }).to match(/Could not find feature "unknown"/)
33
33
  end
34
34
  end
35
35
 
36
36
  context "#get_multi" do
37
37
  it "raises an error for unknown feature" do
38
- expect(silence { subject.get_multi([feature]) }).to match(/Could not find feature "unknown"/)
38
+ expect(capture_output { subject.get_multi([feature]) }).to match(/Could not find feature "unknown"/)
39
39
  end
40
40
  end
41
41
  end
@@ -29,7 +29,7 @@ RSpec.describe Flipper::Engine do
29
29
 
30
30
  let(:config) { application.config.flipper }
31
31
 
32
- subject { application.initialize! }
32
+ subject { SpecHelpers.silence { application.initialize! } }
33
33
 
34
34
  shared_examples 'config.strict' do
35
35
  let(:adapter) { Flipper.adapter.adapter }
@@ -53,7 +53,7 @@ RSpec.describe Flipper::Engine do
53
53
  ENV['FLIPPER_STRICT'] = 'false'
54
54
  subject
55
55
  expect(config.strict).to eq(false)
56
- expect(adapter).to be_instance_of(Flipper::Adapters::Memory)
56
+ expect(adapter).not_to be_instance_of(Flipper::Adapters::Strict)
57
57
  end
58
58
 
59
59
  [true, :raise, :warn].each do |value|
@@ -69,7 +69,7 @@ RSpec.describe Flipper::Engine do
69
69
  initializer { config.strict = false }
70
70
  subject
71
71
  expect(config.strict).to eq(false)
72
- expect(adapter).to be_instance_of(Flipper::Adapters::Memory)
72
+ expect(adapter).not_to be_instance_of(Flipper::Adapters::Strict)
73
73
  end
74
74
 
75
75
  it "defaults to strict=:warn in RAILS_ENV=development" do
@@ -85,7 +85,7 @@ RSpec.describe Flipper::Engine do
85
85
  expect(Rails.env).to eq(env)
86
86
  subject
87
87
  expect(config.strict).to eq(false)
88
- expect(adapter).to be_instance_of(Flipper::Adapters::Memory)
88
+ expect(adapter).not_to be_instance_of(Flipper::Adapters::Strict)
89
89
  end
90
90
  end
91
91
 
@@ -233,7 +233,7 @@ RSpec.describe Flipper::Engine do
233
233
  it "initializes cloud configuration" do
234
234
  stub_request(:get, /flippercloud\.io/).to_return(status: 200, body: "{}")
235
235
 
236
- application.initialize!
236
+ silence { application.initialize! }
237
237
 
238
238
  expect(Flipper.instance).to be_a(Flipper::Cloud::DSL)
239
239
  expect(Flipper.instance.instrumenter).to be_a(Flipper::Cloud::Telemetry::Instrumenter)
@@ -263,7 +263,7 @@ RSpec.describe Flipper::Engine do
263
263
  }
264
264
 
265
265
  it "configures webhook app" do
266
- application.initialize!
266
+ silence { application.initialize! }
267
267
 
268
268
  stub = stub_request(:get, "https://www.flippercloud.io/adapter/features?exclude_gate_names=true").with({
269
269
  headers: { "flipper-cloud-token" => ENV["FLIPPER_CLOUD_TOKEN"] },
@@ -278,7 +278,7 @@ RSpec.describe Flipper::Engine do
278
278
 
279
279
  context "without CLOUD_SYNC_SECRET" do
280
280
  it "does not configure webhook app" do
281
- application.initialize!
281
+ silence { application.initialize! }
282
282
 
283
283
  post "/_flipper"
284
284
  expect(last_response.status).to eq(404)
@@ -288,7 +288,7 @@ RSpec.describe Flipper::Engine do
288
288
  context "without FLIPPER_CLOUD_TOKEN" do
289
289
  it "gracefully skips configuring webhook app" do
290
290
  ENV["FLIPPER_CLOUD_TOKEN"] = nil
291
- application.initialize!
291
+ silence { application.initialize! }
292
292
  expect(Flipper.instance).to be_a(Flipper::DSL)
293
293
 
294
294
  post "/_flipper"
@@ -324,7 +324,7 @@ RSpec.describe Flipper::Engine do
324
324
  end
325
325
 
326
326
  it "enables cloud" do
327
- application.initialize!
327
+ silence { application.initialize! }
328
328
  expect(ENV["FLIPPER_CLOUD_TOKEN"]).to eq("credentials-token")
329
329
  expect(ENV["FLIPPER_CLOUD_SYNC_SECRET"]).to eq("credentials-secret")
330
330
  expect(Flipper.instance).to be_a(Flipper::Cloud::DSL)
@@ -337,6 +337,33 @@ RSpec.describe Flipper::Engine do
337
337
  expect(ActiveRecord::Base.ancestors).to include(Flipper::Model::ActiveRecord)
338
338
  end
339
339
 
340
+ describe "config.actor_limit" do
341
+ let(:adapter) do
342
+ silence { application.initialize! }
343
+ Flipper.adapter.adapter.adapter
344
+ end
345
+
346
+ it "defaults to 100" do
347
+ expect(adapter).to be_instance_of(Flipper::Adapters::ActorLimit)
348
+ expect(adapter.limit).to eq(100)
349
+ end
350
+
351
+ it "can be set from FLIPPER_ACTOR_LIMIT env" do
352
+ ENV["FLIPPER_ACTOR_LIMIT"] = "500"
353
+ expect(adapter.limit).to eq(500)
354
+ end
355
+
356
+ it "can be set from an initializer" do
357
+ initializer { config.actor_limit = 99 }
358
+ expect(adapter.limit).to eq(99)
359
+ end
360
+
361
+ it "can be disabled from an initializer" do
362
+ initializer { config.actor_limit = false }
363
+ expect(adapter).not_to be_instance_of(Flipper::Adapters::ActorLimit)
364
+ end
365
+ end
366
+
340
367
  # Add app initializer in the same order as config/initializers/*
341
368
  def initializer(&block)
342
369
  application.initializer 'spec', before: :load_config_initializers do
@@ -285,7 +285,7 @@ RSpec.describe Flipper::Middleware::Memoizer do
285
285
  end
286
286
 
287
287
  def get(uri, params = {}, env = {}, &block)
288
- silence { super(uri, params, env, &block) }
288
+ capture_output { super(uri, params, env, &block) }
289
289
  end
290
290
 
291
291
  include_examples 'flipper middleware'
data/spec/spec_helper.rb CHANGED
@@ -27,6 +27,9 @@ ENV["FLIPPER_CLOUD_LOGGING_ENABLED"] = "false"
27
27
 
28
28
  RSpec.configure do |config|
29
29
  config.before(:example) do
30
+ # default stub for telemetry
31
+ stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
32
+ to_return(status: 200, body: "", headers: {})
30
33
  Flipper::Cloud::Telemetry.reset if defined?(Flipper::Cloud::Telemetry) && Flipper::Cloud::Telemetry.respond_to?(:reset)
31
34
  Flipper::Poller.reset if defined?(Flipper::Poller)
32
35
  Flipper.unregister_groups
@@ -1,7 +1,7 @@
1
1
  if ENV["CI"] || ENV["FAIL_ON_OUTPUT"]
2
2
  RSpec.configure do |config|
3
3
  config.around do |example|
4
- output = silence { example.run }
4
+ output = capture_output { example.run }
5
5
  fail "Use `silence { }` to avoid printing to STDOUT/STDERR\n#{output}" unless output.empty?
6
6
  end
7
7
  end
@@ -79,11 +79,22 @@ module SpecHelpers
79
79
  original_stdout = $stdout
80
80
 
81
81
  # Redirect stderr and stdout
82
- output = $stderr = $stdout = StringIO.new
82
+ $stderr = $stdout = StringIO.new
83
+
84
+ yield
85
+ ensure
86
+ $stderr = original_stderr
87
+ $stdout = original_stdout
88
+ end
89
+
90
+ def capture_output
91
+ original_stderr = $stderr
92
+ original_stdout = $stdout
93
+
94
+ output = $stdout = $stderr = StringIO.new
83
95
 
84
96
  yield
85
97
 
86
- # Return output
87
98
  output.string
88
99
  ensure
89
100
  $stderr = original_stderr
@@ -0,0 +1,20 @@
1
+ require "test_helper"
2
+ require "flipper/test/shared_adapter_test"
3
+ require "flipper/adapters/actor_limit"
4
+
5
+ class Flipper::Adapters::ActorLimitTest < MiniTest::Test
6
+ prepend Flipper::Test::SharedAdapterTests
7
+
8
+ def setup
9
+ @memory = Flipper::Adapters::Memory.new
10
+ @adapter = Flipper::Adapters::ActorLimit.new(@memory, 5)
11
+ end
12
+
13
+ def test_enable_fails_when_limit_exceeded
14
+ 5.times { |i| @feature.enable Flipper::Actor.new("User;#{i}") }
15
+
16
+ assert_raises Flipper::Adapters::ActorLimit::LimitExceeded do
17
+ @feature.enable Flipper::Actor.new("User;6")
18
+ end
19
+ end
20
+ end
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.3.0.pre
4
+ version: 1.3.1
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-03-14 00:00:00.000000000 Z
11
+ date: 2024-09-04 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/actor_limit.rb
93
94
  - lib/flipper/adapters/cache_base.rb
94
95
  - lib/flipper/adapters/dual_write.rb
95
96
  - lib/flipper/adapters/failover.rb
@@ -203,6 +204,7 @@ files:
203
204
  - spec/flipper/actor_spec.rb
204
205
  - spec/flipper/adapter_builder_spec.rb
205
206
  - spec/flipper/adapter_spec.rb
207
+ - spec/flipper/adapters/actor_limit_spec.rb
206
208
  - spec/flipper/adapters/dual_write_spec.rb
207
209
  - spec/flipper/adapters/failover_spec.rb
208
210
  - spec/flipper/adapters/failsafe_spec.rb
@@ -296,6 +298,7 @@ files:
296
298
  - spec/support/fake_udp_socket.rb
297
299
  - spec/support/skippable.rb
298
300
  - spec/support/spec_helpers.rb
301
+ - test/adapters/actor_limit_test.rb
299
302
  - test/adapters/memory_test.rb
300
303
  - test/adapters/pstore_test.rb
301
304
  - test/test_helper.rb
@@ -311,7 +314,7 @@ metadata:
311
314
  homepage_uri: https://www.flippercloud.io
312
315
  source_code_uri: https://github.com/flippercloud/flipper
313
316
  bug_tracker_uri: https://github.com/flippercloud/flipper/issues
314
- changelog_uri: https://github.com/flippercloud/flipper/releases/tag/v1.3.0.pre
317
+ changelog_uri: https://github.com/flippercloud/flipper/releases/tag/v1.3.1
315
318
  post_install_message:
316
319
  rdoc_options: []
317
320
  require_paths:
@@ -327,7 +330,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
327
330
  - !ruby/object:Gem::Version
328
331
  version: '0'
329
332
  requirements: []
330
- rubygems_version: 3.5.3
333
+ rubygems_version: 3.5.18
331
334
  signing_key:
332
335
  specification_version: 4
333
336
  summary: Beautiful, performant feature flags for Ruby and Rails.
@@ -338,6 +341,7 @@ test_files:
338
341
  - spec/flipper/actor_spec.rb
339
342
  - spec/flipper/adapter_builder_spec.rb
340
343
  - spec/flipper/adapter_spec.rb
344
+ - spec/flipper/adapters/actor_limit_spec.rb
341
345
  - spec/flipper/adapters/dual_write_spec.rb
342
346
  - spec/flipper/adapters/failover_spec.rb
343
347
  - spec/flipper/adapters/failsafe_spec.rb
@@ -431,6 +435,7 @@ test_files:
431
435
  - spec/support/fake_udp_socket.rb
432
436
  - spec/support/skippable.rb
433
437
  - spec/support/spec_helpers.rb
438
+ - test/adapters/actor_limit_test.rb
434
439
  - test/adapters/memory_test.rb
435
440
  - test/adapters/pstore_test.rb
436
441
  - test/test_helper.rb