flipper 0.26.0 → 1.3.6

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.
Files changed (228) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/ci.yml +61 -16
  4. data/.github/workflows/examples.yml +55 -18
  5. data/CLAUDE.md +74 -0
  6. data/Changelog.md +1 -486
  7. data/Gemfile +23 -11
  8. data/README.md +31 -27
  9. data/Rakefile +2 -2
  10. data/benchmark/enabled_ips.rb +10 -0
  11. data/benchmark/enabled_multiple_actors_ips.rb +20 -0
  12. data/benchmark/enabled_profile.rb +20 -0
  13. data/benchmark/instrumentation_ips.rb +21 -0
  14. data/benchmark/typecast_ips.rb +27 -0
  15. data/docs/images/banner.jpg +0 -0
  16. data/docs/images/flipper_cloud.png +0 -0
  17. data/examples/api/basic.ru +3 -4
  18. data/examples/api/custom_memoized.ru +3 -4
  19. data/examples/api/memoized.ru +3 -4
  20. data/examples/cloud/app.ru +12 -0
  21. data/examples/cloud/backoff_policy.rb +13 -0
  22. data/examples/cloud/basic.rb +22 -0
  23. data/examples/cloud/cloud_setup.rb +20 -0
  24. data/examples/cloud/forked.rb +36 -0
  25. data/examples/cloud/import.rb +17 -0
  26. data/examples/cloud/threaded.rb +33 -0
  27. data/examples/dsl.rb +1 -15
  28. data/examples/enabled_for_actor.rb +4 -2
  29. data/examples/expressions.rb +213 -0
  30. data/examples/mirroring.rb +59 -0
  31. data/examples/strict.rb +18 -0
  32. data/exe/flipper +5 -0
  33. data/flipper-cloud.gemspec +19 -0
  34. data/flipper.gemspec +8 -6
  35. data/lib/flipper/actor.rb +6 -3
  36. data/lib/flipper/adapter.rb +33 -7
  37. data/lib/flipper/adapter_builder.rb +44 -0
  38. data/lib/flipper/adapters/actor_limit.rb +28 -0
  39. data/lib/flipper/adapters/cache_base.rb +143 -0
  40. data/lib/flipper/adapters/dual_write.rb +1 -3
  41. data/lib/flipper/adapters/failover.rb +0 -4
  42. data/lib/flipper/adapters/failsafe.rb +0 -4
  43. data/lib/flipper/adapters/http/client.rb +40 -12
  44. data/lib/flipper/adapters/http/error.rb +2 -2
  45. data/lib/flipper/adapters/http.rb +30 -17
  46. data/lib/flipper/adapters/instrumented.rb +25 -6
  47. data/lib/flipper/adapters/memoizable.rb +33 -21
  48. data/lib/flipper/adapters/memory.rb +81 -46
  49. data/lib/flipper/adapters/operation_logger.rb +17 -78
  50. data/lib/flipper/adapters/poll/poller.rb +2 -125
  51. data/lib/flipper/adapters/poll.rb +20 -3
  52. data/lib/flipper/adapters/pstore.rb +17 -11
  53. data/lib/flipper/adapters/read_only.rb +8 -41
  54. data/lib/flipper/adapters/strict.rb +45 -0
  55. data/lib/flipper/adapters/sync/feature_synchronizer.rb +10 -1
  56. data/lib/flipper/adapters/sync.rb +0 -4
  57. data/lib/flipper/adapters/wrapper.rb +54 -0
  58. data/lib/flipper/cli.rb +263 -0
  59. data/lib/flipper/cloud/configuration.rb +266 -0
  60. data/lib/flipper/cloud/dsl.rb +27 -0
  61. data/lib/flipper/cloud/message_verifier.rb +95 -0
  62. data/lib/flipper/cloud/middleware.rb +63 -0
  63. data/lib/flipper/cloud/routes.rb +14 -0
  64. data/lib/flipper/cloud/telemetry/backoff_policy.rb +96 -0
  65. data/lib/flipper/cloud/telemetry/instrumenter.rb +22 -0
  66. data/lib/flipper/cloud/telemetry/metric.rb +39 -0
  67. data/lib/flipper/cloud/telemetry/metric_storage.rb +30 -0
  68. data/lib/flipper/cloud/telemetry/submitter.rb +100 -0
  69. data/lib/flipper/cloud/telemetry.rb +191 -0
  70. data/lib/flipper/cloud.rb +53 -0
  71. data/lib/flipper/configuration.rb +25 -4
  72. data/lib/flipper/dsl.rb +46 -45
  73. data/lib/flipper/engine.rb +102 -0
  74. data/lib/flipper/errors.rb +3 -3
  75. data/lib/flipper/export.rb +24 -0
  76. data/lib/flipper/exporter.rb +17 -0
  77. data/lib/flipper/exporters/json/export.rb +32 -0
  78. data/lib/flipper/exporters/json/v1.rb +33 -0
  79. data/lib/flipper/expression/builder.rb +73 -0
  80. data/lib/flipper/expression/constant.rb +25 -0
  81. data/lib/flipper/expression.rb +71 -0
  82. data/lib/flipper/expressions/all.rb +9 -0
  83. data/lib/flipper/expressions/any.rb +9 -0
  84. data/lib/flipper/expressions/boolean.rb +9 -0
  85. data/lib/flipper/expressions/comparable.rb +13 -0
  86. data/lib/flipper/expressions/duration.rb +28 -0
  87. data/lib/flipper/expressions/equal.rb +9 -0
  88. data/lib/flipper/expressions/greater_than.rb +9 -0
  89. data/lib/flipper/expressions/greater_than_or_equal_to.rb +9 -0
  90. data/lib/flipper/expressions/less_than.rb +9 -0
  91. data/lib/flipper/expressions/less_than_or_equal_to.rb +9 -0
  92. data/lib/flipper/expressions/not_equal.rb +9 -0
  93. data/lib/flipper/expressions/now.rb +9 -0
  94. data/lib/flipper/expressions/number.rb +9 -0
  95. data/lib/flipper/expressions/percentage.rb +9 -0
  96. data/lib/flipper/expressions/percentage_of_actors.rb +12 -0
  97. data/lib/flipper/expressions/property.rb +9 -0
  98. data/lib/flipper/expressions/random.rb +9 -0
  99. data/lib/flipper/expressions/string.rb +9 -0
  100. data/lib/flipper/expressions/time.rb +9 -0
  101. data/lib/flipper/feature.rb +94 -26
  102. data/lib/flipper/feature_check_context.rb +10 -6
  103. data/lib/flipper/gate.rb +13 -11
  104. data/lib/flipper/gate_values.rb +5 -18
  105. data/lib/flipper/gates/actor.rb +10 -17
  106. data/lib/flipper/gates/boolean.rb +1 -1
  107. data/lib/flipper/gates/expression.rb +75 -0
  108. data/lib/flipper/gates/group.rb +5 -7
  109. data/lib/flipper/gates/percentage_of_actors.rb +10 -13
  110. data/lib/flipper/gates/percentage_of_time.rb +1 -2
  111. data/lib/flipper/identifier.rb +2 -2
  112. data/lib/flipper/instrumentation/log_subscriber.rb +35 -8
  113. data/lib/flipper/instrumentation/statsd.rb +4 -2
  114. data/lib/flipper/instrumentation/statsd_subscriber.rb +2 -4
  115. data/lib/flipper/instrumentation/subscriber.rb +8 -5
  116. data/lib/flipper/metadata.rb +8 -1
  117. data/lib/flipper/middleware/memoizer.rb +30 -14
  118. data/lib/flipper/model/active_record.rb +23 -0
  119. data/lib/flipper/poller.rb +118 -0
  120. data/lib/flipper/serializers/gzip.rb +22 -0
  121. data/lib/flipper/serializers/json.rb +17 -0
  122. data/lib/flipper/spec/shared_adapter_specs.rb +105 -63
  123. data/lib/flipper/test/shared_adapter_test.rb +101 -58
  124. data/lib/flipper/test_help.rb +43 -0
  125. data/lib/flipper/typecast.rb +59 -18
  126. data/lib/flipper/types/actor.rb +13 -13
  127. data/lib/flipper/types/group.rb +4 -4
  128. data/lib/flipper/types/percentage.rb +1 -1
  129. data/lib/flipper/version.rb +11 -1
  130. data/lib/flipper.rb +50 -11
  131. data/lib/generators/flipper/setup_generator.rb +68 -0
  132. data/lib/generators/flipper/templates/initializer.rb +45 -0
  133. data/lib/generators/flipper/templates/update/migrations/01_create_flipper_tables.rb.erb +22 -0
  134. data/lib/generators/flipper/templates/update/migrations/02_change_flipper_gates_value_to_text.rb.erb +18 -0
  135. data/lib/generators/flipper/update_generator.rb +35 -0
  136. data/package-lock.json +41 -0
  137. data/package.json +10 -0
  138. data/spec/fixtures/environment.rb +1 -0
  139. data/spec/fixtures/flipper_pstore_1679087600.json +46 -0
  140. data/spec/flipper/adapter_builder_spec.rb +72 -0
  141. data/spec/flipper/adapter_spec.rb +30 -2
  142. data/spec/flipper/adapters/actor_limit_spec.rb +20 -0
  143. data/spec/flipper/adapters/dual_write_spec.rb +2 -2
  144. data/spec/flipper/adapters/http/client_spec.rb +61 -0
  145. data/spec/flipper/adapters/http_spec.rb +138 -55
  146. data/spec/flipper/adapters/instrumented_spec.rb +29 -11
  147. data/spec/flipper/adapters/memoizable_spec.rb +51 -31
  148. data/spec/flipper/adapters/memory_spec.rb +14 -3
  149. data/spec/flipper/adapters/operation_logger_spec.rb +31 -12
  150. data/spec/flipper/adapters/poll_spec.rb +41 -0
  151. data/spec/flipper/adapters/read_only_spec.rb +32 -17
  152. data/spec/flipper/adapters/strict_spec.rb +64 -0
  153. data/spec/flipper/adapters/sync/feature_synchronizer_spec.rb +27 -0
  154. data/spec/flipper/cli_spec.rb +166 -0
  155. data/spec/flipper/cloud/configuration_spec.rb +251 -0
  156. data/spec/flipper/cloud/dsl_spec.rb +82 -0
  157. data/spec/flipper/cloud/message_verifier_spec.rb +104 -0
  158. data/spec/flipper/cloud/middleware_spec.rb +289 -0
  159. data/spec/flipper/cloud/telemetry/backoff_policy_spec.rb +107 -0
  160. data/spec/flipper/cloud/telemetry/metric_spec.rb +87 -0
  161. data/spec/flipper/cloud/telemetry/metric_storage_spec.rb +58 -0
  162. data/spec/flipper/cloud/telemetry/submitter_spec.rb +145 -0
  163. data/spec/flipper/cloud/telemetry_spec.rb +208 -0
  164. data/spec/flipper/cloud_spec.rb +186 -0
  165. data/spec/flipper/configuration_spec.rb +17 -0
  166. data/spec/flipper/dsl_spec.rb +54 -76
  167. data/spec/flipper/engine_spec.rb +374 -0
  168. data/spec/flipper/export_spec.rb +13 -0
  169. data/spec/flipper/exporter_spec.rb +16 -0
  170. data/spec/flipper/exporters/json/export_spec.rb +60 -0
  171. data/spec/flipper/exporters/json/v1_spec.rb +33 -0
  172. data/spec/flipper/expression/builder_spec.rb +248 -0
  173. data/spec/flipper/expression_spec.rb +188 -0
  174. data/spec/flipper/expressions/all_spec.rb +15 -0
  175. data/spec/flipper/expressions/any_spec.rb +15 -0
  176. data/spec/flipper/expressions/boolean_spec.rb +15 -0
  177. data/spec/flipper/expressions/duration_spec.rb +43 -0
  178. data/spec/flipper/expressions/equal_spec.rb +24 -0
  179. data/spec/flipper/expressions/greater_than_or_equal_to_spec.rb +28 -0
  180. data/spec/flipper/expressions/greater_than_spec.rb +28 -0
  181. data/spec/flipper/expressions/less_than_or_equal_to_spec.rb +28 -0
  182. data/spec/flipper/expressions/less_than_spec.rb +32 -0
  183. data/spec/flipper/expressions/not_equal_spec.rb +15 -0
  184. data/spec/flipper/expressions/now_spec.rb +11 -0
  185. data/spec/flipper/expressions/number_spec.rb +21 -0
  186. data/spec/flipper/expressions/percentage_of_actors_spec.rb +20 -0
  187. data/spec/flipper/expressions/percentage_spec.rb +15 -0
  188. data/spec/flipper/expressions/property_spec.rb +13 -0
  189. data/spec/flipper/expressions/random_spec.rb +9 -0
  190. data/spec/flipper/expressions/string_spec.rb +11 -0
  191. data/spec/flipper/expressions/time_spec.rb +13 -0
  192. data/spec/flipper/feature_check_context_spec.rb +17 -17
  193. data/spec/flipper/feature_spec.rb +453 -39
  194. data/spec/flipper/gate_values_spec.rb +2 -33
  195. data/spec/flipper/gates/boolean_spec.rb +1 -1
  196. data/spec/flipper/gates/expression_spec.rb +108 -0
  197. data/spec/flipper/gates/group_spec.rb +2 -3
  198. data/spec/flipper/gates/percentage_of_actors_spec.rb +61 -5
  199. data/spec/flipper/gates/percentage_of_time_spec.rb +2 -2
  200. data/spec/flipper/identifier_spec.rb +4 -5
  201. data/spec/flipper/instrumentation/log_subscriber_spec.rb +24 -6
  202. data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +26 -2
  203. data/spec/flipper/middleware/memoizer_spec.rb +79 -10
  204. data/spec/flipper/model/active_record_spec.rb +72 -0
  205. data/spec/flipper/poller_spec.rb +47 -0
  206. data/spec/flipper/serializers/gzip_spec.rb +13 -0
  207. data/spec/flipper/serializers/json_spec.rb +13 -0
  208. data/spec/flipper/typecast_spec.rb +121 -6
  209. data/spec/flipper/types/actor_spec.rb +63 -46
  210. data/spec/flipper/types/group_spec.rb +2 -2
  211. data/spec/flipper_integration_spec.rb +168 -58
  212. data/spec/flipper_spec.rb +94 -30
  213. data/spec/spec_helper.rb +18 -18
  214. data/spec/support/actor_names.yml +1 -0
  215. data/spec/support/fail_on_output.rb +8 -0
  216. data/spec/support/fake_backoff_policy.rb +15 -0
  217. data/spec/support/skippable.rb +18 -0
  218. data/spec/support/spec_helpers.rb +34 -8
  219. data/test/adapters/actor_limit_test.rb +20 -0
  220. data/test_rails/generators/flipper/setup_generator_test.rb +69 -0
  221. data/test_rails/generators/flipper/update_generator_test.rb +96 -0
  222. data/test_rails/helper.rb +22 -2
  223. data/test_rails/system/test_help_test.rb +52 -0
  224. metadata +203 -20
  225. data/.github/workflows/release.yml +0 -44
  226. data/.tool-versions +0 -1
  227. data/lib/flipper/railtie.rb +0 -47
  228. data/spec/flipper/railtie_spec.rb +0 -109
data/Rakefile CHANGED
@@ -27,7 +27,8 @@ end
27
27
 
28
28
  require 'rspec/core/rake_task'
29
29
  RSpec::Core::RakeTask.new(:spec) do |t|
30
- t.rspec_opts = %w(--color --format documentation)
30
+ t.rspec_opts = %w(--color)
31
+ t.verbose = false
31
32
  end
32
33
 
33
34
  namespace :spec do
@@ -41,7 +42,6 @@ end
41
42
  Rake::TestTask.new do |t|
42
43
  t.libs = %w(lib test)
43
44
  t.pattern = 'test/**/*_test.rb'
44
- t.options = '--documentation'
45
45
  t.warning = false
46
46
  end
47
47
 
@@ -0,0 +1,10 @@
1
+ require 'bundler/setup'
2
+ require 'flipper'
3
+ require 'benchmark/ips'
4
+
5
+ actor = Flipper::Actor.new("User;1")
6
+
7
+ Benchmark.ips do |x|
8
+ x.report("with actor") { Flipper.enabled?(:foo, actor) }
9
+ x.report("without actor") { Flipper.enabled?(:foo) }
10
+ end
@@ -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
Binary file
Binary file
@@ -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
@@ -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
+ 5.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 a thing with an identifier
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)
@@ -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