flipper 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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