unleash 3.2.3 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/pull_request.yml +73 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +113 -3
- data/README.md +149 -18
- data/bin/unleash-client +15 -5
- data/examples/bootstrap.rb +51 -0
- data/examples/default-toggles.json +42 -0
- data/examples/simple.rb +4 -3
- data/lib/unleash/bootstrap/configuration.rb +25 -0
- data/lib/unleash/bootstrap/handler.rb +22 -0
- data/lib/unleash/bootstrap/provider/base.rb +14 -0
- data/lib/unleash/bootstrap/provider/from_file.rb +14 -0
- data/lib/unleash/bootstrap/provider/from_url.rb +19 -0
- data/lib/unleash/client.rb +26 -16
- data/lib/unleash/configuration.rb +26 -11
- data/lib/unleash/context.rb +1 -1
- data/lib/unleash/feature_toggle.rb +18 -13
- data/lib/unleash/metrics_reporter.rb +18 -4
- data/lib/unleash/scheduled_executor.rb +5 -2
- data/lib/unleash/strategy/application_hostname.rb +1 -0
- data/lib/unleash/strategy/flexible_rollout.rb +5 -5
- data/lib/unleash/strategy/remote_address.rb +17 -1
- data/lib/unleash/toggle_fetcher.rb +33 -13
- data/lib/unleash/util/http.rb +7 -6
- data/lib/unleash/variant_definition.rb +5 -4
- data/lib/unleash/version.rb +1 -1
- data/lib/unleash.rb +0 -5
- data/unleash-client.gemspec +2 -1
- metadata +31 -10
- data/.travis.yml +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 14f988b84b07e45401ad61a5814c1ca44185fc9358cef33f050b1d3bae45dd4e
|
4
|
+
data.tar.gz: afda9cf356088ef976047fe58d38d3b457fb1d8797cb7f519de6bdb953f43267
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c1ac85ea8b774a230212583b33b9fdab3110fadab2fa1652810ea0913312c1fd20abe4471761b5ccd399d66a52995c0ddb6b803ee42aa4c4432add8a5524e51d
|
7
|
+
data.tar.gz: b03da5caf2bf25683b2d61599a37f15582d26664553ef72096dfd3073aceb96224eaa46fd19c948f9f4fc143e3226ee176a4e66d762016aedf736fb903edae85
|
@@ -0,0 +1,73 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
pull_request:
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
test:
|
9
|
+
|
10
|
+
runs-on: ${{ matrix.os }}-latest
|
11
|
+
|
12
|
+
strategy:
|
13
|
+
matrix:
|
14
|
+
os:
|
15
|
+
- ubuntu
|
16
|
+
- macos
|
17
|
+
ruby-version:
|
18
|
+
- jruby-9.2
|
19
|
+
- jruby-9.3
|
20
|
+
- 3.1
|
21
|
+
- 3.0
|
22
|
+
- 2.7
|
23
|
+
- 2.6
|
24
|
+
- 2.5
|
25
|
+
|
26
|
+
steps:
|
27
|
+
- uses: actions/checkout@v2
|
28
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
29
|
+
uses: ruby/setup-ruby@v1
|
30
|
+
with:
|
31
|
+
bundler-cache: true
|
32
|
+
ruby-version: ${{ matrix.ruby-version }}
|
33
|
+
- name: Install dependencies
|
34
|
+
run: bundle install
|
35
|
+
- name: Download test cases
|
36
|
+
run: git clone --depth 5 --branch v4.0.0 https://github.com/Unleash/client-specification.git client-specification
|
37
|
+
- name: rubocop
|
38
|
+
uses: reviewdog/action-rubocop@v2
|
39
|
+
with:
|
40
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
41
|
+
rubocop_version: gemfile
|
42
|
+
rubocop_extensions: rubocop-rspec:gemfile
|
43
|
+
reporter: github-pr-review # Default is github-pr-check
|
44
|
+
- name: Run tests
|
45
|
+
run: bundle exec rake
|
46
|
+
env:
|
47
|
+
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
48
|
+
- name: Coveralls Parallel
|
49
|
+
uses: coverallsapp/github-action@master
|
50
|
+
with:
|
51
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
52
|
+
flag-name: run-${{ matrix.test_number }}
|
53
|
+
parallel: true
|
54
|
+
- name: Notify Slack of pipeline completion
|
55
|
+
uses: 8398a7/action-slack@v3
|
56
|
+
if: ${{ github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name }}
|
57
|
+
with:
|
58
|
+
status: ${{ job.status }}
|
59
|
+
text: Built on ${{ matrix.os }} - Ruby ${{ matrix.ruby-version }}
|
60
|
+
fields: repo,message,commit,author,action,eventName,ref,workflow,job,took
|
61
|
+
env:
|
62
|
+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
63
|
+
|
64
|
+
finish:
|
65
|
+
needs: test
|
66
|
+
runs-on: ubuntu-latest
|
67
|
+
steps:
|
68
|
+
- name: Coveralls Finished
|
69
|
+
uses: coverallsapp/github-action@master
|
70
|
+
with:
|
71
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
72
|
+
parallel-finished: true
|
73
|
+
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -14,17 +14,19 @@ Layout/LineLength:
|
|
14
14
|
Metrics/MethodLength:
|
15
15
|
Max: 20
|
16
16
|
Metrics/BlockLength:
|
17
|
-
Max:
|
17
|
+
Max: 110
|
18
18
|
Exclude:
|
19
|
+
- 'spec/unleash/configuration_spec.rb'
|
19
20
|
- 'spec/unleash/client_spec.rb'
|
21
|
+
- 'spec/unleash/context_spec.rb'
|
20
22
|
- 'spec/unleash/feature_toggle_spec.rb'
|
21
23
|
|
22
24
|
Metrics/AbcSize:
|
23
|
-
Max:
|
25
|
+
Max: 28
|
24
26
|
Metrics/CyclomaticComplexity:
|
25
27
|
Max: 9
|
26
28
|
Metrics/PerceivedComplexity:
|
27
|
-
Max:
|
29
|
+
Max: 10
|
28
30
|
|
29
31
|
Style/Documentation:
|
30
32
|
Enabled: false
|
@@ -51,6 +53,12 @@ Style/HashTransformKeys:
|
|
51
53
|
Enabled: true
|
52
54
|
Style/HashTransformValues:
|
53
55
|
Enabled: true
|
56
|
+
Style/EmptyElse:
|
57
|
+
Exclude:
|
58
|
+
- 'lib/unleash/strategy/flexible_rollout.rb'
|
59
|
+
|
60
|
+
Style/DoubleNegation:
|
61
|
+
Enabled: false
|
54
62
|
|
55
63
|
Style/IfInsideElse:
|
56
64
|
Exclude:
|
@@ -60,6 +68,61 @@ Style/Next:
|
|
60
68
|
Exclude:
|
61
69
|
- 'lib/unleash/scheduled_executor.rb'
|
62
70
|
|
71
|
+
|
72
|
+
Style/AccessorGrouping:
|
73
|
+
Enabled: true
|
74
|
+
Style/BisectedAttrAccessor:
|
75
|
+
Enabled: true
|
76
|
+
Style/CaseLikeIf:
|
77
|
+
Enabled: true
|
78
|
+
#Style/ClassEqualityComparison:
|
79
|
+
# Enabled: true
|
80
|
+
Style/CombinableLoops:
|
81
|
+
Enabled: true
|
82
|
+
Style/ExplicitBlockArgument:
|
83
|
+
Enabled: true
|
84
|
+
Style/ExponentialNotation:
|
85
|
+
Enabled: true
|
86
|
+
#Style/GlobalStdStream:
|
87
|
+
# Enabled: true
|
88
|
+
Style/HashAsLastArrayItem:
|
89
|
+
Enabled: true
|
90
|
+
Style/HashLikeCase:
|
91
|
+
Enabled: true
|
92
|
+
Style/KeywordParametersOrder:
|
93
|
+
Enabled: true
|
94
|
+
#Style/OptionalBooleanParameter:
|
95
|
+
# Enabled: false
|
96
|
+
Style/RedundantAssignment:
|
97
|
+
Enabled: true
|
98
|
+
Style/RedundantFetchBlock:
|
99
|
+
Enabled: true
|
100
|
+
Style/RedundantFileExtensionInRequire:
|
101
|
+
Enabled: true
|
102
|
+
Style/RedundantRegexpCharacterClass:
|
103
|
+
Enabled: true
|
104
|
+
Style/RedundantRegexpEscape:
|
105
|
+
Enabled: true
|
106
|
+
Style/RedundantSelfAssignment:
|
107
|
+
Enabled: true
|
108
|
+
Style/SingleArgumentDig:
|
109
|
+
Enabled: true
|
110
|
+
Style/SlicingWithRange:
|
111
|
+
Enabled: true
|
112
|
+
Style/SoleNestedConditional:
|
113
|
+
Enabled: true
|
114
|
+
Style/StringConcatenation:
|
115
|
+
Enabled: false
|
116
|
+
Style/TrailingCommaInHashLiteral:
|
117
|
+
Enabled: true
|
118
|
+
# EnforcedStyleForMultiline: consistent_comma
|
119
|
+
|
120
|
+
Layout/BeginEndAlignment:
|
121
|
+
Enabled: true
|
122
|
+
Layout/EmptyLinesAroundAttributeAccessor:
|
123
|
+
Enabled: true
|
124
|
+
Layout/SpaceAroundMethodCallOperator:
|
125
|
+
Enabled: true
|
63
126
|
Layout/MultilineMethodCallIndentation:
|
64
127
|
EnforcedStyle: indented
|
65
128
|
|
@@ -68,3 +131,50 @@ Layout/SpaceBeforeBlockBraces:
|
|
68
131
|
Exclude:
|
69
132
|
- 'unleash-client.gemspec'
|
70
133
|
- 'spec/**/*.rb'
|
134
|
+
|
135
|
+
Lint/BinaryOperatorWithIdenticalOperands:
|
136
|
+
Enabled: true
|
137
|
+
Lint/ConstantDefinitionInBlock:
|
138
|
+
Enabled: false
|
139
|
+
Lint/DeprecatedOpenSSLConstant:
|
140
|
+
Enabled: true
|
141
|
+
Lint/DuplicateElsifCondition:
|
142
|
+
Enabled: true
|
143
|
+
Lint/DuplicateRequire:
|
144
|
+
Enabled: true
|
145
|
+
Lint/DuplicateRescueException:
|
146
|
+
Enabled: true
|
147
|
+
Lint/EmptyConditionalBody:
|
148
|
+
Enabled: true
|
149
|
+
Lint/EmptyFile:
|
150
|
+
Enabled: true
|
151
|
+
Lint/FloatComparison:
|
152
|
+
Enabled: true
|
153
|
+
Lint/HashCompareByIdentity:
|
154
|
+
Enabled: true
|
155
|
+
Lint/IdentityComparison:
|
156
|
+
Enabled: true
|
157
|
+
Lint/MissingSuper:
|
158
|
+
Enabled: false
|
159
|
+
Lint/MixedRegexpCaptureTypes:
|
160
|
+
Enabled: true
|
161
|
+
Lint/OutOfRangeRegexpRef:
|
162
|
+
Enabled: true
|
163
|
+
Lint/RaiseException:
|
164
|
+
Enabled: true
|
165
|
+
Lint/RedundantSafeNavigation:
|
166
|
+
Enabled: true
|
167
|
+
Lint/SelfAssignment:
|
168
|
+
Enabled: true
|
169
|
+
Lint/StructNewOverride:
|
170
|
+
Enabled: true
|
171
|
+
Lint/TopLevelReturnWithArgument:
|
172
|
+
Enabled: true
|
173
|
+
Lint/TrailingCommaInAttributeDeclaration:
|
174
|
+
Enabled: true
|
175
|
+
Lint/UnreachableLoop:
|
176
|
+
Enabled: true
|
177
|
+
Lint/UselessMethodDefinition:
|
178
|
+
Enabled: true
|
179
|
+
Lint/UselessTimes:
|
180
|
+
Enabled: true
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Unleash::Client
|
2
2
|
|
3
|
-
|
4
|
-
[![Coverage Status](https://coveralls.io/repos/github/Unleash/unleash-client-ruby/badge.svg?branch=
|
3
|
+
![Build Status](https://github.com/Unleash/unleash-client-ruby/actions/workflows/pull_request.yml/badge.svg?branch=main)
|
4
|
+
[![Coverage Status](https://coveralls.io/repos/github/Unleash/unleash-client-ruby/badge.svg?branch=main)](https://coveralls.io/github/Unleash/unleash-client-ruby?branch=main)
|
5
5
|
[![Gem Version](https://badge.fury.io/rb/unleash.svg)](https://badge.fury.io/rb/unleash)
|
6
6
|
|
7
7
|
Unleash client so you can roll out your features with confidence.
|
@@ -10,18 +10,20 @@ Leverage the [Unleash Server](https://github.com/Unleash/unleash) for powerful f
|
|
10
10
|
|
11
11
|
## Supported Ruby Interpreters
|
12
12
|
|
13
|
+
* MRI 3.1
|
13
14
|
* MRI 3.0
|
14
15
|
* MRI 2.7
|
15
16
|
* MRI 2.6
|
16
17
|
* MRI 2.5
|
17
|
-
* jruby
|
18
|
+
* jruby 9.3
|
19
|
+
* jruby 9.2
|
18
20
|
|
19
21
|
## Installation
|
20
22
|
|
21
23
|
Add this line to your application's Gemfile:
|
22
24
|
|
23
25
|
```ruby
|
24
|
-
gem 'unleash', '~>
|
26
|
+
gem 'unleash', '~> 4.0.0'
|
25
27
|
```
|
26
28
|
|
27
29
|
And then execute:
|
@@ -34,22 +36,29 @@ Or install it yourself as:
|
|
34
36
|
|
35
37
|
## Configure
|
36
38
|
|
37
|
-
It is **required** to configure
|
39
|
+
It is **required** to configure:
|
40
|
+
- `url` of the unleash server
|
41
|
+
- `app_name` with the name of the runninng application.
|
42
|
+
- `custom_http_headers` with `{'Authorization': '<API token>'}` when using Unleash v4.0.0 and later.
|
38
43
|
|
39
|
-
|
44
|
+
Please substitute the example `'http://unleash.herokuapp.com/api'` for the url of your own instance.
|
45
|
+
|
46
|
+
It is **highly recommended** to configure:
|
47
|
+
- `instance_id` parameter with a unique identifier for the running instance.
|
40
48
|
|
41
49
|
|
42
50
|
```ruby
|
43
51
|
Unleash.configure do |config|
|
44
|
-
config.
|
45
|
-
config.
|
52
|
+
config.app_name = 'my_ruby_app'
|
53
|
+
config.url = 'http://unleash.herokuapp.com/api'
|
54
|
+
config.custom_http_headers = {'Authorization': '<API token>'}
|
46
55
|
end
|
47
56
|
```
|
48
57
|
|
49
58
|
or instantiate the client with the valid configuration:
|
50
59
|
|
51
60
|
```ruby
|
52
|
-
UNLEASH = Unleash::Client.new(url: 'http://unleash.herokuapp.com/api', app_name: 'my_ruby_app')
|
61
|
+
UNLEASH = Unleash::Client.new(url: 'http://unleash.herokuapp.com/api', app_name: 'my_ruby_app', custom_http_headers: {'Authorization': '<API token>'})
|
53
62
|
```
|
54
63
|
|
55
64
|
#### List of Arguments
|
@@ -60,19 +69,25 @@ Argument | Description | Required? | Type | Default Value|
|
|
60
69
|
`app_name` | Name of your program. | Y | String | N/A |
|
61
70
|
`instance_id` | Identifier for the running instance of program. Important so you can trace back to where metrics are being collected from. **Highly recommended be be set.** | N | String | random UUID |
|
62
71
|
`environment` | Environment the program is running on. Could be for example `prod` or `dev`. Not yet in use. | N | String | `default` |
|
72
|
+
`project_name` | Name of the project to retrieve features from. If not set, all feature flags will be retrieved. | N | String | nil |
|
63
73
|
`refresh_interval` | How often the unleash client should check with the server for configuration changes. | N | Integer | 15 |
|
64
|
-
`metrics_interval` | How often the unleash client should send metrics to server. | N | Integer |
|
74
|
+
`metrics_interval` | How often the unleash client should send metrics to server. | N | Integer | 60 |
|
65
75
|
`disable_client` | Disables all communication with the Unleash server, effectively taking it *offline*. If set, `is_enabled?` will always answer with the `default_value` and configuration validation is skipped. Defeats the entire purpose of using unleash, but can be useful in when running tests. | N | Boolean | `false` |
|
66
76
|
`disable_metrics` | Disables sending metrics to Unleash server. | N | Boolean | `false` |
|
67
|
-
`custom_http_headers` | Custom headers to send to Unleash. | N | Hash | {} |
|
77
|
+
`custom_http_headers` | Custom headers to send to Unleash. As of Unleash v4.0.0, the `Authorization` header is required. For example: `{'Authorization': '<API token>'}` | N | Hash | {} |
|
68
78
|
`timeout` | How long to wait for the connection to be established or wait in reading state (open_timeout/read_timeout) | N | Integer | 30 |
|
69
79
|
`retry_limit` | How many consecutive failures in connecting to the Unleash server are allowed before giving up. Use `Float::INFINITY` if you would like it to never give up. | N | Numeric | 5 |
|
70
80
|
`backup_file` | Filename to store the last known state from the Unleash server. Best to not change this from the default. | N | String | `Dir.tmpdir + "/unleash-#{app_name}-repo.json` |
|
71
81
|
`logger` | Specify a custom `Logger` class to handle logs for the Unleash client. | N | Class | `Logger.new(STDOUT)` |
|
72
|
-
`log_level` | Change the log level for the `Logger` class. Constant from `Logger::Severity`. | N | Constant | `Logger::
|
82
|
+
`log_level` | Change the log level for the `Logger` class. Constant from `Logger::Severity`. | N | Constant | `Logger::WARN` |
|
83
|
+
`bootstrap_config` | Bootstrap config on how to loaded data on start-up. This is useful for loading large states on startup without (or before) hitting the network. | N | Unleash::Bootstrap::Configuration | `nil` |
|
73
84
|
|
74
|
-
For
|
85
|
+
For a more in-depth look, please see `lib/unleash/configuration.rb`.
|
75
86
|
|
87
|
+
Environment Variable | Description
|
88
|
+
---------|---------
|
89
|
+
`UNLEASH_BOOTSTRAP_FILE` | File to read bootstrap data from
|
90
|
+
`UNLEASH_BOOTSTRAP_URL` | URL to read bootstrap data from
|
76
91
|
|
77
92
|
## Usage in a plain Ruby Application
|
78
93
|
|
@@ -80,7 +95,7 @@ For in a more in depth look, please see `lib/unleash/configuration.rb`.
|
|
80
95
|
require 'unleash'
|
81
96
|
require 'unleash/context'
|
82
97
|
|
83
|
-
@unleash = Unleash::Client.new(url: 'http://unleash.herokuapp.com/api',
|
98
|
+
@unleash = Unleash::Client.new(app_name: 'my_ruby_app', url: 'http://unleash.herokuapp.com/api', custom_http_headers: { 'Authorization': '<API token>' })
|
84
99
|
|
85
100
|
feature_name = "AwesomeFeature"
|
86
101
|
unleash_context = Unleash::Context.new
|
@@ -101,8 +116,8 @@ Put in `config/initializers/unleash.rb`:
|
|
101
116
|
|
102
117
|
```ruby
|
103
118
|
Unleash.configure do |config|
|
104
|
-
config.url = 'http://unleash.herokuapp.com/api'
|
105
119
|
config.app_name = Rails.application.class.parent.to_s
|
120
|
+
config.url = 'http://unleash.herokuapp.com/api'
|
106
121
|
# config.instance_id = "#{Socket.gethostname}"
|
107
122
|
config.logger = Rails.logger
|
108
123
|
config.environment = Rails.env
|
@@ -122,9 +137,10 @@ on_worker_boot do
|
|
122
137
|
# ...
|
123
138
|
|
124
139
|
Unleash.configure do |config|
|
125
|
-
config.
|
126
|
-
config.app_name = Rails.application.class.parent.to_s
|
140
|
+
config.app_name = Rails.application.class.parent.to_s
|
127
141
|
config.environment = Rails.env
|
142
|
+
config.url = 'http://unleash.herokuapp.com/api'
|
143
|
+
config.custom_http_headers = {'Authorization': '<API token>'}
|
128
144
|
end
|
129
145
|
Rails.configuration.unleash = Unleash::Client.new
|
130
146
|
end
|
@@ -132,6 +148,28 @@ end
|
|
132
148
|
|
133
149
|
Instead of the configuration in `config/initializers/unleash.rb`.
|
134
150
|
|
151
|
+
#### Add Initializer if using [Phusion Passenger](https://github.com/phusion/passenger)
|
152
|
+
|
153
|
+
The unleash client needs to be configured and instantiated inside the `PhusionPassenger.on_event(:starting_worker_process)` code block due to [smart spawning](https://www.phusionpassenger.com/library/indepth/ruby/spawn_methods/#smart-spawning-caveats):
|
154
|
+
|
155
|
+
The initializer in `config/initializers/unleash.rb` should look like:
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
159
|
+
if forked
|
160
|
+
Unleash.configure do |config|
|
161
|
+
config.app_name = Rails.application.class.parent.to_s
|
162
|
+
# config.instance_id = "#{Socket.gethostname}"
|
163
|
+
config.logger = Rails.logger
|
164
|
+
config.environment = Rails.env
|
165
|
+
config.url = 'http://unleash.herokuapp.com/api'
|
166
|
+
config.custom_http_headers = {'Authorization': '<API token>'}
|
167
|
+
end
|
168
|
+
|
169
|
+
UNLEASH = Unleash::Client.new
|
170
|
+
end
|
171
|
+
end
|
172
|
+
```
|
135
173
|
|
136
174
|
#### Set Unleash::Context
|
137
175
|
|
@@ -180,6 +218,40 @@ if UNLEASH.is_enabled? "AwesomeFeature", @unleash_context, true
|
|
180
218
|
end
|
181
219
|
```
|
182
220
|
|
221
|
+
Another possibility is to send a block, [Lambda](https://ruby-doc.org/core-3.0.1/Kernel.html#method-i-lambda) or [Proc](https://ruby-doc.org/core-3.0.1/Proc.html#method-i-yield)
|
222
|
+
to evaluate the default value:
|
223
|
+
|
224
|
+
```ruby
|
225
|
+
net_check_proc = proc do |feature_name, context|
|
226
|
+
context.remote_address.starts_with?("10.0.0.")
|
227
|
+
end
|
228
|
+
|
229
|
+
if UNLEASH.is_enabled?("AwesomeFeature", @unleash_context, &net_check_proc)
|
230
|
+
puts "AwesomeFeature is enabled by default if you are in the 10.0.0.* network."
|
231
|
+
end
|
232
|
+
```
|
233
|
+
|
234
|
+
or
|
235
|
+
|
236
|
+
```ruby
|
237
|
+
awesomeness = 10
|
238
|
+
@unleash_context.properties[:coolness] = 10
|
239
|
+
|
240
|
+
if UNLEASH.is_enabled?("AwesomeFeature", @unleash_context) { |feat, ctx| awesomeness >= 6 && ctx.properties[:coolness] >= 8 }
|
241
|
+
puts "AwesomeFeature is enabled by default if both the user has a high enought coolness and the application has a high enough awesomeness"
|
242
|
+
end
|
243
|
+
```
|
244
|
+
|
245
|
+
Note:
|
246
|
+
- The block/lambda/proc can use feature name and context as an arguments.
|
247
|
+
- The client will evaluate the fallback function once per call of `is_enabled()`.
|
248
|
+
Please keep this in mind when creating your fallback function!
|
249
|
+
- The returned value of the block should be a boolean.
|
250
|
+
However the client will coerce the result to boolean via `!!`.
|
251
|
+
- If both a `default_value` and `fallback_function` are supplied,
|
252
|
+
the client will define the default value by `OR`ing the default value and the output of the fallback function.
|
253
|
+
|
254
|
+
|
183
255
|
Alternatively by using `if_enabled` you can send a code block to be executed as a parameter:
|
184
256
|
|
185
257
|
```ruby
|
@@ -188,6 +260,8 @@ UNLEASH.if_enabled "AwesomeFeature", @unleash_context, true do
|
|
188
260
|
end
|
189
261
|
```
|
190
262
|
|
263
|
+
Note: `if_enabled` only supports `default_value`, but not `fallback_function`.
|
264
|
+
|
191
265
|
##### Variations
|
192
266
|
|
193
267
|
If no variant is found in the server, use the fallback variant.
|
@@ -199,6 +273,62 @@ variant = UNLEASH.get_variant "ColorVariants", @unleash_context, fallback_varian
|
|
199
273
|
puts "variant color is: #{variant.payload.fetch('color')}"
|
200
274
|
```
|
201
275
|
|
276
|
+
## Bootstrapping
|
277
|
+
|
278
|
+
Bootstrap configuration allows the client to be initialized with a predefined set of toggle states. Bootstrapping can be configured by providing a bootstrap configuration when initializing the client.
|
279
|
+
```ruby
|
280
|
+
@unleash = Unleash::Client.new(
|
281
|
+
url: 'http://unleash.herokuapp.com/api',
|
282
|
+
app_name: 'my_ruby_app',
|
283
|
+
custom_http_headers: { 'Authorization': '<API token>' },
|
284
|
+
bootstrap_config: Unleash::Bootstrap::Configuration.new({
|
285
|
+
url: "http://unleash.herokuapp.com/api/client/features",
|
286
|
+
url_headers: {'Authorization': '<API token>'}
|
287
|
+
})
|
288
|
+
)
|
289
|
+
```
|
290
|
+
The `Bootstrap::Configuration` initializer takes a hash with one of the following options specified:
|
291
|
+
|
292
|
+
* `file_path` - An absolute or relative path to a file containing a JSON string of the response body from the Unleash server. This can also be set though the `UNLEASH_BOOTSTRAP_FILE` environment variable.
|
293
|
+
* `url` - A url pointing to an Unleash server's features endpoint, the code sample above is illustrative. This can also be set though the `UNLEASH_BOOTSTRAP_URL` environment variable.
|
294
|
+
* `url_headers` - Headers for the GET http request to the `url` above. Only used if the `url` parameter is also set. If this option isn't set then the bootstrapper will use the same url headers as the Unleash client.
|
295
|
+
* `data` - A raw JSON string as returned by the Unleash server.
|
296
|
+
* `block` - A lambda containing custom logic if you need it, an example is provided below.
|
297
|
+
|
298
|
+
You should only specify one type of bootstrapping since only one will be invoked and the others will be ignored. The order of preference is as follows:
|
299
|
+
|
300
|
+
- Select a data bootstrapper if it exists.
|
301
|
+
- If no data bootstrapper exists, select the block bootstrapper.
|
302
|
+
- If no block bootstrapper exists, select the file bootstrapper from either parameters or the specified environment variable.
|
303
|
+
- If no file bootstrapper exists, then check for a URL bootstrapper from either the parameters or the specified environment variable.
|
304
|
+
|
305
|
+
|
306
|
+
Example usage:
|
307
|
+
|
308
|
+
First saving the toggles locally:
|
309
|
+
```shell
|
310
|
+
curl -H 'Authorization: <API token>' -XGET 'http://unleash.herokuapp.com/api' > ./default-toggles.json
|
311
|
+
```
|
312
|
+
|
313
|
+
Now using them on start up:
|
314
|
+
|
315
|
+
```ruby
|
316
|
+
|
317
|
+
custom_boostrapper = lambda {
|
318
|
+
File.read('./default-toggles.json')
|
319
|
+
}
|
320
|
+
|
321
|
+
@unleash = Unleash::Client.new(
|
322
|
+
app_name: 'my_ruby_app',
|
323
|
+
url: 'http://unleash.herokuapp.com/api',
|
324
|
+
custom_http_headers: { 'Authorization': '<API token>' },
|
325
|
+
bootstrap_config: Unleash::Bootstrap::Configuration.new({
|
326
|
+
block: custom_boostrapper
|
327
|
+
}
|
328
|
+
)
|
329
|
+
```
|
330
|
+
|
331
|
+
This example could be easily achieved with a file bootstrapper, this is just to illustrate the usage of custom bootstrapping. Be aware that the client initializer will block until bootstrapping is complete.
|
202
332
|
|
203
333
|
#### Client methods
|
204
334
|
|
@@ -211,6 +341,7 @@ Method Name | Description | Return Type |
|
|
211
341
|
`shutdown` | Save metrics to disk, flush metrics to server, and then kill ToggleFetcher and MetricsReporter threads. A safe shutdown. Not really useful in long running applications, like web applications. | nil |
|
212
342
|
`shutdown!` | Kill ToggleFetcher and MetricsReporter threads immediately. | nil |
|
213
343
|
|
344
|
+
For the full method signatures, please see [client.rb](lib/unleash/client.rb)
|
214
345
|
|
215
346
|
## Local test client
|
216
347
|
|
@@ -228,6 +359,7 @@ This client comes with the all the required strategies out of the box:
|
|
228
359
|
|
229
360
|
* ApplicationHostnameStrategy
|
230
361
|
* DefaultStrategy
|
362
|
+
* FlexibleRolloutStrategy
|
231
363
|
* GradualRolloutRandomStrategy
|
232
364
|
* GradualRolloutSessionIdStrategy
|
233
365
|
* GradualRolloutUserIdStrategy
|
@@ -235,7 +367,6 @@ This client comes with the all the required strategies out of the box:
|
|
235
367
|
* UnknownStrategy
|
236
368
|
* UserWithIdStrategy
|
237
369
|
|
238
|
-
|
239
370
|
## Development
|
240
371
|
|
241
372
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/bin/unleash-client
CHANGED
@@ -12,11 +12,13 @@ options = {
|
|
12
12
|
url: 'http://localhost:4242',
|
13
13
|
demo: false,
|
14
14
|
disable_metrics: true,
|
15
|
+
custom_http_headers: {},
|
15
16
|
sleep: 0.1
|
16
17
|
}
|
17
18
|
|
18
19
|
OptionParser.new do |opts|
|
19
|
-
opts.banner = "Usage: #{__FILE__} [options] feature [
|
20
|
+
opts.banner = "Usage: #{__FILE__} [options] feature [contextKey1=val1] [contextKey2=val2] \n\n" \
|
21
|
+
"Where contextKey1 could be user_id, session_id, remote_address or any field in the Context class (or any property within it).\n"
|
20
22
|
|
21
23
|
opts.on("-V", "--variant", "Fetch variant for feature") do |v|
|
22
24
|
options[:variant] = v
|
@@ -46,6 +48,13 @@ OptionParser.new do |opts|
|
|
46
48
|
options[:sleep] = s
|
47
49
|
end
|
48
50
|
|
51
|
+
opts.on("-H", "--http-headers='Authorization: *:developement.secretstring'",
|
52
|
+
"Adds http headers to all requests on the unleash server. Use multiple times for multiple headers.") do |h|
|
53
|
+
http_header_as_hash = [h].to_h{ |l| l.split(": ") }.transform_keys(&:to_sym)
|
54
|
+
|
55
|
+
options[:custom_http_headers].merge!(http_header_as_hash)
|
56
|
+
end
|
57
|
+
|
49
58
|
opts.on("-h", "--help", "Prints this help") do
|
50
59
|
puts opts
|
51
60
|
exit
|
@@ -70,10 +79,11 @@ log_level = \
|
|
70
79
|
url: options[:url],
|
71
80
|
app_name: 'unleash-client-ruby-cli',
|
72
81
|
disable_metrics: options[:metrics],
|
82
|
+
custom_http_headers: options[:custom_http_headers],
|
73
83
|
log_level: log_level
|
74
84
|
)
|
75
85
|
|
76
|
-
context_params = ARGV.
|
86
|
+
context_params = ARGV.to_h{ |l| l.split("=") }.transform_keys(&:to_sym)
|
77
87
|
context_properties = context_params.reject{ |k, _v| [:user_id, :session_id, :remote_address].include? k }
|
78
88
|
context_params.select!{ |k, _v| [:user_id, :session_id, :remote_address].include? k }
|
79
89
|
context_params.merge!(properties: context_properties) unless context_properties.nil?
|
@@ -97,12 +107,12 @@ if options[:demo]
|
|
97
107
|
end
|
98
108
|
elsif options[:variant]
|
99
109
|
variant = @unleash.get_variant(feature_name, unleash_context)
|
100
|
-
puts " For feature
|
110
|
+
puts " For feature '#{feature_name}' got variant '#{variant}'"
|
101
111
|
else
|
102
112
|
if @unleash.is_enabled?(feature_name, unleash_context)
|
103
|
-
puts "
|
113
|
+
puts " '#{feature_name}' is enabled according to unleash"
|
104
114
|
else
|
105
|
-
puts "
|
115
|
+
puts " '#{feature_name}' is disabled according to unleash"
|
106
116
|
end
|
107
117
|
end
|
108
118
|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'unleash'
|
4
|
+
require 'unleash/context'
|
5
|
+
require 'unleash/bootstrap/configuration'
|
6
|
+
|
7
|
+
puts ">> START bootstrap.rb"
|
8
|
+
|
9
|
+
@unleash = Unleash::Client.new(
|
10
|
+
url: 'http://unleash.herokuapp.com/api',
|
11
|
+
custom_http_headers: { 'Authorization': '943ca9171e2c884c545c5d82417a655fb77cec970cc3b78a8ff87f4406b495d0' },
|
12
|
+
app_name: 'bootstrap-test',
|
13
|
+
instance_id: 'local-test-cli',
|
14
|
+
refresh_interval: 2,
|
15
|
+
disable_client: true,
|
16
|
+
disable_metrics: true,
|
17
|
+
metrics_interval: 2,
|
18
|
+
retry_limit: 2,
|
19
|
+
bootstrap_config: Unleash::Bootstrap::Configuration.new(file_path: "examples/default-toggles.json")
|
20
|
+
)
|
21
|
+
|
22
|
+
feature_name = "featureX"
|
23
|
+
unleash_context = Unleash::Context.new
|
24
|
+
unleash_context.user_id = 123
|
25
|
+
|
26
|
+
sleep 1
|
27
|
+
3.times do
|
28
|
+
if @unleash.is_enabled?(feature_name, unleash_context)
|
29
|
+
puts "> #{feature_name} is enabled"
|
30
|
+
else
|
31
|
+
puts "> #{feature_name} is not enabled"
|
32
|
+
end
|
33
|
+
sleep 1
|
34
|
+
puts "---"
|
35
|
+
puts ""
|
36
|
+
puts ""
|
37
|
+
end
|
38
|
+
|
39
|
+
sleep 3
|
40
|
+
feature_name = "foobar"
|
41
|
+
if @unleash.is_enabled?(feature_name, unleash_context, true)
|
42
|
+
puts "> #{feature_name} is enabled"
|
43
|
+
else
|
44
|
+
puts "> #{feature_name} is not enabled"
|
45
|
+
end
|
46
|
+
|
47
|
+
puts "> shutting down client..."
|
48
|
+
|
49
|
+
@unleash.shutdown
|
50
|
+
|
51
|
+
puts ">> END bootstrap.rb"
|
@@ -0,0 +1,42 @@
|
|
1
|
+
{
|
2
|
+
"version": 1,
|
3
|
+
"features": [
|
4
|
+
{
|
5
|
+
"name": "featureX",
|
6
|
+
"enabled": true,
|
7
|
+
"strategies": [
|
8
|
+
{
|
9
|
+
"name": "default"
|
10
|
+
}
|
11
|
+
]
|
12
|
+
},
|
13
|
+
{
|
14
|
+
"name": "featureY",
|
15
|
+
"enabled": false,
|
16
|
+
"strategies": [
|
17
|
+
{
|
18
|
+
"name": "baz",
|
19
|
+
"parameters": {
|
20
|
+
"foo": "bar"
|
21
|
+
}
|
22
|
+
}
|
23
|
+
]
|
24
|
+
},
|
25
|
+
{
|
26
|
+
"name": "featureZ",
|
27
|
+
"enabled": true,
|
28
|
+
"strategies": [
|
29
|
+
{
|
30
|
+
"name": "default"
|
31
|
+
},
|
32
|
+
{
|
33
|
+
"name": "hola",
|
34
|
+
"parameters": {
|
35
|
+
"name": "val"
|
36
|
+
}
|
37
|
+
}
|
38
|
+
]
|
39
|
+
}
|
40
|
+
]
|
41
|
+
}
|
42
|
+
|