flipper 1.0.0 → 1.1.0

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 (140) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/ci.yml +7 -3
  4. data/.github/workflows/examples.yml +27 -5
  5. data/Changelog.md +42 -0
  6. data/Gemfile +4 -4
  7. data/README.md +13 -11
  8. data/benchmark/typecast_ips.rb +8 -0
  9. data/docs/images/flipper_cloud.png +0 -0
  10. data/examples/cloud/backoff_policy.rb +13 -0
  11. data/examples/cloud/cloud_setup.rb +16 -0
  12. data/examples/cloud/forked.rb +7 -2
  13. data/examples/cloud/threaded.rb +15 -18
  14. data/examples/expressions.rb +213 -0
  15. data/examples/strict.rb +18 -0
  16. data/flipper.gemspec +1 -2
  17. data/lib/flipper/actor.rb +6 -3
  18. data/lib/flipper/adapter.rb +10 -0
  19. data/lib/flipper/adapter_builder.rb +44 -0
  20. data/lib/flipper/adapters/dual_write.rb +1 -3
  21. data/lib/flipper/adapters/failover.rb +0 -4
  22. data/lib/flipper/adapters/failsafe.rb +0 -4
  23. data/lib/flipper/adapters/http/client.rb +26 -7
  24. data/lib/flipper/adapters/http/error.rb +1 -1
  25. data/lib/flipper/adapters/http.rb +18 -13
  26. data/lib/flipper/adapters/instrumented.rb +0 -4
  27. data/lib/flipper/adapters/memoizable.rb +14 -19
  28. data/lib/flipper/adapters/memory.rb +4 -6
  29. data/lib/flipper/adapters/operation_logger.rb +0 -4
  30. data/lib/flipper/adapters/poll.rb +1 -3
  31. data/lib/flipper/adapters/pstore.rb +17 -11
  32. data/lib/flipper/adapters/read_only.rb +4 -4
  33. data/lib/flipper/adapters/strict.rb +47 -0
  34. data/lib/flipper/adapters/sync/feature_synchronizer.rb +10 -1
  35. data/lib/flipper/adapters/sync.rb +0 -4
  36. data/lib/flipper/cloud/configuration.rb +121 -52
  37. data/lib/flipper/cloud/telemetry/backoff_policy.rb +93 -0
  38. data/lib/flipper/cloud/telemetry/instrumenter.rb +26 -0
  39. data/lib/flipper/cloud/telemetry/metric.rb +39 -0
  40. data/lib/flipper/cloud/telemetry/metric_storage.rb +30 -0
  41. data/lib/flipper/cloud/telemetry/submitter.rb +98 -0
  42. data/lib/flipper/cloud/telemetry.rb +183 -0
  43. data/lib/flipper/configuration.rb +25 -4
  44. data/lib/flipper/dsl.rb +51 -0
  45. data/lib/flipper/engine.rb +28 -3
  46. data/lib/flipper/exporters/json/export.rb +1 -1
  47. data/lib/flipper/exporters/json/v1.rb +1 -1
  48. data/lib/flipper/expression/builder.rb +73 -0
  49. data/lib/flipper/expression/constant.rb +25 -0
  50. data/lib/flipper/expression.rb +71 -0
  51. data/lib/flipper/expressions/all.rb +11 -0
  52. data/lib/flipper/expressions/any.rb +9 -0
  53. data/lib/flipper/expressions/boolean.rb +9 -0
  54. data/lib/flipper/expressions/comparable.rb +13 -0
  55. data/lib/flipper/expressions/duration.rb +28 -0
  56. data/lib/flipper/expressions/equal.rb +9 -0
  57. data/lib/flipper/expressions/greater_than.rb +9 -0
  58. data/lib/flipper/expressions/greater_than_or_equal_to.rb +9 -0
  59. data/lib/flipper/expressions/less_than.rb +9 -0
  60. data/lib/flipper/expressions/less_than_or_equal_to.rb +9 -0
  61. data/lib/flipper/expressions/not_equal.rb +9 -0
  62. data/lib/flipper/expressions/now.rb +9 -0
  63. data/lib/flipper/expressions/number.rb +9 -0
  64. data/lib/flipper/expressions/percentage.rb +9 -0
  65. data/lib/flipper/expressions/percentage_of_actors.rb +12 -0
  66. data/lib/flipper/expressions/property.rb +9 -0
  67. data/lib/flipper/expressions/random.rb +9 -0
  68. data/lib/flipper/expressions/string.rb +9 -0
  69. data/lib/flipper/expressions/time.rb +9 -0
  70. data/lib/flipper/feature.rb +55 -0
  71. data/lib/flipper/gate.rb +1 -0
  72. data/lib/flipper/gate_values.rb +5 -2
  73. data/lib/flipper/gates/expression.rb +75 -0
  74. data/lib/flipper/instrumentation/statsd_subscriber.rb +2 -4
  75. data/lib/flipper/middleware/memoizer.rb +29 -13
  76. data/lib/flipper/poller.rb +1 -1
  77. data/lib/flipper/serializers/gzip.rb +24 -0
  78. data/lib/flipper/serializers/json.rb +19 -0
  79. data/lib/flipper/spec/shared_adapter_specs.rb +29 -11
  80. data/lib/flipper/test/shared_adapter_test.rb +24 -5
  81. data/lib/flipper/typecast.rb +34 -6
  82. data/lib/flipper/types/percentage.rb +1 -1
  83. data/lib/flipper/version.rb +1 -1
  84. data/lib/flipper.rb +38 -1
  85. data/spec/flipper/adapter_builder_spec.rb +73 -0
  86. data/spec/flipper/adapter_spec.rb +1 -0
  87. data/spec/flipper/adapters/http_spec.rb +39 -5
  88. data/spec/flipper/adapters/memoizable_spec.rb +15 -15
  89. data/spec/flipper/adapters/read_only_spec.rb +26 -11
  90. data/spec/flipper/adapters/strict_spec.rb +62 -0
  91. data/spec/flipper/adapters/sync/feature_synchronizer_spec.rb +27 -0
  92. data/spec/flipper/cloud/configuration_spec.rb +6 -23
  93. data/spec/flipper/cloud/telemetry/backoff_policy_spec.rb +108 -0
  94. data/spec/flipper/cloud/telemetry/metric_spec.rb +87 -0
  95. data/spec/flipper/cloud/telemetry/metric_storage_spec.rb +58 -0
  96. data/spec/flipper/cloud/telemetry/submitter_spec.rb +145 -0
  97. data/spec/flipper/cloud/telemetry_spec.rb +156 -0
  98. data/spec/flipper/cloud_spec.rb +12 -12
  99. data/spec/flipper/configuration_spec.rb +17 -0
  100. data/spec/flipper/dsl_spec.rb +39 -0
  101. data/spec/flipper/engine_spec.rb +108 -7
  102. data/spec/flipper/exporters/json/v1_spec.rb +3 -3
  103. data/spec/flipper/expression/builder_spec.rb +248 -0
  104. data/spec/flipper/expression_spec.rb +188 -0
  105. data/spec/flipper/expressions/all_spec.rb +15 -0
  106. data/spec/flipper/expressions/any_spec.rb +15 -0
  107. data/spec/flipper/expressions/boolean_spec.rb +15 -0
  108. data/spec/flipper/expressions/duration_spec.rb +43 -0
  109. data/spec/flipper/expressions/equal_spec.rb +24 -0
  110. data/spec/flipper/expressions/greater_than_or_equal_to_spec.rb +28 -0
  111. data/spec/flipper/expressions/greater_than_spec.rb +28 -0
  112. data/spec/flipper/expressions/less_than_or_equal_to_spec.rb +28 -0
  113. data/spec/flipper/expressions/less_than_spec.rb +32 -0
  114. data/spec/flipper/expressions/not_equal_spec.rb +15 -0
  115. data/spec/flipper/expressions/now_spec.rb +11 -0
  116. data/spec/flipper/expressions/number_spec.rb +21 -0
  117. data/spec/flipper/expressions/percentage_of_actors_spec.rb +20 -0
  118. data/spec/flipper/expressions/percentage_spec.rb +15 -0
  119. data/spec/flipper/expressions/property_spec.rb +13 -0
  120. data/spec/flipper/expressions/random_spec.rb +9 -0
  121. data/spec/flipper/expressions/string_spec.rb +11 -0
  122. data/spec/flipper/expressions/time_spec.rb +13 -0
  123. data/spec/flipper/feature_spec.rb +360 -1
  124. data/spec/flipper/gate_values_spec.rb +2 -2
  125. data/spec/flipper/gates/expression_spec.rb +108 -0
  126. data/spec/flipper/identifier_spec.rb +4 -5
  127. data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +15 -1
  128. data/spec/flipper/middleware/memoizer_spec.rb +67 -0
  129. data/spec/flipper/serializers/gzip_spec.rb +13 -0
  130. data/spec/flipper/serializers/json_spec.rb +13 -0
  131. data/spec/flipper/typecast_spec.rb +43 -7
  132. data/spec/flipper/types/actor_spec.rb +18 -1
  133. data/spec/flipper_integration_spec.rb +102 -4
  134. data/spec/flipper_spec.rb +89 -1
  135. data/spec/spec_helper.rb +5 -0
  136. data/spec/support/actor_names.yml +1 -0
  137. data/spec/support/fake_backoff_policy.rb +15 -0
  138. data/spec/support/spec_helpers.rb +11 -3
  139. metadata +104 -18
  140. data/lib/flipper/cloud/instrumenter.rb +0 -48
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bef9ba1845ca9f8186c9af57816c94309cdbc138e1e666a0dccbef5fba669682
4
- data.tar.gz: fbb79ccee4bdfd08e44c7eafe8052076dfd85c59959dd6c812473960bde0b95c
3
+ metadata.gz: b100ee572f33c8486771b22c3f56b52ba810b73838044e86fa28b161c3484859
4
+ data.tar.gz: cb16f4c82dab816911e1bea6edc3c177caf5f32f5893dd076a2e1fc2f8dab2f5
5
5
  SHA512:
6
- metadata.gz: 36693111d186b9581ce3b959b951c19db36f1f34dd220aebd1667adcfbf4916382983ccca63a44cdd9db9c0518aea53e6178a09e199db7dd25eee7ee796c07c0
7
- data.tar.gz: e9b95b9588f7b08ff3ed91db8583877bd087e28b5d5dd7c5ab9b09cd6c9f09b5632b2d6dace06d5d9a209f61b2b86a1f0aa775adea44b42c768294050c7149bd
6
+ metadata.gz: cb69d068e21b5f695dc8c31e94eaa930e8d48c8de9127a4040ab104ce95da7caec71d433c5ebd5de4089910623ad2d938ee7ec10ea6019d5c10e9c2915040905
7
+ data.tar.gz: 1bb2e7272037c6e73788bf23f407a64576305d8a2834ef3b351d40b787d607ed765e4689d1d7cb298345803b2856e1db5c0384bc4c5fd3596b98dfbd1075ba58
@@ -0,0 +1 @@
1
+ github: flippercloud
@@ -16,10 +16,14 @@ jobs:
16
16
  strategy:
17
17
  matrix:
18
18
  ruby: ['2.6', '2.7', '3.0', '3.1', '3.2']
19
- rails: ['5.2', '6.0.0', '6.1.0', '7.0.0']
19
+ rails: ['5.2', '6.0.0', '6.1.0', '7.0.0', '7.1.0']
20
20
  exclude:
21
+ - ruby: "2.6"
22
+ rails: "7.1.0"
21
23
  - ruby: "2.6"
22
24
  rails: "7.0.0"
25
+ - ruby: "2.7"
26
+ rails: "7.1.0"
23
27
  - ruby: "3.0"
24
28
  rails: "5.2"
25
29
  - ruby: "3.1"
@@ -41,11 +45,11 @@ jobs:
41
45
  - name: Setup memcached
42
46
  uses: KeisukeYamashita/memcached-actions@v1
43
47
  - name: Start MongoDB
44
- uses: supercharge/mongodb-github-action@1.9.0
48
+ uses: supercharge/mongodb-github-action@v1.10.0
45
49
  with:
46
50
  mongodb-version: 4.0
47
51
  - name: Check out repository code
48
- uses: actions/checkout@v3
52
+ uses: actions/checkout@v4
49
53
  - name: Do some action caching
50
54
  uses: actions/cache@v3
51
55
  with:
@@ -16,18 +16,27 @@ jobs:
16
16
  --health-retries 5
17
17
  strategy:
18
18
  matrix:
19
- ruby: ['2.6', '2.7', '3.0', '3.1']
20
- rails: ['5.2', '6.0.0', '6.1.0', '7.0.0']
19
+ ruby: ['2.6', '2.7', '3.0', '3.1', '3.2']
20
+ rails: ['5.2', '6.0.0', '6.1.0', '7.0.0', '7.1.0']
21
21
  exclude:
22
+ - ruby: "2.6"
23
+ rails: "7.1.0"
22
24
  - ruby: "2.6"
23
25
  rails: "7.0.0"
26
+ - ruby: "2.7"
27
+ rails: "7.1.0"
24
28
  - ruby: "3.0"
25
29
  rails: "5.2"
26
30
  - ruby: "3.1"
27
31
  rails: "5.2"
28
32
  - ruby: "3.1"
29
33
  rails: "6.0.0"
30
-
34
+ - ruby: "3.2"
35
+ rails: "5.2"
36
+ - ruby: "3.2"
37
+ rails: "6.0.0"
38
+ - ruby: "3.2"
39
+ rails: "6.1.0"
31
40
  env:
32
41
  SQLITE3_VERSION: 1.4.1
33
42
  REDIS_URL: redis://localhost:6379/0
@@ -37,11 +46,11 @@ jobs:
37
46
  - name: Setup memcached
38
47
  uses: KeisukeYamashita/memcached-actions@v1
39
48
  - name: Start MongoDB
40
- uses: supercharge/mongodb-github-action@1.9.0
49
+ uses: supercharge/mongodb-github-action@v1.10.0
41
50
  with:
42
51
  mongodb-version: 4.0
43
52
  - name: Check out repository code
44
- uses: actions/checkout@v3
53
+ uses: actions/checkout@v4
45
54
  - name: Do some action caching
46
55
  uses: actions/cache@v3
47
56
  with:
@@ -59,4 +68,17 @@ jobs:
59
68
  - name: Run Examples with Rails ${{ matrix.rails }}
60
69
  env:
61
70
  FLIPPER_CLOUD_TOKEN: ${{ secrets.FLIPPER_CLOUD_TOKEN }}
71
+ FLIPPER_CLOUD_TOKEN_26_52: ${{ secrets.FLIPPER_CLOUD_TOKEN_26_52 }}
72
+ FLIPPER_CLOUD_TOKEN_26_60: ${{ secrets.FLIPPER_CLOUD_TOKEN_26_60 }}
73
+ FLIPPER_CLOUD_TOKEN_26_61: ${{ secrets.FLIPPER_CLOUD_TOKEN_26_61 }}
74
+ FLIPPER_CLOUD_TOKEN_27_52: ${{ secrets.FLIPPER_CLOUD_TOKEN_27_52 }}
75
+ FLIPPER_CLOUD_TOKEN_27_60: ${{ secrets.FLIPPER_CLOUD_TOKEN_27_60 }}
76
+ FLIPPER_CLOUD_TOKEN_27_61: ${{ secrets.FLIPPER_CLOUD_TOKEN_27_61 }}
77
+ FLIPPER_CLOUD_TOKEN_27_70: ${{ secrets.FLIPPER_CLOUD_TOKEN_27_70 }}
78
+ FLIPPER_CLOUD_TOKEN_30_60: ${{ secrets.FLIPPER_CLOUD_TOKEN_30_60 }}
79
+ FLIPPER_CLOUD_TOKEN_30_61: ${{ secrets.FLIPPER_CLOUD_TOKEN_30_61 }}
80
+ FLIPPER_CLOUD_TOKEN_30_70: ${{ secrets.FLIPPER_CLOUD_TOKEN_30_70 }}
81
+ FLIPPER_CLOUD_TOKEN_31_61: ${{ secrets.FLIPPER_CLOUD_TOKEN_31_61 }}
82
+ FLIPPER_CLOUD_TOKEN_31_70: ${{ secrets.FLIPPER_CLOUD_TOKEN_31_70 }}
83
+ RAILS_VERSION: ${{ matrix.rails }}
62
84
  run: script/examples
data/Changelog.md CHANGED
@@ -2,6 +2,48 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## Unreleased
6
+
7
+ ### Additions/Changes
8
+
9
+ * Better read-only mode for UI (https://github.com/flippercloud/flipper/pull/772).
10
+ * Allow configuring preload via a block similar to memoize (https://github.com/flippercloud/flipper/pull/771).
11
+ ```ruby
12
+ Rails.application.configure do
13
+ # don't preload features for /assets/* but do for everything else
14
+ config.flipper.preload = ->(request) { !request.path.start_with?('/assets') }
15
+ end
16
+ ```
17
+ * Added `strict` configuration to warn when accessing a feature that doesn't exist (https://github.com/flippercloud/flipper/pull/760, https://github.com/flippercloud/flipper/pull/763)
18
+ ```ruby
19
+ Rails.application.configure do
20
+ # Setting to `true` or `:raise` will raise error when a feature doesn't exist.
21
+ # Use `:warn` to log a warning instead.
22
+ config.flipper.strict = !Rails.env.production?
23
+ end
24
+ ```
25
+ * Handle deprecation of Rack::File in Rack 3.1 (https://github.com/flippercloud/flipper/pull/773).
26
+ * Human readable actor names in flipper-ui (https://github.com/flippercloud/flipper/pull/737).
27
+ * Expressions are now available and considered "alpha". They are not yet documented, but you can see examples in [examples/expressions.rb](examples/expressions.rb). (https://github.com/flippercloud/flipper/pull/692)
28
+ * Allow head requests to api (https://github.com/flippercloud/flipper/pull/759)
29
+ * Cloud Telemetry alpha (https://github.com/flippercloud/flipper/pull/775).
30
+ * Easier statsd setup (https://github.com/flippercloud/flipper/pull/779).
31
+ ```ruby
32
+ # You can now just do this in your configure block and it'll require the files and configure the client.
33
+ Flipper.configure do |conf|
34
+ conf.statsd = my_client
35
+ end
36
+ ```
37
+ * Load cloud secrets from Rails credentials (https://github.com/flippercloud/flipper/pull/782)
38
+ ```bash
39
+ $ rails credentials:edit -e development
40
+ ```
41
+ ```yaml
42
+ flipper:
43
+ cloud_token: <your-cloud-token>
44
+ cloud_sync_secret: <your-webhook-secret>
45
+ ```
46
+
5
47
  ## 1.0.0
6
48
 
7
49
  ### Additions/Changes
data/Gemfile CHANGED
@@ -27,8 +27,8 @@ gem 'flamegraph'
27
27
  gem 'climate_control'
28
28
 
29
29
  group(:guard) do
30
- gem 'guard', '~> 2.15'
31
- gem 'guard-rspec', '~> 4.5'
32
- gem 'guard-bundler', '~> 2.2'
33
- gem 'rb-fsevent', '~> 0.9'
30
+ gem 'guard'
31
+ gem 'guard-rspec'
32
+ gem 'guard-bundler'
33
+ gem 'rb-fsevent'
34
34
  end
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) | [Documentation](https://flippercloud.io/docs) | [Examples](examples) | [Twitter](https://twitter.com/flipper_cloud)
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)
4
4
 
5
5
  # Flipper
6
6
 
@@ -35,7 +35,7 @@ Or install it yourself with:
35
35
 
36
36
  ## Subscribe &amp; Ship
37
37
 
38
- [💌 &nbsp;Subscribe](https://buttondown.email/flipper) - I'll send you short and sweet emails when we release new versions.
38
+ [💌 &nbsp;Subscribe](https://blog.flippercloud.io/#/portal/signup) - we'll send you short and sweet emails when we release new versions ([examples](https://blog.flippercloud.io/tag/releases/)).
39
39
 
40
40
  ## Getting Started
41
41
 
@@ -43,7 +43,7 @@ Use `Flipper#enabled?` in your app to check if a feature is enabled.
43
43
 
44
44
  ```ruby
45
45
  # check if search is enabled
46
- if Flipper.enabled? :search, current_user
46
+ if Flipper.enabled?(:search, current_user)
47
47
  puts 'Search away!'
48
48
  else
49
49
  puts 'No search for you!'
@@ -66,24 +66,26 @@ Flipper.enable_group :search, :admin
66
66
  Flipper.enable_percentage_of_actors :search, 2
67
67
  ```
68
68
 
69
- Read more about [getting started with Flipper](https://flippercloud.io/docs) and [enabling features](https://flippercloud.io/docs/features).
69
+ Read more about [getting started with Flipper](https://flippercloud.io/docs?utm_source=oss&utm_medium=readme&utm_campaign=getting_started) and [enabling features](https://flippercloud.io/docs/features?utm_source=oss&utm_medium=readme&utm_campaign=enabling_features).
70
70
 
71
71
  ## Flipper Cloud
72
72
 
73
- Like Flipper and want more? Check out [Flipper Cloud](https://www.flippercloud.io), which comes with:
73
+ Like Flipper and want more? Check out [Flipper Cloud](https://www.flippercloud.io?utm_source=oss&utm_medium=readme&utm_campaign=check_out), which comes with:
74
74
 
75
- * **everything in one place** &mdash; no need to bounce around from different application UIs or IRB consoles.
76
- * **permissions** &mdash; grant access to everyone in your organization or lockdown each project to particular people.
77
- * **multiple environments** &mdash; production, staging, enterprise, by continent, whatever you need.
78
- * **personal environments** &mdash; no more rake scripts or manual enable/disable to get your laptop to look like production. Every developer gets a personal environment that inherits from production that they can override as they please ([read more](https://www.johnnunemaker.com/flipper-cloud-environments/)).
79
- * **no maintenance** &mdash; we'll keep the lights on for you. We also have handy webhooks for keeping your app in sync with Cloud, so **our availability won't affect yours**. All your feature flag reads are local to your app.
75
+ * **multiple environments** &mdash; production, staging, per continent, whatever you need. Every environment inherits from production by default and every project comes with a [project overview page](https://blog.flippercloud.io/project-overview/) that shows each feature and its status in each environment.
76
+ * **personal environments** &mdash; everyone on your team gets a personal environment (that inherits from production) which they can modify however they want without stepping on anyone else's toes.
77
+ * **permissions** &mdash; grant access to everyone in your organization or lockdown each project to particular people. You can even limit access to a particular environment (like production) to specific people.
80
78
  * **audit history** &mdash; every feature change and who made it.
81
79
  * **rollbacks** &mdash; enable or disable a feature accidentally? No problem. You can roll back to any point in the audit history with a single click.
80
+ * **maintenance** &mdash; we'll keep the lights on for you. We also have handy webhooks and background polling for keeping your app in sync with Cloud, so **our availability won't affect yours**. All your feature flag reads are local to your app.
81
+ * **everything in one place** &mdash; no need to bounce around from different application UIs or IRB consoles.
82
82
 
83
- [![Flipper Cloud Screenshot](docs/images/flipper_cloud.png)](https://www.flippercloud.io)
83
+ [![Flipper Cloud Screenshot](docs/images/flipper_cloud.png)](https://www.flippercloud.io?utm_source=oss&utm_medium=readme&utm_campaign=screenshot)
84
84
 
85
85
  Cloud is super simple to integrate with Rails ([demo app](https://github.com/fewerandfaster/flipper-rails-demo)), Sinatra or any other framework.
86
86
 
87
+ We also have a [free plan](https://www.flippercloud.io?utm_source=oss&utm_medium=readme&utm_campaign=free_plan) that you can use forever.
88
+
87
89
  ## Contributing
88
90
 
89
91
  1. Fork it
@@ -16,4 +16,12 @@ Benchmark.ips do |x|
16
16
  x.report("Typecast.to_float '1'") { Flipper::Typecast.to_float('1'.freeze) }
17
17
  x.report("Typecast.to_float 1.01") { Flipper::Typecast.to_float(1) }
18
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) }
19
27
  end
Binary file
@@ -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)"
@@ -2,3 +2,19 @@ if ENV["FLIPPER_CLOUD_TOKEN"].nil? || ENV["FLIPPER_CLOUD_TOKEN"].empty?
2
2
  warn "FLIPPER_CLOUD_TOKEN missing so skipping cloud example."
3
3
  exit
4
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
@@ -5,11 +5,16 @@ require_relative "./cloud_setup"
5
5
  require 'bundler/setup'
6
6
  require 'flipper/cloud'
7
7
 
8
- pids = 5.times.map do |n|
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|
9
14
  fork {
10
15
  # Check every second to see if the feature is enabled
11
16
  threads = []
12
- 5.times do
17
+ 2.times do
13
18
  threads << Thread.new do
14
19
  loop do
15
20
  sleep rand
@@ -4,33 +4,30 @@
4
4
  require_relative "./cloud_setup"
5
5
  require 'bundler/setup'
6
6
  require 'flipper/cloud'
7
- require "active_support/notifications"
8
- require "active_support/isolated_execution_state"
9
7
 
10
- ActiveSupport::Notifications.subscribe(/poller\.flipper/) do |*args|
11
- p args: args
12
- end
8
+ puts Process.pid
13
9
 
14
10
  Flipper.configure do |config|
15
11
  config.default {
16
- Flipper::Cloud.new(local_adapter: config.adapter, instrumenter: ActiveSupport::Notifications)
12
+ Flipper::Cloud.new(
13
+ local_adapter: config.adapter,
14
+ debug_output: STDOUT,
15
+ )
17
16
  }
18
17
  end
19
18
 
19
+ # You might want to do this at some point to see different results:
20
+ # Flipper.enable(:search)
21
+ # Flipper.disable(:stats)
22
+
20
23
  # Check every second to see if the feature is enabled
21
- threads = []
22
- 10.times do
23
- threads << Thread.new do
24
+ 5.times.map { |i|
25
+ Thread.new {
24
26
  loop do
25
27
  sleep rand
26
28
 
27
- if Flipper[:stats].enabled?
28
- puts "#{Time.now.to_i} Enabled!"
29
- else
30
- puts "#{Time.now.to_i} Disabled!"
31
- end
29
+ Flipper.enabled?(:stats)
30
+ Flipper.enabled?(:search)
32
31
  end
33
- end
34
- end
35
-
36
- threads.map(&:join)
32
+ }
33
+ }.each(&:join)
@@ -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,18 @@
1
+ require 'bundler/setup'
2
+ require 'flipper'
3
+
4
+ adapter = Flipper::Adapters::Strict.new(Flipper::Adapters::Memory.new)
5
+ flipper = Flipper.new(adapter)
6
+
7
+ begin
8
+ puts "Checking :unknown_feature, which should raise an error."
9
+ flipper.enabled?(:unknown_feature)
10
+ warn "An error was not raised, but should have been"
11
+ exit 1
12
+ rescue Flipper::Adapters::Strict::NotFound => exception
13
+ puts "Ok, the exepcted error was raised: #{exception.message}"
14
+ end
15
+
16
+ puts "Flipper.add(:new_feature)"
17
+ flipper.add(:new_feature)
18
+ puts "Flipper.enabled?(:new_feature) => #{flipper.enabled?(:new_feature)}"
data/flipper.gemspec CHANGED
@@ -23,7 +23,7 @@ ignored_test_files.flatten!.uniq!
23
23
  Gem::Specification.new do |gem|
24
24
  gem.authors = ['John Nunemaker']
25
25
  gem.email = 'support@flippercloud.io'
26
- gem.summary = 'Feature flipper for ANYTHING'
26
+ gem.summary = 'Beautiful, performant feature flags for Ruby.'
27
27
  gem.homepage = 'https://www.flippercloud.io/docs'
28
28
  gem.license = 'MIT'
29
29
 
@@ -35,5 +35,4 @@ Gem::Specification.new do |gem|
35
35
  gem.metadata = Flipper::METADATA
36
36
 
37
37
  gem.add_dependency 'concurrent-ruby', '< 2'
38
- gem.add_dependency 'brow', '~> 0.4.1'
39
38
  end
data/lib/flipper/actor.rb CHANGED
@@ -2,14 +2,17 @@
2
2
  # to Flipper::Feature#enabled?.
3
3
  module Flipper
4
4
  class Actor
5
- attr_reader :flipper_id
5
+ attr_reader :flipper_id, :flipper_properties
6
6
 
7
- def initialize(flipper_id)
7
+ def initialize(flipper_id, flipper_properties = {})
8
8
  @flipper_id = flipper_id
9
+ @flipper_properties = flipper_properties
9
10
  end
10
11
 
11
12
  def eql?(other)
12
- self.class.eql?(other.class) && @flipper_id == other.flipper_id
13
+ self.class.eql?(other.class) &&
14
+ @flipper_id == other.flipper_id &&
15
+ @flipper_properties == other.flipper_properties
13
16
  end
14
17
  alias_method :==, :eql?
15
18
 
@@ -12,6 +12,7 @@ module Flipper
12
12
  boolean: nil,
13
13
  groups: Set.new,
14
14
  actors: Set.new,
15
+ expression: nil,
15
16
  percentage_of_actors: nil,
16
17
  percentage_of_time: nil,
17
18
  }
@@ -23,6 +24,10 @@ module Flipper
23
24
  end
24
25
  end
25
26
 
27
+ def read_only?
28
+ false
29
+ end
30
+
26
31
  # Public: Get all features and gate values in one call. Defaults to one call
27
32
  # to features and another to get_multi. Feel free to override per adapter to
28
33
  # make this more efficient.
@@ -63,6 +68,11 @@ module Flipper
63
68
  def default_config
64
69
  self.class.default_config
65
70
  end
71
+
72
+ # Public: default name of the adapter
73
+ def name
74
+ @name ||= self.class.name.split('::').last.split(/(?=[A-Z])/).join('_').downcase.to_sym
75
+ end
66
76
  end
67
77
  end
68
78