flipper 0.24.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/ci.yml +45 -14
- data/.github/workflows/examples.yml +39 -16
- data/Changelog.md +2 -443
- data/Gemfile +19 -11
- data/README.md +31 -27
- data/Rakefile +6 -4
- data/benchmark/enabled_ips.rb +10 -0
- data/benchmark/enabled_multiple_actors_ips.rb +20 -0
- data/benchmark/enabled_profile.rb +20 -0
- data/benchmark/instrumentation_ips.rb +21 -0
- data/benchmark/typecast_ips.rb +27 -0
- data/docs/images/banner.jpg +0 -0
- data/docs/images/flipper_cloud.png +0 -0
- data/examples/api/basic.ru +3 -4
- data/examples/api/custom_memoized.ru +3 -4
- data/examples/api/memoized.ru +3 -4
- data/examples/cloud/app.ru +12 -0
- data/examples/cloud/backoff_policy.rb +13 -0
- data/examples/cloud/basic.rb +22 -0
- data/examples/cloud/cloud_setup.rb +20 -0
- data/examples/cloud/forked.rb +36 -0
- data/examples/cloud/import.rb +17 -0
- data/examples/cloud/threaded.rb +33 -0
- data/examples/dsl.rb +1 -15
- data/examples/enabled_for_actor.rb +4 -2
- data/examples/expressions.rb +213 -0
- data/examples/instrumentation.rb +1 -0
- data/examples/instrumentation_last_accessed_at.rb +1 -0
- data/examples/mirroring.rb +59 -0
- data/examples/strict.rb +18 -0
- data/exe/flipper +5 -0
- data/flipper-cloud.gemspec +19 -0
- data/flipper.gemspec +10 -6
- data/lib/flipper/actor.rb +6 -3
- data/lib/flipper/adapter.rb +33 -7
- data/lib/flipper/adapter_builder.rb +44 -0
- data/lib/flipper/adapters/actor_limit.rb +28 -0
- data/lib/flipper/adapters/cache_base.rb +143 -0
- data/lib/flipper/adapters/dual_write.rb +1 -3
- data/lib/flipper/adapters/failover.rb +0 -4
- data/lib/flipper/adapters/failsafe.rb +72 -0
- data/lib/flipper/adapters/http/client.rb +44 -20
- data/lib/flipper/adapters/http/error.rb +1 -1
- data/lib/flipper/adapters/http.rb +31 -16
- data/lib/flipper/adapters/instrumented.rb +25 -6
- data/lib/flipper/adapters/memoizable.rb +33 -21
- data/lib/flipper/adapters/memory.rb +81 -46
- data/lib/flipper/adapters/operation_logger.rb +17 -78
- data/lib/flipper/adapters/poll/poller.rb +2 -0
- data/lib/flipper/adapters/poll.rb +37 -0
- data/lib/flipper/adapters/pstore.rb +17 -11
- data/lib/flipper/adapters/read_only.rb +8 -41
- data/lib/flipper/adapters/strict.rb +45 -0
- data/lib/flipper/adapters/sync/feature_synchronizer.rb +10 -1
- data/lib/flipper/adapters/sync.rb +0 -4
- data/lib/flipper/adapters/wrapper.rb +54 -0
- data/lib/flipper/cli.rb +263 -0
- data/lib/flipper/cloud/configuration.rb +263 -0
- data/lib/flipper/cloud/dsl.rb +27 -0
- data/lib/flipper/cloud/message_verifier.rb +95 -0
- data/lib/flipper/cloud/middleware.rb +63 -0
- data/lib/flipper/cloud/routes.rb +14 -0
- data/lib/flipper/cloud/telemetry/backoff_policy.rb +93 -0
- data/lib/flipper/cloud/telemetry/instrumenter.rb +22 -0
- data/lib/flipper/cloud/telemetry/metric.rb +39 -0
- data/lib/flipper/cloud/telemetry/metric_storage.rb +30 -0
- data/lib/flipper/cloud/telemetry/submitter.rb +98 -0
- data/lib/flipper/cloud/telemetry.rb +191 -0
- data/lib/flipper/cloud.rb +53 -0
- data/lib/flipper/configuration.rb +25 -4
- data/lib/flipper/dsl.rb +46 -45
- data/lib/flipper/engine.rb +102 -0
- data/lib/flipper/errors.rb +3 -20
- data/lib/flipper/export.rb +26 -0
- data/lib/flipper/exporter.rb +17 -0
- data/lib/flipper/exporters/json/export.rb +32 -0
- data/lib/flipper/exporters/json/v1.rb +33 -0
- data/lib/flipper/expression/builder.rb +73 -0
- data/lib/flipper/expression/constant.rb +25 -0
- data/lib/flipper/expression.rb +71 -0
- data/lib/flipper/expressions/all.rb +11 -0
- data/lib/flipper/expressions/any.rb +9 -0
- data/lib/flipper/expressions/boolean.rb +9 -0
- data/lib/flipper/expressions/comparable.rb +13 -0
- data/lib/flipper/expressions/duration.rb +28 -0
- data/lib/flipper/expressions/equal.rb +9 -0
- data/lib/flipper/expressions/greater_than.rb +9 -0
- data/lib/flipper/expressions/greater_than_or_equal_to.rb +9 -0
- data/lib/flipper/expressions/less_than.rb +9 -0
- data/lib/flipper/expressions/less_than_or_equal_to.rb +9 -0
- data/lib/flipper/expressions/not_equal.rb +9 -0
- data/lib/flipper/expressions/now.rb +9 -0
- data/lib/flipper/expressions/number.rb +9 -0
- data/lib/flipper/expressions/percentage.rb +9 -0
- data/lib/flipper/expressions/percentage_of_actors.rb +12 -0
- data/lib/flipper/expressions/property.rb +9 -0
- data/lib/flipper/expressions/random.rb +9 -0
- data/lib/flipper/expressions/string.rb +9 -0
- data/lib/flipper/expressions/time.rb +9 -0
- data/lib/flipper/feature.rb +87 -26
- data/lib/flipper/feature_check_context.rb +10 -6
- data/lib/flipper/gate.rb +13 -11
- data/lib/flipper/gate_values.rb +5 -18
- data/lib/flipper/gates/actor.rb +10 -17
- data/lib/flipper/gates/boolean.rb +1 -1
- data/lib/flipper/gates/expression.rb +75 -0
- data/lib/flipper/gates/group.rb +5 -7
- data/lib/flipper/gates/percentage_of_actors.rb +10 -13
- data/lib/flipper/gates/percentage_of_time.rb +1 -2
- data/lib/flipper/identifier.rb +2 -2
- data/lib/flipper/instrumentation/log_subscriber.rb +34 -6
- data/lib/flipper/instrumentation/statsd_subscriber.rb +2 -4
- data/lib/flipper/instrumentation/subscriber.rb +8 -1
- data/lib/flipper/metadata.rb +7 -1
- data/lib/flipper/middleware/memoizer.rb +28 -22
- data/lib/flipper/model/active_record.rb +23 -0
- data/lib/flipper/poller.rb +118 -0
- data/lib/flipper/serializers/gzip.rb +22 -0
- data/lib/flipper/serializers/json.rb +17 -0
- data/lib/flipper/spec/shared_adapter_specs.rb +105 -63
- data/lib/flipper/test/shared_adapter_test.rb +101 -58
- data/lib/flipper/test_help.rb +43 -0
- data/lib/flipper/typecast.rb +59 -18
- data/lib/flipper/types/actor.rb +13 -13
- data/lib/flipper/types/group.rb +4 -4
- data/lib/flipper/types/percentage.rb +1 -1
- data/lib/flipper/version.rb +11 -1
- data/lib/flipper.rb +50 -11
- data/lib/generators/flipper/setup_generator.rb +63 -0
- data/lib/generators/flipper/templates/update/migrations/01_create_flipper_tables.rb.erb +22 -0
- data/lib/generators/flipper/templates/update/migrations/02_change_flipper_gates_value_to_text.rb.erb +18 -0
- data/lib/generators/flipper/update_generator.rb +35 -0
- data/package-lock.json +41 -0
- data/package.json +10 -0
- data/spec/fixtures/environment.rb +1 -0
- data/spec/fixtures/flipper_pstore_1679087600.json +46 -0
- data/spec/flipper/adapter_builder_spec.rb +72 -0
- data/spec/flipper/adapter_spec.rb +30 -2
- data/spec/flipper/adapters/actor_limit_spec.rb +20 -0
- data/spec/flipper/adapters/dual_write_spec.rb +2 -2
- data/spec/flipper/adapters/failsafe_spec.rb +58 -0
- data/spec/flipper/adapters/http/client_spec.rb +61 -0
- data/spec/flipper/adapters/http_spec.rb +137 -55
- data/spec/flipper/adapters/instrumented_spec.rb +29 -11
- data/spec/flipper/adapters/memoizable_spec.rb +51 -31
- data/spec/flipper/adapters/memory_spec.rb +14 -3
- data/spec/flipper/adapters/operation_logger_spec.rb +31 -12
- data/spec/flipper/adapters/read_only_spec.rb +32 -17
- data/spec/flipper/adapters/strict_spec.rb +64 -0
- data/spec/flipper/adapters/sync/feature_synchronizer_spec.rb +27 -0
- data/spec/flipper/cli_spec.rb +164 -0
- data/spec/flipper/cloud/configuration_spec.rb +251 -0
- data/spec/flipper/cloud/dsl_spec.rb +82 -0
- data/spec/flipper/cloud/message_verifier_spec.rb +104 -0
- data/spec/flipper/cloud/middleware_spec.rb +289 -0
- data/spec/flipper/cloud/telemetry/backoff_policy_spec.rb +107 -0
- data/spec/flipper/cloud/telemetry/metric_spec.rb +87 -0
- data/spec/flipper/cloud/telemetry/metric_storage_spec.rb +58 -0
- data/spec/flipper/cloud/telemetry/submitter_spec.rb +145 -0
- data/spec/flipper/cloud/telemetry_spec.rb +208 -0
- data/spec/flipper/cloud_spec.rb +181 -0
- data/spec/flipper/configuration_spec.rb +17 -0
- data/spec/flipper/dsl_spec.rb +54 -73
- data/spec/flipper/engine_spec.rb +373 -0
- data/spec/flipper/export_spec.rb +13 -0
- data/spec/flipper/exporter_spec.rb +16 -0
- data/spec/flipper/exporters/json/export_spec.rb +60 -0
- data/spec/flipper/exporters/json/v1_spec.rb +33 -0
- data/spec/flipper/expression/builder_spec.rb +248 -0
- data/spec/flipper/expression_spec.rb +188 -0
- data/spec/flipper/expressions/all_spec.rb +15 -0
- data/spec/flipper/expressions/any_spec.rb +15 -0
- data/spec/flipper/expressions/boolean_spec.rb +15 -0
- data/spec/flipper/expressions/duration_spec.rb +43 -0
- data/spec/flipper/expressions/equal_spec.rb +24 -0
- data/spec/flipper/expressions/greater_than_or_equal_to_spec.rb +28 -0
- data/spec/flipper/expressions/greater_than_spec.rb +28 -0
- data/spec/flipper/expressions/less_than_or_equal_to_spec.rb +28 -0
- data/spec/flipper/expressions/less_than_spec.rb +32 -0
- data/spec/flipper/expressions/not_equal_spec.rb +15 -0
- data/spec/flipper/expressions/now_spec.rb +11 -0
- data/spec/flipper/expressions/number_spec.rb +21 -0
- data/spec/flipper/expressions/percentage_of_actors_spec.rb +20 -0
- data/spec/flipper/expressions/percentage_spec.rb +15 -0
- data/spec/flipper/expressions/property_spec.rb +13 -0
- data/spec/flipper/expressions/random_spec.rb +9 -0
- data/spec/flipper/expressions/string_spec.rb +11 -0
- data/spec/flipper/expressions/time_spec.rb +13 -0
- data/spec/flipper/feature_check_context_spec.rb +17 -17
- data/spec/flipper/feature_spec.rb +436 -33
- data/spec/flipper/gate_values_spec.rb +2 -33
- data/spec/flipper/gates/boolean_spec.rb +1 -1
- data/spec/flipper/gates/expression_spec.rb +108 -0
- data/spec/flipper/gates/group_spec.rb +2 -3
- data/spec/flipper/gates/percentage_of_actors_spec.rb +61 -5
- data/spec/flipper/gates/percentage_of_time_spec.rb +2 -2
- data/spec/flipper/identifier_spec.rb +4 -5
- data/spec/flipper/instrumentation/log_subscriber_spec.rb +23 -6
- data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +25 -1
- data/spec/flipper/middleware/memoizer_spec.rb +74 -24
- data/spec/flipper/model/active_record_spec.rb +61 -0
- data/spec/flipper/poller_spec.rb +47 -0
- data/spec/flipper/serializers/gzip_spec.rb +13 -0
- data/spec/flipper/serializers/json_spec.rb +13 -0
- data/spec/flipper/typecast_spec.rb +121 -6
- data/spec/flipper/types/actor_spec.rb +63 -46
- data/spec/flipper/types/group_spec.rb +2 -2
- data/spec/flipper_integration_spec.rb +168 -58
- data/spec/flipper_spec.rb +93 -29
- data/spec/spec_helper.rb +8 -14
- data/spec/support/actor_names.yml +1 -0
- data/spec/support/fail_on_output.rb +8 -0
- data/spec/support/fake_backoff_policy.rb +15 -0
- data/spec/support/skippable.rb +18 -0
- data/spec/support/spec_helpers.rb +23 -8
- data/test/adapters/actor_limit_test.rb +20 -0
- data/test_rails/generators/flipper/setup_generator_test.rb +64 -0
- data/test_rails/generators/flipper/update_generator_test.rb +96 -0
- data/test_rails/helper.rb +19 -2
- data/test_rails/system/test_help_test.rb +51 -0
- metadata +223 -19
- data/lib/flipper/railtie.rb +0 -47
- data/spec/flipper/railtie_spec.rb +0 -73
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'flipper'
|
3
|
+
require 'benchmark/ips'
|
4
|
+
|
5
|
+
actor1 = Flipper::Actor.new("User;1")
|
6
|
+
actor2 = Flipper::Actor.new("User;2")
|
7
|
+
actor3 = Flipper::Actor.new("User;3")
|
8
|
+
actor4 = Flipper::Actor.new("User;4")
|
9
|
+
actor5 = Flipper::Actor.new("User;5")
|
10
|
+
actor6 = Flipper::Actor.new("User;6")
|
11
|
+
actor7 = Flipper::Actor.new("User;7")
|
12
|
+
actor8 = Flipper::Actor.new("User;8")
|
13
|
+
|
14
|
+
actors = [actor1, actor2, actor3, actor4, actor5, actor6, actor7, actor8]
|
15
|
+
|
16
|
+
Benchmark.ips do |x|
|
17
|
+
x.report("with array of actors") { Flipper.enabled?(:foo, actors) }
|
18
|
+
x.report("with multiple enabled? checks") { actors.each { |actor| Flipper.enabled?(:foo, actor) } }
|
19
|
+
x.compare!
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'flipper'
|
3
|
+
require 'stackprof'
|
4
|
+
require 'benchmark/ips'
|
5
|
+
|
6
|
+
flipper = Flipper.new(Flipper::Adapters::Memory.new)
|
7
|
+
feature = flipper.feature(:foo)
|
8
|
+
actor = Flipper::Actor.new("User;1")
|
9
|
+
|
10
|
+
profile = StackProf.run(mode: :wall, interval: 1_000) do
|
11
|
+
2_000_000.times do
|
12
|
+
feature.enabled?(actor)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
result = StackProf::Report.new(profile)
|
17
|
+
puts
|
18
|
+
result.print_text
|
19
|
+
puts "\n\n\n"
|
20
|
+
result.print_method(/Flipper::Feature#enabled?/)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'flipper'
|
3
|
+
require 'active_support/notifications'
|
4
|
+
require 'active_support/isolated_execution_state'
|
5
|
+
require 'benchmark/ips'
|
6
|
+
|
7
|
+
class FlipperSubscriber
|
8
|
+
def call(name, start, finish, id, payload)
|
9
|
+
end
|
10
|
+
|
11
|
+
ActiveSupport::Notifications.subscribe(/flipper/, new)
|
12
|
+
end
|
13
|
+
|
14
|
+
actor = Flipper::Actor.new("User;1")
|
15
|
+
bare = Flipper.new(Flipper::Adapters::Memory.new)
|
16
|
+
instrumented = Flipper.new(Flipper::Adapters::Memory.new, instrumenter: ActiveSupport::Notifications)
|
17
|
+
|
18
|
+
Benchmark.ips do |x|
|
19
|
+
x.report("with instrumentation") { instrumented.enabled?(:foo, actor) }
|
20
|
+
x.report("without instrumentation") { bare.enabled?(:foo, actor) }
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'flipper'
|
3
|
+
require 'benchmark/ips'
|
4
|
+
|
5
|
+
Benchmark.ips do |x|
|
6
|
+
x.report("Typecast.to_boolean true") { Flipper::Typecast.to_boolean(true) }
|
7
|
+
x.report("Typecast.to_boolean 1") { Flipper::Typecast.to_boolean(1) }
|
8
|
+
x.report("Typecast.to_boolean 'true'") { Flipper::Typecast.to_boolean('true'.freeze) }
|
9
|
+
x.report("Typecast.to_boolean '1'") { Flipper::Typecast.to_boolean('1'.freeze) }
|
10
|
+
x.report("Typecast.to_boolean false") { Flipper::Typecast.to_boolean(false) }
|
11
|
+
|
12
|
+
x.report("Typecast.to_integer 1") { Flipper::Typecast.to_integer(1) }
|
13
|
+
x.report("Typecast.to_integer '1'") { Flipper::Typecast.to_integer('1') }
|
14
|
+
|
15
|
+
x.report("Typecast.to_float 1") { Flipper::Typecast.to_float(1) }
|
16
|
+
x.report("Typecast.to_float '1'") { Flipper::Typecast.to_float('1'.freeze) }
|
17
|
+
x.report("Typecast.to_float 1.01") { Flipper::Typecast.to_float(1) }
|
18
|
+
x.report("Typecast.to_float '1.01'") { Flipper::Typecast.to_float('1'.freeze) }
|
19
|
+
|
20
|
+
x.report("Typecast.to_number 1") { Flipper::Typecast.to_number(1) }
|
21
|
+
x.report("Typecast.to_number 1.1") { Flipper::Typecast.to_number(1.1) }
|
22
|
+
x.report("Typecast.to_number '1'") { Flipper::Typecast.to_number('1'.freeze) }
|
23
|
+
x.report("Typecast.to_number '1.1'") { Flipper::Typecast.to_number('1.1'.freeze) }
|
24
|
+
x.report("Typecast.to_number nil") { Flipper::Typecast.to_number(nil) }
|
25
|
+
time = Time.now
|
26
|
+
x.report("Typecast.to_number Time.now") { Flipper::Typecast.to_number(time) }
|
27
|
+
end
|
data/docs/images/banner.jpg
CHANGED
Binary file
|
Binary file
|
data/examples/api/basic.ru
CHANGED
@@ -1,19 +1,18 @@
|
|
1
1
|
#
|
2
2
|
# Usage:
|
3
|
-
# # if you want it to not reload and be really fast
|
4
3
|
# bin/rackup examples/api/basic.ru -p 9999
|
5
4
|
#
|
6
|
-
# # if you want reloading
|
7
|
-
# bin/shotgun examples/api/basic.ru -p 9999
|
8
|
-
#
|
9
5
|
# http://localhost:9999/
|
10
6
|
#
|
11
7
|
|
12
8
|
require 'bundler/setup'
|
9
|
+
require 'rack/reloader'
|
13
10
|
require "flipper/api"
|
14
11
|
require "flipper/adapters/pstore"
|
15
12
|
|
16
13
|
# You can uncomment this to get some default data:
|
17
14
|
# Flipper.enable :logging
|
18
15
|
|
16
|
+
use Rack::Reloader
|
17
|
+
|
19
18
|
run Flipper::Api.app
|
@@ -1,15 +1,12 @@
|
|
1
1
|
#
|
2
2
|
# Usage:
|
3
|
-
# # if you want it to not reload and be really fast
|
4
3
|
# bin/rackup examples/api/custom_memoized.ru -p 9999
|
5
4
|
#
|
6
|
-
# # if you want reloading
|
7
|
-
# bin/shotgun examples/api/custom_memoized.ru -p 9999
|
8
|
-
#
|
9
5
|
# http://localhost:9999/
|
10
6
|
#
|
11
7
|
|
12
8
|
require 'bundler/setup'
|
9
|
+
require 'rack/reloader'
|
13
10
|
require "active_support/notifications"
|
14
11
|
require "flipper/api"
|
15
12
|
require "flipper/adapters/pstore"
|
@@ -31,6 +28,8 @@ ActiveSupport::Notifications.subscribe(/.*/, ->(*args) {
|
|
31
28
|
# You can uncomment this to get some default data:
|
32
29
|
# flipper[:logging].enable_percentage_of_time 5
|
33
30
|
|
31
|
+
use Rack::Reloader
|
32
|
+
|
34
33
|
run Flipper::Api.app(flipper) { |builder|
|
35
34
|
builder.use Flipper::Middleware::SetupEnv, flipper
|
36
35
|
builder.use Flipper::Middleware::Memoizer, preload: true
|
data/examples/api/memoized.ru
CHANGED
@@ -1,15 +1,12 @@
|
|
1
1
|
#
|
2
2
|
# Usage:
|
3
|
-
# # if you want it to not reload and be really fast
|
4
3
|
# bin/rackup examples/api/memoized.ru -p 9999
|
5
4
|
#
|
6
|
-
# # if you want reloading
|
7
|
-
# bin/shotgun examples/api/memoized.ru -p 9999
|
8
|
-
#
|
9
5
|
# http://localhost:9999/
|
10
6
|
#
|
11
7
|
|
12
8
|
require 'bundler/setup'
|
9
|
+
require 'rack/reloader'
|
13
10
|
require "active_support/notifications"
|
14
11
|
require "flipper/api"
|
15
12
|
require "flipper/adapters/pstore"
|
@@ -38,6 +35,8 @@ Flipper.register(:admins) { |actor|
|
|
38
35
|
# You can uncomment this to get some default data:
|
39
36
|
# Flipper.enable :logging
|
40
37
|
|
38
|
+
use Rack::Reloader
|
39
|
+
|
41
40
|
run Flipper::Api.app { |builder|
|
42
41
|
builder.use Flipper::Middleware::Memoizer, preload: true
|
43
42
|
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Usage (from the repo root):
|
2
|
+
# env FLIPPER_CLOUD_TOKEN=<token> FLIPPER_CLOUD_SYNC_SECRET=<secret> bundle exec rackup examples/cloud/app.ru -p 9999
|
3
|
+
# http://localhost:9999/
|
4
|
+
|
5
|
+
require 'bundler/setup'
|
6
|
+
require 'flipper/cloud'
|
7
|
+
|
8
|
+
Flipper.configure do |config|
|
9
|
+
config.default { Flipper::Cloud.new }
|
10
|
+
end
|
11
|
+
|
12
|
+
run Flipper::Cloud.app
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Just a simple example that shows how the backoff policy works.
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'flipper/cloud/telemetry/backoff_policy'
|
4
|
+
|
5
|
+
intervals = []
|
6
|
+
policy = Flipper::Cloud::Telemetry::BackoffPolicy.new
|
7
|
+
|
8
|
+
10.times do |n|
|
9
|
+
intervals << policy.next_interval
|
10
|
+
end
|
11
|
+
|
12
|
+
pp intervals.map { |i| i.round(2) }
|
13
|
+
puts "Total: #{intervals.sum.round(2)}ms (#{(intervals.sum/1_000.0).round(2)} sec)"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Usage (from the repo root):
|
2
|
+
# env FLIPPER_CLOUD_TOKEN=<token> bundle exec ruby examples/cloud/basic.rb
|
3
|
+
|
4
|
+
require_relative "./cloud_setup"
|
5
|
+
require 'bundler/setup'
|
6
|
+
require 'flipper/cloud'
|
7
|
+
|
8
|
+
Flipper[:stats].enable
|
9
|
+
|
10
|
+
if Flipper[:stats].enabled?
|
11
|
+
puts 'Enabled!'
|
12
|
+
else
|
13
|
+
puts 'Disabled!'
|
14
|
+
end
|
15
|
+
|
16
|
+
Flipper[:stats].disable
|
17
|
+
|
18
|
+
if Flipper[:stats].enabled?
|
19
|
+
puts 'Enabled!'
|
20
|
+
else
|
21
|
+
puts 'Disabled!'
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
if ENV["FLIPPER_CLOUD_TOKEN"].nil? || ENV["FLIPPER_CLOUD_TOKEN"].empty?
|
2
|
+
warn "FLIPPER_CLOUD_TOKEN missing so skipping cloud example."
|
3
|
+
exit
|
4
|
+
end
|
5
|
+
|
6
|
+
matrix_key = if ENV["CI"]
|
7
|
+
suffix_rails = ENV["RAILS_VERSION"].split(".").take(2).join
|
8
|
+
suffix_ruby = RUBY_VERSION.split(".").take(2).join
|
9
|
+
"FLIPPER_CLOUD_TOKEN_#{suffix_ruby}_#{suffix_rails}"
|
10
|
+
else
|
11
|
+
"FLIPPER_CLOUD_TOKEN"
|
12
|
+
end
|
13
|
+
|
14
|
+
if matrix_token = ENV[matrix_key]
|
15
|
+
puts "Using #{matrix_key} for FLIPPER_CLOUD_TOKEN"
|
16
|
+
ENV["FLIPPER_CLOUD_TOKEN"] = matrix_token
|
17
|
+
else
|
18
|
+
warn "Missing #{matrix_key}. Go create an environment in flipper cloud and set #{matrix_key} to the adapter token for that environment in github actions secrets."
|
19
|
+
exit 1
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Usage (from the repo root):
|
2
|
+
# env FLIPPER_CLOUD_TOKEN=<token> bundle exec ruby examples/cloud/threaded.rb
|
3
|
+
|
4
|
+
require_relative "./cloud_setup"
|
5
|
+
require 'bundler/setup'
|
6
|
+
require 'flipper/cloud'
|
7
|
+
|
8
|
+
puts Process.pid
|
9
|
+
|
10
|
+
# Make a call in the parent process so we can detect forking.
|
11
|
+
Flipper.enabled?(:stats)
|
12
|
+
|
13
|
+
pids = 2.times.map do |n|
|
14
|
+
fork {
|
15
|
+
# Check every second to see if the feature is enabled
|
16
|
+
threads = []
|
17
|
+
2.times do
|
18
|
+
threads << Thread.new do
|
19
|
+
loop do
|
20
|
+
sleep rand
|
21
|
+
|
22
|
+
if Flipper[:stats].enabled?
|
23
|
+
puts "#{Process.pid} #{Time.now.to_i} Enabled!"
|
24
|
+
else
|
25
|
+
puts "#{Process.pid} #{Time.now.to_i} Disabled!"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
threads.map(&:join)
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
pids.each do |pid|
|
35
|
+
Process.waitpid pid, 0
|
36
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Usage (from the repo root):
|
2
|
+
# env FLIPPER_CLOUD_TOKEN=<token> bundle exec ruby examples/cloud/import.rb
|
3
|
+
|
4
|
+
require_relative "./cloud_setup"
|
5
|
+
require 'bundler/setup'
|
6
|
+
require 'flipper'
|
7
|
+
require 'flipper/cloud'
|
8
|
+
|
9
|
+
Flipper.enable(:test)
|
10
|
+
Flipper.enable(:search)
|
11
|
+
Flipper.enable_actor(:stats, Flipper::Actor.new("jnunemaker"))
|
12
|
+
Flipper.enable_percentage_of_time(:logging, 5)
|
13
|
+
|
14
|
+
cloud = Flipper::Cloud.new
|
15
|
+
|
16
|
+
# makes cloud identical to memory flipper
|
17
|
+
cloud.import(Flipper)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Usage (from the repo root):
|
2
|
+
# env FLIPPER_CLOUD_TOKEN=<token> bundle exec ruby examples/cloud/threaded.rb
|
3
|
+
|
4
|
+
require_relative "./cloud_setup"
|
5
|
+
require 'bundler/setup'
|
6
|
+
require 'flipper/cloud'
|
7
|
+
|
8
|
+
puts Process.pid
|
9
|
+
|
10
|
+
Flipper.configure do |config|
|
11
|
+
config.default {
|
12
|
+
Flipper::Cloud.new(
|
13
|
+
local_adapter: config.adapter,
|
14
|
+
debug_output: STDOUT,
|
15
|
+
)
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
# You might want to do this at some point to see different results:
|
20
|
+
# Flipper.enable(:search)
|
21
|
+
# Flipper.disable(:stats)
|
22
|
+
|
23
|
+
# Check every second to see if the feature is enabled
|
24
|
+
5.times.map { |i|
|
25
|
+
Thread.new {
|
26
|
+
loop do
|
27
|
+
sleep rand
|
28
|
+
|
29
|
+
Flipper.enabled?(:stats)
|
30
|
+
Flipper.enabled?(:search)
|
31
|
+
end
|
32
|
+
}
|
33
|
+
}.each(&:join)
|
data/examples/dsl.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'bundler/setup'
|
2
2
|
require 'flipper'
|
3
3
|
|
4
|
-
# create
|
4
|
+
# create an actor with an identifier
|
5
5
|
class Person < Struct.new(:id)
|
6
6
|
include Flipper::Identifier
|
7
7
|
end
|
@@ -47,20 +47,6 @@ puts "stats.enabled?: #{stats.enabled?}"
|
|
47
47
|
puts "stats.enabled? person: #{stats.enabled? person}"
|
48
48
|
puts
|
49
49
|
|
50
|
-
# get an instance of the percentage of time type set to 5
|
51
|
-
puts Flipper.time(5).inspect
|
52
|
-
|
53
|
-
# get an instance of the percentage of actors type set to 15
|
54
|
-
puts Flipper.actors(15).inspect
|
55
|
-
|
56
|
-
# get an instance of an actor using an object that responds to flipper_id
|
57
|
-
responds_to_flipper_id = Struct.new(:flipper_id).new(10)
|
58
|
-
puts Flipper.actor(responds_to_flipper_id).inspect
|
59
|
-
|
60
|
-
# get an instance of an actor using an object
|
61
|
-
thing = Struct.new(:flipper_id).new(22)
|
62
|
-
puts Flipper.actor(thing).inspect
|
63
|
-
|
64
50
|
# register a top level group
|
65
51
|
admins = Flipper.register(:admins) { |actor|
|
66
52
|
actor.respond_to?(:admin?) && actor.admin?
|
@@ -31,5 +31,7 @@ Flipper.enable_percentage_of_actors :pro_stats, 50
|
|
31
31
|
Flipper.enable_group :tweets, :admins
|
32
32
|
Flipper.enable_actor :posts, user2
|
33
33
|
|
34
|
-
pp Flipper.features.select { |feature| feature.enabled?(user1) }.map(&:name)
|
35
|
-
pp Flipper.features.select { |feature| feature.enabled?(user2) }.map(&:name)
|
34
|
+
pp Flipper.features.select { |feature| feature.enabled?(user1) }.map(&:name).sort
|
35
|
+
pp Flipper.features.select { |feature| feature.enabled?(user2) }.map(&:name).sort
|
36
|
+
pp Flipper.features.select { |feature| feature.enabled?(user1, user2) }.map(&:name).sort
|
37
|
+
pp Flipper.features.select { |feature| feature.enabled?([user2, user1]) }.map(&:name).sort
|
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'flipper'
|
3
|
+
|
4
|
+
def assert(value)
|
5
|
+
if value
|
6
|
+
p value
|
7
|
+
else
|
8
|
+
puts "Expected true but was #{value}. Please correct."
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def refute(value)
|
14
|
+
if value
|
15
|
+
puts "Expected false but was #{value}. Please correct."
|
16
|
+
exit 1
|
17
|
+
else
|
18
|
+
p value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def reset
|
23
|
+
Flipper.disable_expression :something
|
24
|
+
end
|
25
|
+
|
26
|
+
class User < Struct.new(:id, :flipper_properties)
|
27
|
+
include Flipper::Identifier
|
28
|
+
end
|
29
|
+
|
30
|
+
class Org < Struct.new(:id, :flipper_properties)
|
31
|
+
include Flipper::Identifier
|
32
|
+
end
|
33
|
+
|
34
|
+
NOW = Time.now.to_i
|
35
|
+
DAY = 60 * 60 * 24
|
36
|
+
|
37
|
+
org = Org.new(1, {
|
38
|
+
"type" => "Org",
|
39
|
+
"id" => 1,
|
40
|
+
"now" => NOW,
|
41
|
+
})
|
42
|
+
|
43
|
+
user = User.new(1, {
|
44
|
+
"type" => "User",
|
45
|
+
"id" => 1,
|
46
|
+
"plan" => "basic",
|
47
|
+
"age" => 39,
|
48
|
+
"team_user" => true,
|
49
|
+
"now" => NOW,
|
50
|
+
})
|
51
|
+
|
52
|
+
admin_user = User.new(2, {
|
53
|
+
"type" => "User",
|
54
|
+
"id" => 2,
|
55
|
+
"admin" => true,
|
56
|
+
"team_user" => true,
|
57
|
+
"now" => NOW,
|
58
|
+
})
|
59
|
+
|
60
|
+
other_user = User.new(3, {
|
61
|
+
"type" => "User",
|
62
|
+
"id" => 3,
|
63
|
+
"plan" => "plus",
|
64
|
+
"age" => 18,
|
65
|
+
"org_admin" => true,
|
66
|
+
"now" => NOW - DAY,
|
67
|
+
})
|
68
|
+
|
69
|
+
age_expression = Flipper.property(:age).gte(21)
|
70
|
+
plan_expression = Flipper.property(:plan).eq("basic")
|
71
|
+
admin_expression = Flipper.property(:admin).eq(true)
|
72
|
+
|
73
|
+
puts "Single Expression"
|
74
|
+
refute Flipper.enabled?(:something, user)
|
75
|
+
|
76
|
+
puts "Enabling single expression"
|
77
|
+
Flipper.enable :something, plan_expression
|
78
|
+
assert Flipper.enabled?(:something, user)
|
79
|
+
refute Flipper.enabled?(:something, admin_user)
|
80
|
+
refute Flipper.enabled?(:something, other_user)
|
81
|
+
|
82
|
+
puts "Disabling single expression"
|
83
|
+
reset
|
84
|
+
refute Flipper.enabled?(:something, user)
|
85
|
+
|
86
|
+
puts "\n\nAny Expression"
|
87
|
+
any_expression = Flipper.any(plan_expression, age_expression)
|
88
|
+
refute Flipper.enabled?(:something, user)
|
89
|
+
|
90
|
+
puts "Enabling any expression"
|
91
|
+
Flipper.enable :something, any_expression
|
92
|
+
assert Flipper.enabled?(:something, user)
|
93
|
+
refute Flipper.enabled?(:something, admin_user)
|
94
|
+
refute Flipper.enabled?(:something, other_user)
|
95
|
+
|
96
|
+
puts "Disabling any expression"
|
97
|
+
reset
|
98
|
+
refute Flipper.enabled?(:something, user)
|
99
|
+
|
100
|
+
puts "\n\nAll Expression"
|
101
|
+
all_expression = Flipper.all(plan_expression, age_expression)
|
102
|
+
refute Flipper.enabled?(:something, user)
|
103
|
+
|
104
|
+
puts "Enabling all expression"
|
105
|
+
Flipper.enable :something, all_expression
|
106
|
+
assert Flipper.enabled?(:something, user)
|
107
|
+
refute Flipper.enabled?(:something, admin_user)
|
108
|
+
refute Flipper.enabled?(:something, other_user)
|
109
|
+
|
110
|
+
puts "Disabling all expression"
|
111
|
+
reset
|
112
|
+
refute Flipper.enabled?(:something, user)
|
113
|
+
|
114
|
+
puts "\n\nNested Expression"
|
115
|
+
nested_expression = Flipper.any(admin_expression, all_expression)
|
116
|
+
refute Flipper.enabled?(:something, user)
|
117
|
+
refute Flipper.enabled?(:something, admin_user)
|
118
|
+
refute Flipper.enabled?(:something, other_user)
|
119
|
+
|
120
|
+
puts "Enabling nested expression"
|
121
|
+
Flipper.enable :something, nested_expression
|
122
|
+
assert Flipper.enabled?(:something, user)
|
123
|
+
assert Flipper.enabled?(:something, admin_user)
|
124
|
+
refute Flipper.enabled?(:something, other_user)
|
125
|
+
|
126
|
+
puts "Disabling nested expression"
|
127
|
+
reset
|
128
|
+
refute Flipper.enabled?(:something, user)
|
129
|
+
refute Flipper.enabled?(:something, admin_user)
|
130
|
+
refute Flipper.enabled?(:something, other_user)
|
131
|
+
|
132
|
+
puts "\n\nBoolean Expression"
|
133
|
+
boolean_expression = Flipper.boolean(true)
|
134
|
+
Flipper.enable :something, boolean_expression
|
135
|
+
assert Flipper.enabled?(:something)
|
136
|
+
assert Flipper.enabled?(:something, user)
|
137
|
+
reset
|
138
|
+
|
139
|
+
puts "\n\nSet of Actors Expression"
|
140
|
+
set_of_actors_expression = Flipper.any(
|
141
|
+
Flipper.property(:flipper_id).eq("User;1"),
|
142
|
+
Flipper.property(:flipper_id).eq("User;3"),
|
143
|
+
)
|
144
|
+
Flipper.enable :something, set_of_actors_expression
|
145
|
+
assert Flipper.enabled?(:something, user)
|
146
|
+
assert Flipper.enabled?(:something, other_user)
|
147
|
+
refute Flipper.enabled?(:something, admin_user)
|
148
|
+
reset
|
149
|
+
|
150
|
+
puts "\n\n% of Actors Expression"
|
151
|
+
percentage_of_actors = Flipper.property(:flipper_id).percentage_of_actors(30)
|
152
|
+
Flipper.enable :something, percentage_of_actors
|
153
|
+
refute Flipper.enabled?(:something, user)
|
154
|
+
refute Flipper.enabled?(:something, other_user)
|
155
|
+
assert Flipper.enabled?(:something, admin_user)
|
156
|
+
reset
|
157
|
+
|
158
|
+
puts "\n\n% of Actors Per Type Expression"
|
159
|
+
percentage_of_actors_per_type = Flipper.any(
|
160
|
+
Flipper.all(
|
161
|
+
Flipper.property(:type).eq("User"),
|
162
|
+
Flipper.property(:flipper_id).percentage_of_actors(40),
|
163
|
+
),
|
164
|
+
Flipper.all(
|
165
|
+
Flipper.property(:type).eq("Org"),
|
166
|
+
Flipper.property(:flipper_id).percentage_of_actors(10),
|
167
|
+
)
|
168
|
+
)
|
169
|
+
Flipper.enable :something, percentage_of_actors_per_type
|
170
|
+
refute Flipper.enabled?(:something, user) # not in the 40% enabled for Users
|
171
|
+
assert Flipper.enabled?(:something, other_user)
|
172
|
+
assert Flipper.enabled?(:something, admin_user)
|
173
|
+
refute Flipper.enabled?(:something, org) # not in the 10% of enabled for Orgs
|
174
|
+
reset
|
175
|
+
|
176
|
+
puts "\n\nPercentage of Time Expression"
|
177
|
+
percentage_of_time_expression = Flipper.random(100).lt(50)
|
178
|
+
Flipper.enable :something, percentage_of_time_expression
|
179
|
+
results = (1..10000).map { |n| Flipper.enabled?(:something, user) }
|
180
|
+
enabled, disabled = results.partition { |r| r }
|
181
|
+
p enabled: enabled.size
|
182
|
+
p disabled: disabled.size
|
183
|
+
assert (4_700..5_200).include?(enabled.size)
|
184
|
+
assert (4_700..5_200).include?(disabled.size)
|
185
|
+
reset
|
186
|
+
|
187
|
+
puts "\n\nChanging single expression to all expression"
|
188
|
+
Flipper.enable :something, plan_expression
|
189
|
+
Flipper.enable :something, Flipper.expression(:something).all.add(age_expression)
|
190
|
+
assert Flipper.enabled?(:something, user)
|
191
|
+
refute Flipper.enabled?(:something, admin_user)
|
192
|
+
refute Flipper.enabled?(:something, other_user)
|
193
|
+
|
194
|
+
puts "\n\nChanging single expression to any expression"
|
195
|
+
Flipper.enable :something, plan_expression
|
196
|
+
Flipper.enable :something, Flipper.expression(:something).any.add(age_expression, admin_expression)
|
197
|
+
assert Flipper.enabled?(:something, user)
|
198
|
+
assert Flipper.enabled?(:something, admin_user)
|
199
|
+
refute Flipper.enabled?(:something, other_user)
|
200
|
+
|
201
|
+
puts "\n\nChanging single expression to any expression by adding to condition"
|
202
|
+
Flipper.enable :something, plan_expression
|
203
|
+
Flipper.enable :something, Flipper.expression(:something).add(admin_expression)
|
204
|
+
assert Flipper.enabled?(:something, user)
|
205
|
+
assert Flipper.enabled?(:something, admin_user)
|
206
|
+
refute Flipper.enabled?(:something, other_user)
|
207
|
+
|
208
|
+
puts "\n\nEnabling based on time"
|
209
|
+
scheduled_time_expression = Flipper.property(:now).gte(NOW)
|
210
|
+
Flipper.enable :something, scheduled_time_expression
|
211
|
+
assert Flipper.enabled?(:something, user)
|
212
|
+
assert Flipper.enabled?(:something, admin_user)
|
213
|
+
refute Flipper.enabled?(:something, other_user)
|
data/examples/instrumentation.rb
CHANGED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require_relative 'active_record/ar_setup'
|
3
|
+
require 'flipper'
|
4
|
+
require 'flipper/adapters/redis'
|
5
|
+
require 'flipper/adapters/active_record'
|
6
|
+
|
7
|
+
# Say you have production...
|
8
|
+
production_adapter = Flipper::Adapters::Memory.new
|
9
|
+
production = Flipper.new(production_adapter)
|
10
|
+
|
11
|
+
# And production has some stuff enabled...
|
12
|
+
production.enable(:search)
|
13
|
+
production.enable_percentage_of_time(:verbose_logging, 5)
|
14
|
+
production.enable_percentage_of_actors(:new_feature, 5)
|
15
|
+
production.enable_actor(:issues, Flipper::Actor.new('1'))
|
16
|
+
production.enable_actor(:issues, Flipper::Actor.new('2'))
|
17
|
+
production.enable_group(:request_tracing, :staff)
|
18
|
+
|
19
|
+
# And you would like to mirror production to staging...
|
20
|
+
staging_adapter = Flipper::Adapters::Memory.new
|
21
|
+
staging = Flipper.new(staging_adapter)
|
22
|
+
staging_export = staging.export
|
23
|
+
|
24
|
+
puts "Here is the state of the world for staging and production..."
|
25
|
+
puts "Staging"
|
26
|
+
staging.features.each do |feature|
|
27
|
+
pp feature: feature.key, values: feature.gate_values
|
28
|
+
end
|
29
|
+
puts "Production"
|
30
|
+
production.features.each do |feature|
|
31
|
+
pp feature: feature.key, values: feature.gate_values
|
32
|
+
end
|
33
|
+
|
34
|
+
# NOTE: This wipes active record clean and copies features/gates from redis into active record.
|
35
|
+
puts "Mirroring production to staging..."
|
36
|
+
staging.import(production.export)
|
37
|
+
puts "Staging is now identical to Production."
|
38
|
+
|
39
|
+
puts "Staging"
|
40
|
+
staging.features.each do |feature|
|
41
|
+
pp feature: feature.key, values: feature.gate_values
|
42
|
+
end
|
43
|
+
puts "Production"
|
44
|
+
production.features.each do |feature|
|
45
|
+
pp feature: feature.key, values: feature.gate_values
|
46
|
+
end
|
47
|
+
|
48
|
+
puts "Restoring staging to original state..."
|
49
|
+
staging.import(staging_export)
|
50
|
+
puts "Staging restored."
|
51
|
+
|
52
|
+
puts "Staging"
|
53
|
+
staging.features.each do |feature|
|
54
|
+
pp feature: feature.key, values: feature.gate_values
|
55
|
+
end
|
56
|
+
puts "Production"
|
57
|
+
production.features.each do |feature|
|
58
|
+
pp feature: feature.key, values: feature.gate_values
|
59
|
+
end
|