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.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/ci.yml +7 -3
- data/.github/workflows/examples.yml +27 -5
- data/Changelog.md +42 -0
- data/Gemfile +4 -4
- data/README.md +13 -11
- data/benchmark/typecast_ips.rb +8 -0
- data/docs/images/flipper_cloud.png +0 -0
- data/examples/cloud/backoff_policy.rb +13 -0
- data/examples/cloud/cloud_setup.rb +16 -0
- data/examples/cloud/forked.rb +7 -2
- data/examples/cloud/threaded.rb +15 -18
- data/examples/expressions.rb +213 -0
- data/examples/strict.rb +18 -0
- data/flipper.gemspec +1 -2
- data/lib/flipper/actor.rb +6 -3
- data/lib/flipper/adapter.rb +10 -0
- data/lib/flipper/adapter_builder.rb +44 -0
- data/lib/flipper/adapters/dual_write.rb +1 -3
- data/lib/flipper/adapters/failover.rb +0 -4
- data/lib/flipper/adapters/failsafe.rb +0 -4
- data/lib/flipper/adapters/http/client.rb +26 -7
- data/lib/flipper/adapters/http/error.rb +1 -1
- data/lib/flipper/adapters/http.rb +18 -13
- data/lib/flipper/adapters/instrumented.rb +0 -4
- data/lib/flipper/adapters/memoizable.rb +14 -19
- data/lib/flipper/adapters/memory.rb +4 -6
- data/lib/flipper/adapters/operation_logger.rb +0 -4
- data/lib/flipper/adapters/poll.rb +1 -3
- data/lib/flipper/adapters/pstore.rb +17 -11
- data/lib/flipper/adapters/read_only.rb +4 -4
- data/lib/flipper/adapters/strict.rb +47 -0
- data/lib/flipper/adapters/sync/feature_synchronizer.rb +10 -1
- data/lib/flipper/adapters/sync.rb +0 -4
- data/lib/flipper/cloud/configuration.rb +121 -52
- data/lib/flipper/cloud/telemetry/backoff_policy.rb +93 -0
- data/lib/flipper/cloud/telemetry/instrumenter.rb +26 -0
- data/lib/flipper/cloud/telemetry/metric.rb +39 -0
- data/lib/flipper/cloud/telemetry/metric_storage.rb +30 -0
- data/lib/flipper/cloud/telemetry/submitter.rb +98 -0
- data/lib/flipper/cloud/telemetry.rb +183 -0
- data/lib/flipper/configuration.rb +25 -4
- data/lib/flipper/dsl.rb +51 -0
- data/lib/flipper/engine.rb +28 -3
- data/lib/flipper/exporters/json/export.rb +1 -1
- data/lib/flipper/exporters/json/v1.rb +1 -1
- data/lib/flipper/expression/builder.rb +73 -0
- data/lib/flipper/expression/constant.rb +25 -0
- data/lib/flipper/expression.rb +71 -0
- data/lib/flipper/expressions/all.rb +11 -0
- data/lib/flipper/expressions/any.rb +9 -0
- data/lib/flipper/expressions/boolean.rb +9 -0
- data/lib/flipper/expressions/comparable.rb +13 -0
- data/lib/flipper/expressions/duration.rb +28 -0
- data/lib/flipper/expressions/equal.rb +9 -0
- data/lib/flipper/expressions/greater_than.rb +9 -0
- data/lib/flipper/expressions/greater_than_or_equal_to.rb +9 -0
- data/lib/flipper/expressions/less_than.rb +9 -0
- data/lib/flipper/expressions/less_than_or_equal_to.rb +9 -0
- data/lib/flipper/expressions/not_equal.rb +9 -0
- data/lib/flipper/expressions/now.rb +9 -0
- data/lib/flipper/expressions/number.rb +9 -0
- data/lib/flipper/expressions/percentage.rb +9 -0
- data/lib/flipper/expressions/percentage_of_actors.rb +12 -0
- data/lib/flipper/expressions/property.rb +9 -0
- data/lib/flipper/expressions/random.rb +9 -0
- data/lib/flipper/expressions/string.rb +9 -0
- data/lib/flipper/expressions/time.rb +9 -0
- data/lib/flipper/feature.rb +55 -0
- data/lib/flipper/gate.rb +1 -0
- data/lib/flipper/gate_values.rb +5 -2
- data/lib/flipper/gates/expression.rb +75 -0
- data/lib/flipper/instrumentation/statsd_subscriber.rb +2 -4
- data/lib/flipper/middleware/memoizer.rb +29 -13
- data/lib/flipper/poller.rb +1 -1
- data/lib/flipper/serializers/gzip.rb +24 -0
- data/lib/flipper/serializers/json.rb +19 -0
- data/lib/flipper/spec/shared_adapter_specs.rb +29 -11
- data/lib/flipper/test/shared_adapter_test.rb +24 -5
- data/lib/flipper/typecast.rb +34 -6
- data/lib/flipper/types/percentage.rb +1 -1
- data/lib/flipper/version.rb +1 -1
- data/lib/flipper.rb +38 -1
- data/spec/flipper/adapter_builder_spec.rb +73 -0
- data/spec/flipper/adapter_spec.rb +1 -0
- data/spec/flipper/adapters/http_spec.rb +39 -5
- data/spec/flipper/adapters/memoizable_spec.rb +15 -15
- data/spec/flipper/adapters/read_only_spec.rb +26 -11
- data/spec/flipper/adapters/strict_spec.rb +62 -0
- data/spec/flipper/adapters/sync/feature_synchronizer_spec.rb +27 -0
- data/spec/flipper/cloud/configuration_spec.rb +6 -23
- data/spec/flipper/cloud/telemetry/backoff_policy_spec.rb +108 -0
- data/spec/flipper/cloud/telemetry/metric_spec.rb +87 -0
- data/spec/flipper/cloud/telemetry/metric_storage_spec.rb +58 -0
- data/spec/flipper/cloud/telemetry/submitter_spec.rb +145 -0
- data/spec/flipper/cloud/telemetry_spec.rb +156 -0
- data/spec/flipper/cloud_spec.rb +12 -12
- data/spec/flipper/configuration_spec.rb +17 -0
- data/spec/flipper/dsl_spec.rb +39 -0
- data/spec/flipper/engine_spec.rb +108 -7
- data/spec/flipper/exporters/json/v1_spec.rb +3 -3
- data/spec/flipper/expression/builder_spec.rb +248 -0
- data/spec/flipper/expression_spec.rb +188 -0
- data/spec/flipper/expressions/all_spec.rb +15 -0
- data/spec/flipper/expressions/any_spec.rb +15 -0
- data/spec/flipper/expressions/boolean_spec.rb +15 -0
- data/spec/flipper/expressions/duration_spec.rb +43 -0
- data/spec/flipper/expressions/equal_spec.rb +24 -0
- data/spec/flipper/expressions/greater_than_or_equal_to_spec.rb +28 -0
- data/spec/flipper/expressions/greater_than_spec.rb +28 -0
- data/spec/flipper/expressions/less_than_or_equal_to_spec.rb +28 -0
- data/spec/flipper/expressions/less_than_spec.rb +32 -0
- data/spec/flipper/expressions/not_equal_spec.rb +15 -0
- data/spec/flipper/expressions/now_spec.rb +11 -0
- data/spec/flipper/expressions/number_spec.rb +21 -0
- data/spec/flipper/expressions/percentage_of_actors_spec.rb +20 -0
- data/spec/flipper/expressions/percentage_spec.rb +15 -0
- data/spec/flipper/expressions/property_spec.rb +13 -0
- data/spec/flipper/expressions/random_spec.rb +9 -0
- data/spec/flipper/expressions/string_spec.rb +11 -0
- data/spec/flipper/expressions/time_spec.rb +13 -0
- data/spec/flipper/feature_spec.rb +360 -1
- data/spec/flipper/gate_values_spec.rb +2 -2
- data/spec/flipper/gates/expression_spec.rb +108 -0
- data/spec/flipper/identifier_spec.rb +4 -5
- data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +15 -1
- data/spec/flipper/middleware/memoizer_spec.rb +67 -0
- data/spec/flipper/serializers/gzip_spec.rb +13 -0
- data/spec/flipper/serializers/json_spec.rb +13 -0
- data/spec/flipper/typecast_spec.rb +43 -7
- data/spec/flipper/types/actor_spec.rb +18 -1
- data/spec/flipper_integration_spec.rb +102 -4
- data/spec/flipper_spec.rb +89 -1
- data/spec/spec_helper.rb +5 -0
- data/spec/support/actor_names.yml +1 -0
- data/spec/support/fake_backoff_policy.rb +15 -0
- data/spec/support/spec_helpers.rb +11 -3
- metadata +104 -18
- data/lib/flipper/cloud/instrumenter.rb +0 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b100ee572f33c8486771b22c3f56b52ba810b73838044e86fa28b161c3484859
|
4
|
+
data.tar.gz: cb16f4c82dab816911e1bea6edc3c177caf5f32f5893dd076a2e1fc2f8dab2f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cb69d068e21b5f695dc8c31e94eaa930e8d48c8de9127a4040ab104ce95da7caec71d433c5ebd5de4089910623ad2d938ee7ec10ea6019d5c10e9c2915040905
|
7
|
+
data.tar.gz: 1bb2e7272037c6e73788bf23f407a64576305d8a2834ef3b351d40b787d607ed765e4689d1d7cb298345803b2856e1db5c0384bc4c5fd3596b98dfbd1075ba58
|
data/.github/FUNDING.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
github: flippercloud
|
data/.github/workflows/ci.yml
CHANGED
@@ -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@
|
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@
|
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@
|
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@
|
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'
|
31
|
-
gem 'guard-rspec'
|
32
|
-
gem 'guard-bundler'
|
33
|
-
gem 'rb-fsevent'
|
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
|
[](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 & Ship
|
37
37
|
|
38
|
-
[💌 Subscribe](https://
|
38
|
+
[💌 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?
|
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
|
-
* **
|
76
|
-
* **
|
77
|
-
* **
|
78
|
-
* **personal environments** — 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** — 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** — 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** — 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** — 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** — every feature change and who made it.
|
81
79
|
* **rollbacks** — 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** — 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** — no need to bounce around from different application UIs or IRB consoles.
|
82
82
|
|
83
|
-
[](https://www.flippercloud.io)
|
83
|
+
[](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
|
data/benchmark/typecast_ips.rb
CHANGED
@@ -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
|
data/examples/cloud/forked.rb
CHANGED
@@ -5,11 +5,16 @@ require_relative "./cloud_setup"
|
|
5
5
|
require 'bundler/setup'
|
6
6
|
require 'flipper/cloud'
|
7
7
|
|
8
|
-
|
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
|
-
|
17
|
+
2.times do
|
13
18
|
threads << Thread.new do
|
14
19
|
loop do
|
15
20
|
sleep rand
|
data/examples/cloud/threaded.rb
CHANGED
@@ -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
|
-
|
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(
|
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
|
-
|
22
|
-
|
23
|
-
threads << Thread.new do
|
24
|
+
5.times.map { |i|
|
25
|
+
Thread.new {
|
24
26
|
loop do
|
25
27
|
sleep rand
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
-
else
|
30
|
-
puts "#{Time.now.to_i} Disabled!"
|
31
|
-
end
|
29
|
+
Flipper.enabled?(:stats)
|
30
|
+
Flipper.enabled?(:search)
|
32
31
|
end
|
33
|
-
|
34
|
-
|
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)
|
data/examples/strict.rb
ADDED
@@ -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 = '
|
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) &&
|
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
|
|
data/lib/flipper/adapter.rb
CHANGED
@@ -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
|
|