flipper 0.22.1 → 0.24.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +1 -0
  3. data/.github/workflows/ci.yml +12 -3
  4. data/.github/workflows/examples.yml +14 -3
  5. data/.rspec +1 -0
  6. data/Changelog.md +50 -4
  7. data/Dockerfile +1 -1
  8. data/Gemfile +2 -2
  9. data/README.md +15 -67
  10. data/docs/README.md +1 -0
  11. data/docs/images/banner.jpg +0 -0
  12. data/lib/flipper/adapters/dual_write.rb +9 -15
  13. data/lib/flipper/adapters/failover.rb +83 -0
  14. data/lib/flipper/adapters/http/client.rb +11 -1
  15. data/lib/flipper/adapters/http/error.rb +19 -1
  16. data/lib/flipper/adapters/memoizable.rb +8 -16
  17. data/lib/flipper/adapters/pstore.rb +2 -5
  18. data/lib/flipper/adapters/sync/interval_synchronizer.rb +1 -6
  19. data/lib/flipper/adapters/sync.rb +9 -15
  20. data/lib/flipper/instrumenters/memory.rb +6 -2
  21. data/lib/flipper/railtie.rb +18 -17
  22. data/lib/flipper/types/group.rb +8 -1
  23. data/lib/flipper/version.rb +1 -1
  24. data/spec/flipper/actor_spec.rb +0 -2
  25. data/spec/flipper/adapter_spec.rb +0 -2
  26. data/spec/flipper/adapters/dual_write_spec.rb +0 -2
  27. data/spec/flipper/adapters/failover_spec.rb +129 -0
  28. data/spec/flipper/adapters/http_spec.rb +39 -3
  29. data/spec/flipper/adapters/instrumented_spec.rb +0 -2
  30. data/spec/flipper/adapters/memoizable_spec.rb +0 -2
  31. data/spec/flipper/adapters/memory_spec.rb +0 -3
  32. data/spec/flipper/adapters/operation_logger_spec.rb +0 -2
  33. data/spec/flipper/adapters/pstore_spec.rb +0 -2
  34. data/spec/flipper/adapters/read_only_spec.rb +0 -1
  35. data/spec/flipper/adapters/sync/feature_synchronizer_spec.rb +0 -1
  36. data/spec/flipper/adapters/sync/interval_synchronizer_spec.rb +4 -5
  37. data/spec/flipper/adapters/sync/synchronizer_spec.rb +0 -1
  38. data/spec/flipper/adapters/sync_spec.rb +0 -2
  39. data/spec/flipper/configuration_spec.rb +0 -1
  40. data/spec/flipper/dsl_spec.rb +0 -1
  41. data/spec/flipper/feature_check_context_spec.rb +0 -2
  42. data/spec/flipper/feature_spec.rb +0 -1
  43. data/spec/flipper/gate_spec.rb +0 -2
  44. data/spec/flipper/gate_values_spec.rb +0 -1
  45. data/spec/flipper/gates/actor_spec.rb +0 -2
  46. data/spec/flipper/gates/boolean_spec.rb +0 -2
  47. data/spec/flipper/gates/group_spec.rb +0 -2
  48. data/spec/flipper/gates/percentage_of_actors_spec.rb +0 -2
  49. data/spec/flipper/gates/percentage_of_time_spec.rb +0 -2
  50. data/spec/flipper/identifier_spec.rb +0 -1
  51. data/spec/flipper/instrumentation/log_subscriber_spec.rb +0 -1
  52. data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +0 -1
  53. data/spec/flipper/instrumenters/memory_spec.rb +18 -1
  54. data/spec/flipper/instrumenters/noop_spec.rb +14 -8
  55. data/spec/flipper/middleware/memoizer_spec.rb +0 -3
  56. data/spec/flipper/middleware/setup_env_spec.rb +0 -2
  57. data/spec/flipper/railtie_spec.rb +37 -33
  58. data/spec/flipper/registry_spec.rb +0 -1
  59. data/spec/flipper/typecast_spec.rb +0 -1
  60. data/spec/flipper/types/actor_spec.rb +0 -1
  61. data/spec/flipper/types/boolean_spec.rb +0 -1
  62. data/spec/flipper/types/group_spec.rb +0 -1
  63. data/spec/flipper/types/percentage_of_actors_spec.rb +0 -1
  64. data/spec/flipper/types/percentage_of_time_spec.rb +0 -1
  65. data/spec/flipper/types/percentage_spec.rb +0 -1
  66. data/spec/flipper_integration_spec.rb +0 -1
  67. data/spec/flipper_spec.rb +0 -1
  68. data/spec/{helper.rb → spec_helper.rb} +3 -1
  69. data/spec/support/spec_helpers.rb +2 -6
  70. metadata +10 -13
  71. data/docs/Adapters.md +0 -124
  72. data/docs/Caveats.md +0 -4
  73. data/docs/Gates.md +0 -167
  74. data/docs/Instrumentation.md +0 -27
  75. data/docs/Optimization.md +0 -137
  76. data/docs/api/README.md +0 -884
  77. data/docs/http/README.md +0 -36
  78. data/docs/read-only/README.md +0 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14514238ff1ea823df0cefeb2e1048816b86995d582e6c3dda037d36db344078
4
- data.tar.gz: 285650a29a45623ed0468a08736621ccf88a6453eeadd8f42a29740a4a2955bc
3
+ metadata.gz: f74cad5c410ce4146e29c11d692fb97d500c54301af8dab6b1c4b0da95ae79c8
4
+ data.tar.gz: 5e6f5613ea2166f1899e553601c555a87b9f47de169ef45b5f19c0d3aa77b3c9
5
5
  SHA512:
6
- metadata.gz: 3af99a75cafb349025fd03a6c2bab4bb224f76acd2a0ac820bf6306ded28235b615fc0f891ce8d3df7612eca26d631fc7a51fb6ee738fdfdb614a561b121af04
7
- data.tar.gz: bbaa9c0da5be73f4d1ab4ebc284919585878162e2c0151e346d77ad6909fddff7d0da7eff823ad29281d645e521d53e21f1de9184efda072e90e919d465a2c48
6
+ metadata.gz: 44f19778f0d3e35f99b909b4bd2fa2603b596b4cff77eb70d806c73453c1a1cf4a3d79ddf5c4ef593464099134505507152e1f392174f525e4815d5ca5592371
7
+ data.tar.gz: e999db32f892bbbf307c38f65b98b149489c830ba1b2530f2f5d308b837eb8a4d5d1f49f95242047d989e93b253a0599df8929fece5d78a9d959a2cad9dfac52
data/.codeclimate.yml CHANGED
@@ -1,2 +1,3 @@
1
1
  exclude_patterns:
2
2
  - "lib/flipper/ui/public"
3
+ - "spec/"
@@ -15,8 +15,17 @@ jobs:
15
15
  --health-retries 5
16
16
  strategy:
17
17
  matrix:
18
- ruby: ['2.5', '2.6', '2.7']
19
- rails: ['5.2', '6.0.0', '6.1.0']
18
+ ruby: ['2.6', '2.7', '3.0', '3.1']
19
+ rails: ['5.2', '6.0.0', '6.1.0', '7.0.0']
20
+ exclude:
21
+ - ruby: "2.6"
22
+ rails: "7.0.0"
23
+ - ruby: "3.0"
24
+ rails: "5.2"
25
+ - ruby: "3.1"
26
+ rails: "5.2"
27
+ - ruby: "3.1"
28
+ rails: "6.0.0"
20
29
  env:
21
30
  SQLITE3_VERSION: 1.4.1
22
31
  REDIS_URL: redis://localhost:6379/0
@@ -38,7 +47,7 @@ jobs:
38
47
  restore-keys: |
39
48
  ${{ runner.os }}-gems-${{ matrix.ruby }}-${{ matrix.rails }}-
40
49
  - name: Set up Ruby ${{ matrix.ruby }}
41
- uses: actions/setup-ruby@v1
50
+ uses: ruby/setup-ruby@v1
42
51
  with:
43
52
  ruby-version: ${{ matrix.ruby }}
44
53
  - name: Install libpq-dev
@@ -2,6 +2,7 @@ name: Examples
2
2
  on: [push, pull_request]
3
3
  jobs:
4
4
  test:
5
+ if: github.repository_owner == 'jnunemaker'
5
6
  name: Test on ruby ${{ matrix.ruby }} and rails ${{ matrix.rails }}
6
7
  runs-on: ubuntu-latest
7
8
  services:
@@ -15,8 +16,18 @@ jobs:
15
16
  --health-retries 5
16
17
  strategy:
17
18
  matrix:
18
- ruby: ['2.5', '2.6', '2.7']
19
- rails: ['5.2', '6.0.0', '6.1.0']
19
+ ruby: ['2.6', '2.7', '3.0', '3.1']
20
+ rails: ['5.2', '6.0.0', '6.1.0', '7.0.0']
21
+ exclude:
22
+ - ruby: "2.6"
23
+ rails: "7.0.0"
24
+ - ruby: "3.0"
25
+ rails: "5.2"
26
+ - ruby: "3.1"
27
+ rails: "5.2"
28
+ - ruby: "3.1"
29
+ rails: "6.0.0"
30
+
20
31
  env:
21
32
  SQLITE3_VERSION: 1.4.1
22
33
  REDIS_URL: redis://localhost:6379/0
@@ -38,7 +49,7 @@ jobs:
38
49
  restore-keys: |
39
50
  ${{ runner.os }}-gems-${{ matrix.ruby }}-${{ matrix.rails }}-
40
51
  - name: Set up Ruby ${{ matrix.ruby }}
41
- uses: actions/setup-ruby@v1
52
+ uses: ruby/setup-ruby@v1
42
53
  with:
43
54
  ruby-version: ${{ matrix.ruby }}
44
55
  - name: Install libpq-dev
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/Changelog.md CHANGED
@@ -1,6 +1,49 @@
1
+ ## 0.24.0
2
+
3
+ ### Additions/Changes
4
+
5
+ * Add Ruby 3.0 and 3.1 to the CI matrix and fix groups block arity check for ruby 3 (https://github.com/jnunemaker/flipper/pull/601)
6
+ * Removed support for Ruby 2.5 (which was end of line 9 months ago)
7
+ * Add (alpha) client side instrumentation of events to cloud (https://github.com/jnunemaker/flipper/pull/602)
8
+ * Fix deprecated uses of Redis#pipelined (https://github.com/jnunemaker/flipper/pull/603). redis-rb >= 3 now required.
9
+ * Fix Flipper UI Rack application when `Rack::Session::Pool` is used to build it.
10
+
11
+ ## 0.23.1
12
+
13
+ ### Additions/Changes
14
+
15
+ * Relax dalli version constraint (https://github.com/jnunemaker/flipper/pull/596)
16
+
17
+ ### Bug Fixes
18
+
19
+ * Fix railtie initialization to mount middleware after config/intializers/* (https://github.com/jnunemaker/flipper/pull/586)
20
+
21
+ ## 0.23.0
22
+
23
+ ### Additions/Changes
24
+
25
+ * Allow some HTML in banner and descriptions (https://github.com/jnunemaker/flipper/pull/570).
26
+ * Moved some cloud headers to http client (https://github.com/jnunemaker/flipper/pull/567).
27
+ * Update flipper-ui jquery and bootstrap versions (https://github.com/jnunemaker/flipper/issues/565 and https://github.com/jnunemaker/flipper/pull/566).
28
+ * Moved docs to www.flippercloud.io/docs (https://github.com/jnunemaker/flipper/pull/574).
29
+ * PStore adapter now defaults to thread safe and no longer supports `.thread_safe` (https://github.com/jnunemaker/flipper/commit/4048704fefe41b716015294a19a0b94546637630).
30
+ * Add failover adapter (https://github.com/jnunemaker/flipper/pull/584).
31
+ * Improve http adapter error message (https://github.com/jnunemaker/flipper/pull/587).
32
+ * Rails 7 support (mostly in https://github.com/jnunemaker/flipper/pull/592).
33
+
34
+ ## 0.22.2
35
+
36
+ ### Additions/Changes
37
+
38
+ * Allow adding multiple actors at once in flipper-ui via comma separation (configurable via `Flipper::UI.configuration.actors_separator`) (https://github.com/jnunemaker/flipper/pull/556)
39
+
40
+ ### Bug Fixes
41
+
42
+ * Fix railtie initialization to avoid altering middleware order (https://github.com/jnunemaker/flipper/pull/563)
43
+
1
44
  ## 0.22.1
2
45
 
3
- ## Additions/Changes
46
+ ### Additions/Changes
4
47
 
5
48
  * Remove Octicons and replace with a pure CSS status circle (https://github.com/jnunemaker/flipper/pull/547)
6
49
  * Rescue unique errors in AR and Sequel when setting value (https://github.com/jnunemaker/flipper/commit/87f5a98bce7baad7a27b75b5bce3256967769f27)
@@ -9,7 +52,7 @@
9
52
 
10
53
  ## 0.22.0
11
54
 
12
- ## Additions/Changes
55
+ ### Additions/Changes
13
56
 
14
57
  * Enable log subscriber by default in Rails (https://github.com/jnunemaker/flipper/pull/525)
15
58
  * Remove memoizer from API and UI (https://github.com/jnunemaker/flipper/pull/527). If you are using the UI or API without configuring the default instance of Flipper, you'll need to enable memoization if you want it. For examples, see the examples/ui and examples/api directories.
@@ -25,7 +68,7 @@
25
68
  * Added cloud recommendation to flipper-ui. Can be disabled with `Flipper::UI.configure { |config| config.cloud_recommendation = false }`. Just want to raise awareness that more is available if people want it (https://github.com/jnunemaker/flipper/pull/504)
26
69
  * Added default `flipper_id` implementation via `Flipper::Identifier` and automatically included it in ActiveRecord and Sequel models (https://github.com/jnunemaker/flipper/pull/505)
27
70
  * Deprecate superflous sync_method setting (https://github.com/jnunemaker/flipper/pull/511)
28
- * Flipper is now pre-configured when used with Rails. By default, it will [memoize and preload all features for each request](docs/Optimization.md#memoization). (https://github.com/jnunemaker/flipper/pull/506)
71
+ * Flipper is now pre-configured when used with Rails. By default, it will [memoize and preload all features for each request](https://flippercloud.io/docs/optimization#memoization). (https://github.com/jnunemaker/flipper/pull/506)
29
72
 
30
73
  ### Upgrading
31
74
 
@@ -40,7 +83,7 @@ You should be able to upgrade to 0.21 without any breaking changes. However, if
40
83
  - end
41
84
  ```
42
85
 
43
- 2. `Flipper::Middleware::Memoizer` will be enabled by default.
86
+ 2. `Flipper::Middleware::Memoizer` will be enabled by default -- including preloading. **Note**: You may want to disable preloading (see below) if you have > 100 features.
44
87
 
45
88
  ```diff
46
89
  # config/initializers/flipper.rb
@@ -49,6 +92,9 @@ You should be able to upgrade to 0.21 without any breaking changes. However, if
49
92
  + Rails.application.configure do
50
93
  + # Uncomment to configure which features to preload on all requests
51
94
  + # config.flipper.preload = [:stats, :search, :some_feature]
95
+ + #
96
+ + # Or, you may want to disable preloading entirely:
97
+ + # config.flipper.preload = false
52
98
  + end
53
99
  ```
54
100
 
data/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- FROM ruby:2.5
1
+ FROM ruby:3.0
2
2
 
3
3
  RUN apt-get update && apt-get install -y \
4
4
  # build-essential \
data/Gemfile CHANGED
@@ -13,11 +13,11 @@ gem 'statsd-ruby', '~> 1.2.1'
13
13
  gem 'rspec', '~> 3.0'
14
14
  gem 'rack-test', '~> 0.6.3'
15
15
  gem 'sqlite3', "~> #{ENV['SQLITE3_VERSION'] || '1.4.1'}"
16
- gem 'rails', "~> #{ENV['RAILS_VERSION'] || '6.0.0'}"
16
+ gem 'rails', "~> #{ENV['RAILS_VERSION'] || '7.0.0'}"
17
17
  gem 'minitest', '~> 5.8'
18
18
  gem 'minitest-documentation'
19
19
  gem 'webmock', '~> 3.0'
20
- gem 'climate_control'
20
+ gem 'ice_age'
21
21
  gem 'redis-namespace'
22
22
  gem 'webrick'
23
23
 
data/README.md CHANGED
@@ -1,5 +1,7 @@
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)
4
+
3
5
  # Flipper
4
6
 
5
7
  > Beautiful, performant feature flags for Ruby.
@@ -19,7 +21,7 @@ Add this line to your application's Gemfile:
19
21
 
20
22
  gem 'flipper'
21
23
 
22
- You'll also want to pick a storage [adapter](#adapters), for example:
24
+ You'll also want to pick a storage [adapter](https://flippercloud.io/docs/adapters), for example:
23
25
 
24
26
  gem 'flipper-active_record'
25
27
 
@@ -31,6 +33,10 @@ Or install it yourself with:
31
33
 
32
34
  $ gem install flipper
33
35
 
36
+ ## Subscribe & Ship
37
+
38
+ [💌  Subscribe](https://buttondown.email/flipper) - I'll send you short and sweet emails when we release new versions.
39
+
34
40
  ## Getting Started
35
41
 
36
42
  Use `Flipper#enabled?` in your app to check if a feature is enabled.
@@ -46,74 +52,25 @@ end
46
52
 
47
53
  All features are disabled by default, so you'll need to explicitly enable them.
48
54
 
49
- #### Enable a feature for everyone
50
-
51
55
  ```ruby
56
+ # Enable a feature for everyone
52
57
  Flipper.enable :search
53
- ```
54
58
 
55
- #### Enable a feature for a specific actor
56
-
57
- ```ruby
59
+ # Enable a feature for a specific actor
58
60
  Flipper.enable_actor :search, current_user
59
- ```
60
-
61
- #### Enable a feature for a group of actors
62
61
 
63
- First tell Flipper about your groups:
64
-
65
- ```ruby
66
- # config/initializers/flipper.rb
67
- Flipper.register(:admin) do |actor|
68
- actor.respond_to?(:admin?) && actor.admin?
69
- end
70
- ```
71
-
72
- Then enable the feature for that group:
73
-
74
- ```ruby
62
+ # Enable a feature for a group of actors
75
63
  Flipper.enable_group :search, :admin
76
- ```
77
-
78
- #### Enable a feature for a percentage of actors
79
64
 
80
- ```ruby
65
+ # Enable a feature for a percentage of actors
81
66
  Flipper.enable_percentage_of_actors :search, 2
82
67
  ```
83
68
 
84
-
85
- Read more about enabling and disabling features with [Gates](docs/Gates.md). Check out the [examples directory](examples/) for more, and take a peek at the [DSL](lib/flipper/dsl.rb) and [Feature](lib/flipper/feature.rb) classes for code/docs.
86
-
87
- ## Adapters
88
-
89
- Flipper is built on adapters for maximum flexibility. Regardless of what data store you are using, Flipper can performantly store data in it.
90
-
91
- Pick one of our [supported adapters](docs/Adapters.md#officially-supported) and follow the installation instructions:
92
-
93
- * [Active Record](docs/active_record/README.md)
94
- * [Sequel](docs/sequel/README.md)
95
- * [Redis](docs/redis/README.md)
96
- * [Mongo](docs/mongo/README.md)
97
- * [Moneta](docs/moneta/README.md)
98
- * [Rollout](docs/rollout/README.md)
99
-
100
- Or [roll your own](docs/Adapters.md#roll-your-own). We even provide automatic (rspec and minitest) tests for you, so you know you've built your custom adapter correctly.
101
-
102
- Read more about [Adapters](docs/Adapters.md).
103
-
104
- ## Flipper UI
105
-
106
- If you prefer a web UI to an IRB console, you can setup the [Flipper UI](docs/ui/README.md).
107
-
108
- It's simple and pretty.
109
-
110
- ![Flipper UI Screenshot](docs/ui/images/feature.png)
111
-
112
-
69
+ Read more about [getting started with Flipper](https://flippercloud.io/docs) and [enabling features](https://flippercloud.io/docs/features).
113
70
 
114
71
  ## Flipper Cloud
115
72
 
116
- Or, (even better than OSS + UI) use [Flipper Cloud](https://www.flippercloud.io) which comes with:
73
+ Like Flipper and want more? Check out [Flipper Cloud](https://www.flippercloud.io), which comes with:
117
74
 
118
75
  * **everything in one place** — no need to bounce around from different application UIs or IRB consoles.
119
76
  * **permissions** — grant access to everyone in your organization or lockdown each project to particular people.
@@ -127,21 +84,11 @@ Or, (even better than OSS + UI) use [Flipper Cloud](https://www.flippercloud.io)
127
84
 
128
85
  Cloud is super simple to integrate with Rails ([demo app](https://github.com/fewerandfaster/flipper-rails-demo)), Sinatra or any other framework.
129
86
 
130
- ## Advanced
131
-
132
- A few miscellaneous docs with more info for the hungry.
133
-
134
- * [Instrumentation](docs/Instrumentation.md) - ActiveSupport::Notifications and Statsd
135
- * [Optimization](docs/Optimization.md) - Memoization middleware and Cache adapters
136
- * [API](docs/api/README.md) - HTTP API interface
137
- * [Caveats](docs/Caveats.md) - Flipper beware! (see what I did there)
138
- * [Docker-Compose](docs/DockerCompose.md) - Using docker-compose in contributing
139
-
140
87
  ## Contributing
141
88
 
142
89
  1. Fork it
143
90
  2. Create your feature branch (`git checkout -b my-new-feature`)
144
- 3. Run the tests (`bundle exec rake`)
91
+ 3. Run the tests (`bundle exec rake`). Check out [Docker-Compose](docs/DockerCompose.md) if you need help getting all the adapters running.
145
92
  4. Commit your changes (`git commit -am 'Added some feature'`)
146
93
  5. Push to the branch (`git push origin my-new-feature`)
147
94
  6. Create new Pull Request
@@ -158,6 +105,7 @@ A few miscellaneous docs with more info for the hungry.
158
105
  |---|---|---|
159
106
  | ![@jnunemaker](https://avatars3.githubusercontent.com/u/235?s=64) | [@jnunemaker](https://github.com/jnunemaker) | most things |
160
107
  | ![@bkeepers](https://avatars3.githubusercontent.com/u/173?s=64) | [@bkeepers](https://github.com/bkeepers) | most things |
108
+ | ![@dpep](https://avatars3.githubusercontent.com/u/918804?s=64) | [@dpep](https://github.com/dpep) | tbd |
161
109
  | ![@alexwheeler](https://avatars3.githubusercontent.com/u/3260042?s=64) | [@alexwheeler](https://github.com/alexwheeler) | api |
162
110
  | ![@thetimbanks](https://avatars1.githubusercontent.com/u/471801?s=64) | [@thetimbanks](https://github.com/thetimbanks) | ui |
163
111
  | ![@lazebny](https://avatars1.githubusercontent.com/u/6276766?s=64) | [@lazebny](https://github.com/lazebny) | docker |
data/docs/README.md ADDED
@@ -0,0 +1 @@
1
+ See [flippercloud.io](https://flippercloud.io/docs) for extensive docs.
Binary file
@@ -34,33 +34,27 @@ module Flipper
34
34
  end
35
35
 
36
36
  def add(feature)
37
- result = @remote.add(feature)
38
- @local.add(feature)
39
- result
37
+ @remote.add(feature).tap { @local.add(feature) }
40
38
  end
41
39
 
42
40
  def remove(feature)
43
- result = @remote.remove(feature)
44
- @local.remove(feature)
45
- result
41
+ @remote.remove(feature).tap { @local.remove(feature) }
46
42
  end
47
43
 
48
44
  def clear(feature)
49
- result = @remote.clear(feature)
50
- @local.clear(feature)
51
- result
45
+ @remote.clear(feature).tap { @local.clear(feature) }
52
46
  end
53
47
 
54
48
  def enable(feature, gate, thing)
55
- result = @remote.enable(feature, gate, thing)
56
- @local.enable(feature, gate, thing)
57
- result
49
+ @remote.enable(feature, gate, thing).tap do
50
+ @local.enable(feature, gate, thing)
51
+ end
58
52
  end
59
53
 
60
54
  def disable(feature, gate, thing)
61
- result = @remote.disable(feature, gate, thing)
62
- @local.disable(feature, gate, thing)
63
- result
55
+ @remote.disable(feature, gate, thing).tap do
56
+ @local.disable(feature, gate, thing)
57
+ end
64
58
  end
65
59
  end
66
60
  end
@@ -0,0 +1,83 @@
1
+ module Flipper
2
+ module Adapters
3
+ class Failover
4
+ include ::Flipper::Adapter
5
+
6
+ # Public: The name of the adapter.
7
+ attr_reader :name
8
+
9
+ # Public: Build a new failover instance.
10
+ #
11
+ # primary - The primary flipper adapter.
12
+ # secondary - The secondary flipper adapter which services reads when
13
+ # the primary adapter is unavailable.
14
+ # options - Hash of options:
15
+ # :dual_write - Boolean, whether to update secondary when
16
+ # primary is updated
17
+ # :errors - Array of exception types for which to failover
18
+
19
+ def initialize(primary, secondary, options = {})
20
+ @name = :failover
21
+ @primary = primary
22
+ @secondary = secondary
23
+
24
+ @dual_write = options.fetch(:dual_write, false)
25
+ @errors = options.fetch(:errors, [ StandardError ])
26
+ end
27
+
28
+ def features
29
+ @primary.features
30
+ rescue *@errors
31
+ @secondary.features
32
+ end
33
+
34
+ def get(feature)
35
+ @primary.get(feature)
36
+ rescue *@errors
37
+ @secondary.get(feature)
38
+ end
39
+
40
+ def get_multi(features)
41
+ @primary.get_multi(features)
42
+ rescue *@errors
43
+ @secondary.get_multi(features)
44
+ end
45
+
46
+ def get_all
47
+ @primary.get_all
48
+ rescue *@errors
49
+ @secondary.get_all
50
+ end
51
+
52
+ def add(feature)
53
+ @primary.add(feature).tap do
54
+ @secondary.add(feature) if @dual_write
55
+ end
56
+ end
57
+
58
+ def remove(feature)
59
+ @primary.remove(feature).tap do
60
+ @secondary.remove(feature) if @dual_write
61
+ end
62
+ end
63
+
64
+ def clear(feature)
65
+ @primary.clear(feature).tap do
66
+ @secondary.clear(feature) if @dual_write
67
+ end
68
+ end
69
+
70
+ def enable(feature, gate, thing)
71
+ @primary.enable(feature, gate, thing).tap do
72
+ @secondary.enable(feature, gate, thing) if @dual_write
73
+ end
74
+ end
75
+
76
+ def disable(feature, gate, thing)
77
+ @primary.disable(feature, gate, thing).tap do
78
+ @secondary.disable(feature, gate, thing) if @dual_write
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -70,9 +70,19 @@ module Flipper
70
70
  end
71
71
 
72
72
  def build_request(http_method, uri, headers, options)
73
+ request_headers = {
74
+ "Client-Language" => "ruby",
75
+ "Client-Language-Version" => "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})",
76
+ "Client-Platform" => RUBY_PLATFORM,
77
+ "Client-Engine" => defined?(RUBY_ENGINE) ? RUBY_ENGINE : "",
78
+ "Client-Pid" => Process.pid.to_s,
79
+ "Client-Thread" => Thread.current.object_id.to_s,
80
+ "Client-Hostname" => Socket.gethostname,
81
+ }.merge(headers)
82
+
73
83
  body = options[:body]
74
84
  request = http_method.new(uri.request_uri)
75
- request.initialize_http_header(headers) if headers
85
+ request.initialize_http_header(request_headers)
76
86
  request.body = body if body
77
87
 
78
88
  if @basic_auth_username && @basic_auth_password
@@ -1,3 +1,5 @@
1
+ require "json"
2
+
1
3
  module Flipper
2
4
  module Adapters
3
5
  class Http
@@ -6,7 +8,23 @@ module Flipper
6
8
 
7
9
  def initialize(response)
8
10
  @response = response
9
- super("Failed with status: #{response.code}")
11
+ message = "Failed with status: #{response.code}"
12
+
13
+ begin
14
+ data = JSON.parse(response.body)
15
+
16
+ if error_message = data["message"]
17
+ message << "\n\n#{data["message"]}"
18
+ end
19
+
20
+ if more_info = data["more_info"]
21
+ message << "\n#{data["more_info"]}"
22
+ end
23
+ rescue => exception
24
+ # welp we tried
25
+ end
26
+
27
+ super(message)
10
28
  end
11
29
  end
12
30
  end
@@ -45,24 +45,20 @@ module Flipper
45
45
 
46
46
  # Public
47
47
  def add(feature)
48
- result = @adapter.add(feature)
49
- expire_features_set
50
- result
48
+ @adapter.add(feature).tap { expire_features_set }
51
49
  end
52
50
 
53
51
  # Public
54
52
  def remove(feature)
55
- result = @adapter.remove(feature)
56
- expire_features_set
57
- expire_feature(feature)
58
- result
53
+ @adapter.remove(feature).tap do
54
+ expire_features_set
55
+ expire_feature(feature)
56
+ end
59
57
  end
60
58
 
61
59
  # Public
62
60
  def clear(feature)
63
- result = @adapter.clear(feature)
64
- expire_feature(feature)
65
- result
61
+ @adapter.clear(feature).tap { expire_feature(feature) }
66
62
  end
67
63
 
68
64
  # Public
@@ -124,16 +120,12 @@ module Flipper
124
120
 
125
121
  # Public
126
122
  def enable(feature, gate, thing)
127
- result = @adapter.enable(feature, gate, thing)
128
- expire_feature(feature)
129
- result
123
+ @adapter.enable(feature, gate, thing).tap { expire_feature(feature) }
130
124
  end
131
125
 
132
126
  # Public
133
127
  def disable(feature, gate, thing)
134
- result = @adapter.disable(feature, gate, thing)
135
- expire_feature(feature)
136
- result
128
+ @adapter.disable(feature, gate, thing).tap { expire_feature(feature) }
137
129
  end
138
130
 
139
131
  # Internal: Turns local caching on/off.
@@ -17,14 +17,11 @@ module Flipper
17
17
  # Public: The path to where the file is stored.
18
18
  attr_reader :path
19
19
 
20
- # Public: PStore's thread_safe option.
21
- attr_reader :thread_safe
22
-
23
20
  # Public
24
- def initialize(path = 'flipper.pstore', thread_safe = false)
21
+ def initialize(path = 'flipper.pstore', thread_safe = true)
22
+ @name = :pstore
25
23
  @path = path
26
24
  @store = ::PStore.new(path, thread_safe)
27
- @name = :pstore
28
25
  end
29
26
 
30
27
  # Public: The set of known features.
@@ -7,11 +7,6 @@ module Flipper
7
7
  # Private: Number of seconds between syncs (default: 10).
8
8
  DEFAULT_INTERVAL = 10
9
9
 
10
- # Private
11
- def self.now
12
- Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
13
- end
14
-
15
10
  # Public: The Float or Integer number of seconds between invocations of
16
11
  # the wrapped synchronizer.
17
12
  attr_reader :interval
@@ -46,7 +41,7 @@ module Flipper
46
41
  end
47
42
 
48
43
  def now
49
- self.class.now
44
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
50
45
  end
51
46
  end
52
47
  end
@@ -58,33 +58,27 @@ module Flipper
58
58
  end
59
59
 
60
60
  def add(feature)
61
- result = @remote.add(feature)
62
- @local.add(feature)
63
- result
61
+ @remote.add(feature).tap { @local.add(feature) }
64
62
  end
65
63
 
66
64
  def remove(feature)
67
- result = @remote.remove(feature)
68
- @local.remove(feature)
69
- result
65
+ @remote.remove(feature).tap { @local.remove(feature) }
70
66
  end
71
67
 
72
68
  def clear(feature)
73
- result = @remote.clear(feature)
74
- @local.clear(feature)
75
- result
69
+ @remote.clear(feature).tap { @local.clear(feature) }
76
70
  end
77
71
 
78
72
  def enable(feature, gate, thing)
79
- result = @remote.enable(feature, gate, thing)
80
- @local.enable(feature, gate, thing)
81
- result
73
+ @remote.enable(feature, gate, thing).tap do
74
+ @local.enable(feature, gate, thing)
75
+ end
82
76
  end
83
77
 
84
78
  def disable(feature, gate, thing)
85
- result = @remote.disable(feature, gate, thing)
86
- @local.disable(feature, gate, thing)
87
- result
79
+ @remote.disable(feature, gate, thing).tap do
80
+ @local.disable(feature, gate, thing)
81
+ end
88
82
  end
89
83
 
90
84
  private
@@ -17,9 +17,13 @@ module Flipper
17
17
  # block rather than the one passed to #instrument.
18
18
  payload = payload.dup
19
19
 
20
- result = (yield payload if block_given?)
20
+ result = yield payload if block_given?
21
+ rescue Exception => e
22
+ payload[:exception] = [e.class.name, e.message]
23
+ payload[:exception_object] = e
24
+ raise e
25
+ ensure
21
26
  @events << Event.new(name, payload, result)
22
- result
23
27
  end
24
28
 
25
29
  def events_by_name(name)