unleash 3.2.5 → 4.0.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/workflows/pull_request.yml +71 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +113 -3
- data/README.md +82 -13
- data/bin/unleash-client +15 -5
- data/examples/simple.rb +4 -3
- data/lib/unleash/client.rb +18 -8
- data/lib/unleash/configuration.rb +15 -7
- 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/strategy/application_hostname.rb +1 -0
- data/lib/unleash/strategy/flexible_rollout.rb +5 -5
- data/lib/unleash/toggle_fetcher.rb +4 -3
- data/lib/unleash/util/http.rb +2 -4
- 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 +23 -9
- 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: 4ce69a2f4588a0d5e459e76aa8d78894497736f484ae20f5d987d50b34aced50
|
4
|
+
data.tar.gz: 24222c2c023197dc4f283ee4e09316f233245b2e82ca4c9ad934fe5519de75d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e056f24c5f840b211f04bc3c605ccf0846d1ff8717cc824d1e50307e636f28c50f91a8fe85f648de9f16377d5441d5feea459a15ad1eae00c35a6238ebb0355
|
7
|
+
data.tar.gz: 51b49947d4aea7d1901bf0083ef366604b5d1cd7859cab1cfaceacc420fb056c4188dd4512c1a7feddc5f9711e0c5ad2660e91c1e681cd57e47307f29948790c
|
@@ -0,0 +1,71 @@
|
|
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
|
19
|
+
- 3.0
|
20
|
+
- 2.7
|
21
|
+
- 2.6
|
22
|
+
- 2.5
|
23
|
+
|
24
|
+
steps:
|
25
|
+
- uses: actions/checkout@v2
|
26
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
27
|
+
uses: ruby/setup-ruby@v1
|
28
|
+
with:
|
29
|
+
bundler-cache: true
|
30
|
+
ruby-version: ${{ matrix.ruby-version }}
|
31
|
+
- name: Install dependencies
|
32
|
+
run: bundle install
|
33
|
+
- name: Download test cases
|
34
|
+
run: git clone --depth 5 --branch v4.0.0 https://github.com/Unleash/client-specification.git client-specification
|
35
|
+
- name: rubocop
|
36
|
+
uses: reviewdog/action-rubocop@v2
|
37
|
+
with:
|
38
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
39
|
+
rubocop_version: gemfile
|
40
|
+
rubocop_extensions: rubocop-rspec:gemfile
|
41
|
+
reporter: github-pr-review # Default is github-pr-check
|
42
|
+
- name: Run tests
|
43
|
+
run: bundle exec rake
|
44
|
+
env:
|
45
|
+
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
46
|
+
- name: Coveralls Parallel
|
47
|
+
uses: coverallsapp/github-action@master
|
48
|
+
with:
|
49
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
50
|
+
flag-name: run-${{ matrix.test_number }}
|
51
|
+
parallel: true
|
52
|
+
- name: Notify Slack of pipeline completion
|
53
|
+
uses: 8398a7/action-slack@v3
|
54
|
+
if: ${{ github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name }}
|
55
|
+
with:
|
56
|
+
status: ${{ job.status }}
|
57
|
+
text: Built on ${{ matrix.os }} - Ruby ${{ matrix.ruby-version }}
|
58
|
+
fields: repo,message,commit,author,action,eventName,ref,workflow,job,took
|
59
|
+
env:
|
60
|
+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
61
|
+
|
62
|
+
finish:
|
63
|
+
needs: test
|
64
|
+
runs-on: ubuntu-latest
|
65
|
+
steps:
|
66
|
+
- name: Coveralls Finished
|
67
|
+
uses: coverallsapp/github-action@master
|
68
|
+
with:
|
69
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
70
|
+
parallel-finished: true
|
71
|
+
|
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
@@ -21,7 +21,7 @@ Leverage the [Unleash Server](https://github.com/Unleash/unleash) for powerful f
|
|
21
21
|
Add this line to your application's Gemfile:
|
22
22
|
|
23
23
|
```ruby
|
24
|
-
gem 'unleash', '~>
|
24
|
+
gem 'unleash', '~> 4.0.0'
|
25
25
|
```
|
26
26
|
|
27
27
|
And then execute:
|
@@ -34,22 +34,29 @@ Or install it yourself as:
|
|
34
34
|
|
35
35
|
## Configure
|
36
36
|
|
37
|
-
It is **required** to configure
|
37
|
+
It is **required** to configure:
|
38
|
+
- `url` of the unleash server
|
39
|
+
- `app_name` with the name of the runninng application.
|
40
|
+
- `custom_http_headers` with `{'Authorization': '<API token>'}` when using Unleash v4.0.0 and later.
|
38
41
|
|
39
|
-
|
42
|
+
Please substitute the example `'http://unleash.herokuapp.com/api'` for the url of your own instance.
|
43
|
+
|
44
|
+
It is **highly recommended** to configure:
|
45
|
+
- `instance_id` parameter with a unique identifier for the running instance.
|
40
46
|
|
41
47
|
|
42
48
|
```ruby
|
43
49
|
Unleash.configure do |config|
|
44
|
-
config.
|
45
|
-
config.
|
50
|
+
config.app_name = 'my_ruby_app'
|
51
|
+
config.url = 'http://unleash.herokuapp.com/api'
|
52
|
+
config.custom_http_headers = {'Authorization': '<API token>'}
|
46
53
|
end
|
47
54
|
```
|
48
55
|
|
49
56
|
or instantiate the client with the valid configuration:
|
50
57
|
|
51
58
|
```ruby
|
52
|
-
UNLEASH = Unleash::Client.new(url: 'http://unleash.herokuapp.com/api', app_name: 'my_ruby_app')
|
59
|
+
UNLEASH = Unleash::Client.new(url: 'http://unleash.herokuapp.com/api', app_name: 'my_ruby_app', custom_http_headers: {'Authorization': '<API token>'})
|
53
60
|
```
|
54
61
|
|
55
62
|
#### List of Arguments
|
@@ -60,16 +67,17 @@ Argument | Description | Required? | Type | Default Value|
|
|
60
67
|
`app_name` | Name of your program. | Y | String | N/A |
|
61
68
|
`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
69
|
`environment` | Environment the program is running on. Could be for example `prod` or `dev`. Not yet in use. | N | String | `default` |
|
70
|
+
`project_name` | Name of the project to retrieve features from. If not set, all feature flags will be retrieved. | N | String | nil |
|
63
71
|
`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 |
|
72
|
+
`metrics_interval` | How often the unleash client should send metrics to server. | N | Integer | 30 |
|
65
73
|
`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
74
|
`disable_metrics` | Disables sending metrics to Unleash server. | N | Boolean | `false` |
|
67
|
-
`custom_http_headers` | Custom headers to send to Unleash. | N | Hash | {} |
|
75
|
+
`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
76
|
`timeout` | How long to wait for the connection to be established or wait in reading state (open_timeout/read_timeout) | N | Integer | 30 |
|
69
77
|
`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
78
|
`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
79
|
`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::
|
80
|
+
`log_level` | Change the log level for the `Logger` class. Constant from `Logger::Severity`. | N | Constant | `Logger::WARN` |
|
73
81
|
|
74
82
|
For in a more in depth look, please see `lib/unleash/configuration.rb`.
|
75
83
|
|
@@ -80,7 +88,7 @@ For in a more in depth look, please see `lib/unleash/configuration.rb`.
|
|
80
88
|
require 'unleash'
|
81
89
|
require 'unleash/context'
|
82
90
|
|
83
|
-
@unleash = Unleash::Client.new(url: 'http://unleash.herokuapp.com/api',
|
91
|
+
@unleash = Unleash::Client.new(app_name: 'my_ruby_app', url: 'http://unleash.herokuapp.com/api', custom_http_headers: {'Authorization': '<API token>'})
|
84
92
|
|
85
93
|
feature_name = "AwesomeFeature"
|
86
94
|
unleash_context = Unleash::Context.new
|
@@ -101,8 +109,8 @@ Put in `config/initializers/unleash.rb`:
|
|
101
109
|
|
102
110
|
```ruby
|
103
111
|
Unleash.configure do |config|
|
104
|
-
config.url = 'http://unleash.herokuapp.com/api'
|
105
112
|
config.app_name = Rails.application.class.parent.to_s
|
113
|
+
config.url = 'http://unleash.herokuapp.com/api'
|
106
114
|
# config.instance_id = "#{Socket.gethostname}"
|
107
115
|
config.logger = Rails.logger
|
108
116
|
config.environment = Rails.env
|
@@ -122,9 +130,10 @@ on_worker_boot do
|
|
122
130
|
# ...
|
123
131
|
|
124
132
|
Unleash.configure do |config|
|
125
|
-
config.
|
126
|
-
config.app_name = Rails.application.class.parent.to_s
|
133
|
+
config.app_name = Rails.application.class.parent.to_s
|
127
134
|
config.environment = Rails.env
|
135
|
+
config.url = 'http://unleash.herokuapp.com/api'
|
136
|
+
config.custom_http_headers = {'Authorization': '<API token>'}
|
128
137
|
end
|
129
138
|
Rails.configuration.unleash = Unleash::Client.new
|
130
139
|
end
|
@@ -132,6 +141,28 @@ end
|
|
132
141
|
|
133
142
|
Instead of the configuration in `config/initializers/unleash.rb`.
|
134
143
|
|
144
|
+
#### Add Initializer if using [Phusion Passenger](https://github.com/phusion/passenger)
|
145
|
+
|
146
|
+
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):
|
147
|
+
|
148
|
+
The initializer in `config/initializers/unleash.rb` should look like:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
152
|
+
if forked
|
153
|
+
Unleash.configure do |config|
|
154
|
+
config.app_name = Rails.application.class.parent.to_s
|
155
|
+
# config.instance_id = "#{Socket.gethostname}"
|
156
|
+
config.logger = Rails.logger
|
157
|
+
config.environment = Rails.env
|
158
|
+
config.url = 'http://unleash.herokuapp.com/api'
|
159
|
+
config.custom_http_headers = {'Authorization': '<API token>'}
|
160
|
+
end
|
161
|
+
|
162
|
+
UNLEASH = Unleash::Client.new
|
163
|
+
end
|
164
|
+
end
|
165
|
+
```
|
135
166
|
|
136
167
|
#### Set Unleash::Context
|
137
168
|
|
@@ -180,6 +211,40 @@ if UNLEASH.is_enabled? "AwesomeFeature", @unleash_context, true
|
|
180
211
|
end
|
181
212
|
```
|
182
213
|
|
214
|
+
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)
|
215
|
+
to evaluate the default value:
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
net_check_proc = proc do |feature_name, context|
|
219
|
+
context.remote_address.starts_with?("10.0.0.")
|
220
|
+
end
|
221
|
+
|
222
|
+
if UNLEASH.is_enabled?("AwesomeFeature", @unleash_context, &net_check_proc)
|
223
|
+
puts "AwesomeFeature is enabled by default if you are in the 10.0.0.* network."
|
224
|
+
end
|
225
|
+
```
|
226
|
+
|
227
|
+
or
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
awesomeness = 10
|
231
|
+
@unleash_context.properties[:coolness] = 10
|
232
|
+
|
233
|
+
if UNLEASH.is_enabled?("AwesomeFeature", @unleash_context) { |feat, ctx| awesomeness >= 6 && ctx.properties[:coolness] >= 8 }
|
234
|
+
puts "AwesomeFeature is enabled by default if both the user has a high enought coolness and the application has a high enough awesomeness"
|
235
|
+
end
|
236
|
+
```
|
237
|
+
|
238
|
+
Note:
|
239
|
+
- The block/lambda/proc can use feature name and context as an arguments.
|
240
|
+
- The client will evaluate the fallback function once per call of `is_enabled()`.
|
241
|
+
Please keep this in mind when creating your fallback function!
|
242
|
+
- The returned value of the block should be a boolean.
|
243
|
+
However the client will coerce the result to boolean via `!!`.
|
244
|
+
- If both a `default_value` and `fallback_function` are supplied,
|
245
|
+
the client will define the default value by `OR`ing the default value and the output of the fallback function.
|
246
|
+
|
247
|
+
|
183
248
|
Alternatively by using `if_enabled` you can send a code block to be executed as a parameter:
|
184
249
|
|
185
250
|
```ruby
|
@@ -188,6 +253,8 @@ UNLEASH.if_enabled "AwesomeFeature", @unleash_context, true do
|
|
188
253
|
end
|
189
254
|
```
|
190
255
|
|
256
|
+
Note: `if_enabled` only supports `default_value`, but not `fallback_function`.
|
257
|
+
|
191
258
|
##### Variations
|
192
259
|
|
193
260
|
If no variant is found in the server, use the fallback variant.
|
@@ -211,6 +278,7 @@ Method Name | Description | Return Type |
|
|
211
278
|
`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
279
|
`shutdown!` | Kill ToggleFetcher and MetricsReporter threads immediately. | nil |
|
213
280
|
|
281
|
+
For the full method signatures, please see [client.rb](lib/unleash/client.rb)
|
214
282
|
|
215
283
|
## Local test client
|
216
284
|
|
@@ -228,6 +296,7 @@ This client comes with the all the required strategies out of the box:
|
|
228
296
|
|
229
297
|
* ApplicationHostnameStrategy
|
230
298
|
* DefaultStrategy
|
299
|
+
* FlexibleRolloutStrategy
|
231
300
|
* GradualRolloutRandomStrategy
|
232
301
|
* GradualRolloutSessionIdStrategy
|
233
302
|
* GradualRolloutUserIdStrategy
|
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
|
|
data/examples/simple.rb
CHANGED
@@ -7,6 +7,7 @@ puts ">> START simple.rb"
|
|
7
7
|
|
8
8
|
# Unleash.configure do |config|
|
9
9
|
# config.url = 'http://unleash.herokuapp.com/api'
|
10
|
+
# config.custom_http_headers = { 'Authorization': '943ca9171e2c884c545c5d82417a655fb77cec970cc3b78a8ff87f4406b495d0' }
|
10
11
|
# config.app_name = 'simple-test'
|
11
12
|
# config.refresh_interval = 2
|
12
13
|
# config.metrics_interval = 2
|
@@ -17,13 +18,13 @@ puts ">> START simple.rb"
|
|
17
18
|
# or:
|
18
19
|
|
19
20
|
@unleash = Unleash::Client.new(
|
20
|
-
url: '
|
21
|
+
url: 'http://unleash.herokuapp.com/api',
|
22
|
+
custom_http_headers: { 'Authorization': '943ca9171e2c884c545c5d82417a655fb77cec970cc3b78a8ff87f4406b495d0' },
|
21
23
|
app_name: 'simple-test',
|
22
24
|
instance_id: 'local-test-cli',
|
23
25
|
refresh_interval: 2,
|
24
26
|
metrics_interval: 2,
|
25
|
-
retry_limit: 2
|
26
|
-
custom_http_headers: {'Authorization': '943ca9171e2c884c545c5d82417a655fb77cec970cc3b78a8ff87f4406b495d0'},
|
27
|
+
retry_limit: 2
|
27
28
|
)
|
28
29
|
|
29
30
|
# feature_name = "AwesomeFeature"
|
data/lib/unleash/client.rb
CHANGED
@@ -28,9 +28,15 @@ module Unleash
|
|
28
28
|
start_metrics unless Unleash.configuration.disable_metrics
|
29
29
|
end
|
30
30
|
|
31
|
-
def is_enabled?(feature, context = nil,
|
31
|
+
def is_enabled?(feature, context = nil, default_value_param = false, &fallback_blk)
|
32
32
|
Unleash.logger.debug "Unleash::Client.is_enabled? feature: #{feature} with context #{context}"
|
33
33
|
|
34
|
+
default_value = if block_given?
|
35
|
+
default_value_param || !!fallback_blk.call(feature, context)
|
36
|
+
else
|
37
|
+
default_value_param
|
38
|
+
end
|
39
|
+
|
34
40
|
if Unleash.configuration.disable_client
|
35
41
|
Unleash.logger.warn "unleash_client is disabled! Always returning #{default_value} for feature #{feature}!"
|
36
42
|
return default_value
|
@@ -56,19 +62,19 @@ module Unleash
|
|
56
62
|
yield(blk) if is_enabled?(feature, context, default_value)
|
57
63
|
end
|
58
64
|
|
59
|
-
def get_variant(feature, context =
|
65
|
+
def get_variant(feature, context = Unleash::Context.new, fallback_variant = disabled_variant)
|
60
66
|
Unleash.logger.debug "Unleash::Client.get_variant for feature: #{feature} with context #{context}"
|
61
67
|
|
62
68
|
if Unleash.configuration.disable_client
|
63
69
|
Unleash.logger.debug "unleash_client is disabled! Always returning #{fallback_variant} for feature #{feature}!"
|
64
|
-
return fallback_variant
|
70
|
+
return fallback_variant
|
65
71
|
end
|
66
72
|
|
67
73
|
toggle_as_hash = Unleash&.toggles&.select{ |toggle| toggle['name'] == feature }&.first
|
68
74
|
|
69
75
|
if toggle_as_hash.nil?
|
70
76
|
Unleash.logger.debug "Unleash::Client.get_variant feature: #{feature} not found"
|
71
|
-
return fallback_variant
|
77
|
+
return fallback_variant
|
72
78
|
end
|
73
79
|
|
74
80
|
toggle = Unleash::FeatureToggle.new(toggle_as_hash)
|
@@ -76,7 +82,7 @@ module Unleash
|
|
76
82
|
|
77
83
|
if variant.nil?
|
78
84
|
Unleash.logger.debug "Unleash::Client.get_variant variants for feature: #{feature} not found"
|
79
|
-
return fallback_variant
|
85
|
+
return fallback_variant
|
80
86
|
end
|
81
87
|
|
82
88
|
# TODO: Add to README: name, payload, enabled (bool)
|
@@ -88,7 +94,7 @@ module Unleash
|
|
88
94
|
def shutdown
|
89
95
|
unless Unleash.configuration.disable_client
|
90
96
|
Unleash.toggle_fetcher.save!
|
91
|
-
Unleash.reporter.
|
97
|
+
Unleash.reporter.post unless Unleash.configuration.disable_metrics
|
92
98
|
shutdown!
|
93
99
|
end
|
94
100
|
end
|
@@ -135,7 +141,7 @@ module Unleash
|
|
135
141
|
Unleash.configuration.retry_limit
|
136
142
|
)
|
137
143
|
self.metrics_scheduled_executor.run do
|
138
|
-
Unleash.reporter.
|
144
|
+
Unleash.reporter.post
|
139
145
|
end
|
140
146
|
end
|
141
147
|
|
@@ -144,12 +150,16 @@ module Unleash
|
|
144
150
|
|
145
151
|
# Send the request, if possible
|
146
152
|
begin
|
147
|
-
response = Unleash::Util::Http.post(Unleash.configuration.
|
153
|
+
response = Unleash::Util::Http.post(Unleash.configuration.client_register_uri, info.to_json)
|
148
154
|
rescue StandardError => e
|
149
155
|
Unleash.logger.error "unable to register client with unleash server due to exception #{e.class}:'#{e}'."
|
150
156
|
Unleash.logger.error "stacktrace: #{e.backtrace}"
|
151
157
|
end
|
152
158
|
Unleash.logger.debug "client registered: #{response}"
|
153
159
|
end
|
160
|
+
|
161
|
+
def disabled_variant
|
162
|
+
@disabled_variant ||= Unleash::FeatureToggle.disabled_variant
|
163
|
+
end
|
154
164
|
end
|
155
165
|
end
|
@@ -8,6 +8,7 @@ module Unleash
|
|
8
8
|
:app_name,
|
9
9
|
:environment,
|
10
10
|
:instance_id,
|
11
|
+
:project_name,
|
11
12
|
:custom_http_headers,
|
12
13
|
:disable_client,
|
13
14
|
:disable_metrics,
|
@@ -41,7 +42,7 @@ module Unleash
|
|
41
42
|
end
|
42
43
|
|
43
44
|
def refresh_backup_file!
|
44
|
-
self.backup_file = Dir.tmpdir
|
45
|
+
self.backup_file = File.join(Dir.tmpdir, "unleash-#{app_name}-repo.json")
|
45
46
|
end
|
46
47
|
|
47
48
|
def http_headers
|
@@ -51,16 +52,22 @@ module Unleash
|
|
51
52
|
}.merge(custom_http_headers.dup)
|
52
53
|
end
|
53
54
|
|
54
|
-
def
|
55
|
-
self.
|
55
|
+
def fetch_toggles_uri
|
56
|
+
uri = URI("#{self.url_stripped_of_slash}/client/features")
|
57
|
+
uri.query = "project=#{self.project_name}" unless self.project_name.nil?
|
58
|
+
uri
|
56
59
|
end
|
57
60
|
|
58
|
-
def
|
59
|
-
self.
|
61
|
+
def client_metrics_uri
|
62
|
+
URI("#{self.url_stripped_of_slash}/client/metrics")
|
60
63
|
end
|
61
64
|
|
62
|
-
def
|
63
|
-
self.
|
65
|
+
def client_register_uri
|
66
|
+
URI("#{self.url_stripped_of_slash}/client/register")
|
67
|
+
end
|
68
|
+
|
69
|
+
def url_stripped_of_slash
|
70
|
+
self.url.delete_suffix '/'
|
64
71
|
end
|
65
72
|
|
66
73
|
private
|
@@ -76,6 +83,7 @@ module Unleash
|
|
76
83
|
self.environment = 'default'
|
77
84
|
self.url = nil
|
78
85
|
self.instance_id = SecureRandom.uuid
|
86
|
+
self.project_name = nil
|
79
87
|
self.disable_client = false
|
80
88
|
self.disable_metrics = false
|
81
89
|
self.refresh_interval = 10
|
data/lib/unleash/context.rb
CHANGED
@@ -32,23 +32,30 @@ module Unleash
|
|
32
32
|
result
|
33
33
|
end
|
34
34
|
|
35
|
-
def get_variant(context, fallback_variant = disabled_variant)
|
35
|
+
def get_variant(context, fallback_variant = Unleash::FeatureToggle.disabled_variant)
|
36
36
|
raise ArgumentError, "Provided fallback_variant is not of type Unleash::Variant" if fallback_variant.class.name != 'Unleash::Variant'
|
37
37
|
|
38
38
|
context = ensure_valid_context(context)
|
39
39
|
|
40
|
-
return disabled_variant unless self.enabled && am_enabled?(context, true)
|
41
|
-
return disabled_variant if sum_variant_defs_weights <= 0
|
40
|
+
return Unleash::FeatureToggle.disabled_variant unless self.enabled && am_enabled?(context, true)
|
41
|
+
return Unleash::FeatureToggle.disabled_variant if sum_variant_defs_weights <= 0
|
42
42
|
|
43
|
-
variant = variant_from_override_match(context)
|
44
|
-
variant = variant_from_weights(context) if variant.nil?
|
43
|
+
variant = variant_from_override_match(context) || variant_from_weights(context, resolve_stickiness)
|
45
44
|
|
46
45
|
Unleash.toggle_metrics.increment_variant(self.name, variant.name) unless Unleash.configuration.disable_metrics
|
47
46
|
variant
|
48
47
|
end
|
49
48
|
|
49
|
+
def self.disabled_variant
|
50
|
+
Unleash::Variant.new(name: 'disabled', enabled: false)
|
51
|
+
end
|
52
|
+
|
50
53
|
private
|
51
54
|
|
55
|
+
def resolve_stickiness
|
56
|
+
self.variant_definitions&.map(&:stickiness)&.compact&.first || "default"
|
57
|
+
end
|
58
|
+
|
52
59
|
# only check if it is enabled, do not do metrics
|
53
60
|
def am_enabled?(context, default_result)
|
54
61
|
result =
|
@@ -77,15 +84,12 @@ module Unleash
|
|
77
84
|
strategy.constraints.empty? || strategy.constraints.all?{ |c| c.matches_context?(context) }
|
78
85
|
end
|
79
86
|
|
80
|
-
def disabled_variant
|
81
|
-
Unleash::Variant.new(name: 'disabled', enabled: false)
|
82
|
-
end
|
83
|
-
|
84
87
|
def sum_variant_defs_weights
|
85
88
|
self.variant_definitions.map(&:weight).reduce(0, :+)
|
86
89
|
end
|
87
90
|
|
88
|
-
def variant_salt(context)
|
91
|
+
def variant_salt(context, stickiness = "default")
|
92
|
+
return context.get_by_name(stickiness) unless stickiness == "default"
|
89
93
|
return context.user_id unless context.user_id.to_s.empty?
|
90
94
|
return context.session_id unless context.session_id.to_s.empty?
|
91
95
|
return context.remote_address unless context.remote_address.to_s.empty?
|
@@ -100,8 +104,8 @@ module Unleash
|
|
100
104
|
Unleash::Variant.new(name: variant.name, enabled: true, payload: variant.payload)
|
101
105
|
end
|
102
106
|
|
103
|
-
def variant_from_weights(context)
|
104
|
-
variant_weight = Unleash::Strategy::Util.get_normalized_number(variant_salt(context), self.name, sum_variant_defs_weights)
|
107
|
+
def variant_from_weights(context, stickiness)
|
108
|
+
variant_weight = Unleash::Strategy::Util.get_normalized_number(variant_salt(context, stickiness), self.name, sum_variant_defs_weights)
|
105
109
|
prev_weights = 0
|
106
110
|
|
107
111
|
variant_definition = self.variant_definitions
|
@@ -110,7 +114,7 @@ module Unleash
|
|
110
114
|
prev_weights += v.weight
|
111
115
|
res
|
112
116
|
end
|
113
|
-
return disabled_variant if variant_definition.nil?
|
117
|
+
return self.disabled_variant if variant_definition.nil?
|
114
118
|
|
115
119
|
Unleash::Variant.new(name: variant_definition.name, enabled: true, payload: variant_definition.payload)
|
116
120
|
end
|
@@ -150,6 +154,7 @@ module Unleash
|
|
150
154
|
v.fetch('name', ''),
|
151
155
|
v.fetch('weight', 0),
|
152
156
|
v.fetch('payload', nil),
|
157
|
+
v.fetch('stickiness', nil),
|
153
158
|
v.fetch('overrides', [])
|
154
159
|
)
|
155
160
|
end || []
|
@@ -6,6 +6,8 @@ require 'time'
|
|
6
6
|
|
7
7
|
module Unleash
|
8
8
|
class MetricsReporter
|
9
|
+
LONGEST_WITHOUT_A_REPORT = 600
|
10
|
+
|
9
11
|
attr_accessor :last_time
|
10
12
|
|
11
13
|
def initialize
|
@@ -33,16 +35,28 @@ module Unleash
|
|
33
35
|
report
|
34
36
|
end
|
35
37
|
|
36
|
-
def
|
37
|
-
Unleash.logger.debug "
|
38
|
+
def post
|
39
|
+
Unleash.logger.debug "post() Report"
|
40
|
+
|
41
|
+
if bucket_empty? && (Time.now - self.last_time < LONGEST_WITHOUT_A_REPORT) # and last time is less then 10 minutes...
|
42
|
+
Unleash.logger.debug "Report not posted to server, as it would have been empty. (and has been empty for up to 10 min)"
|
43
|
+
|
44
|
+
return
|
45
|
+
end
|
38
46
|
|
39
|
-
response = Unleash::Util::Http.post(Unleash.configuration.
|
47
|
+
response = Unleash::Util::Http.post(Unleash.configuration.client_metrics_uri, self.generate_report.to_json)
|
40
48
|
|
41
49
|
if ['200', '202'].include? response.code
|
42
|
-
Unleash.logger.debug "Report sent to unleash server
|
50
|
+
Unleash.logger.debug "Report sent to unleash server successfully. Server responded with http code #{response.code}"
|
43
51
|
else
|
44
52
|
Unleash.logger.error "Error when sending report to unleash server. Server responded with http code #{response.code}."
|
45
53
|
end
|
46
54
|
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def bucket_empty?
|
59
|
+
Unleash.toggle_metrics.features.empty?
|
60
|
+
end
|
47
61
|
end
|
48
62
|
end
|
@@ -38,16 +38,16 @@ module Unleash
|
|
38
38
|
|
39
39
|
def resolve_stickiness(stickiness, context)
|
40
40
|
case stickiness
|
41
|
-
when 'userId'
|
42
|
-
context.user_id
|
43
|
-
when 'sessionId'
|
44
|
-
context.session_id
|
45
41
|
when 'random'
|
46
42
|
random
|
47
43
|
when 'default'
|
48
44
|
context.user_id || context.session_id || random
|
49
45
|
else
|
50
|
-
|
46
|
+
begin
|
47
|
+
context.get_by_name(stickiness)
|
48
|
+
rescue KeyError
|
49
|
+
nil
|
50
|
+
end
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
@@ -36,7 +36,7 @@ module Unleash
|
|
36
36
|
# rename to refresh_from_server! ??
|
37
37
|
def fetch
|
38
38
|
Unleash.logger.debug "fetch()"
|
39
|
-
response = Unleash::Util::Http.get(Unleash.configuration.
|
39
|
+
response = Unleash::Util::Http.get(Unleash.configuration.fetch_toggles_uri, etag)
|
40
40
|
|
41
41
|
if response.code == '304'
|
42
42
|
Unleash.logger.debug "No changes according to the unleash server, nothing to do."
|
@@ -106,10 +106,11 @@ module Unleash
|
|
106
106
|
|
107
107
|
def read!
|
108
108
|
Unleash.logger.debug "read!()"
|
109
|
-
|
109
|
+
backup_file = Unleash.configuration.backup_file
|
110
|
+
return nil unless File.exist?(backup_file)
|
110
111
|
|
111
112
|
begin
|
112
|
-
file = File.new(
|
113
|
+
file = File.new(backup_file, "r")
|
113
114
|
file_content = file.read
|
114
115
|
|
115
116
|
backup_as_hash = JSON.parse(file_content)
|
data/lib/unleash/util/http.rb
CHANGED
@@ -4,8 +4,7 @@ require 'uri'
|
|
4
4
|
module Unleash
|
5
5
|
module Util
|
6
6
|
module Http
|
7
|
-
def self.get(
|
8
|
-
uri = URI(url)
|
7
|
+
def self.get(uri, etag = nil)
|
9
8
|
http = http_connection(uri)
|
10
9
|
|
11
10
|
request = Net::HTTP::Get.new(uri.request_uri, http_headers(etag))
|
@@ -13,8 +12,7 @@ module Unleash
|
|
13
12
|
http.request(request)
|
14
13
|
end
|
15
14
|
|
16
|
-
def self.post(
|
17
|
-
uri = URI(url)
|
15
|
+
def self.post(uri, body)
|
18
16
|
http = http_connection(uri)
|
19
17
|
|
20
18
|
request = Net::HTTP::Post.new(uri.request_uri, http_headers)
|
@@ -2,13 +2,13 @@ require 'unleash/variant_override'
|
|
2
2
|
|
3
3
|
module Unleash
|
4
4
|
class VariantDefinition
|
5
|
-
attr_accessor :name, :weight, :payload, :overrides
|
5
|
+
attr_accessor :name, :weight, :payload, :overrides, :stickiness
|
6
6
|
|
7
|
-
def initialize(name, weight = 0, payload = nil, overrides = [])
|
7
|
+
def initialize(name, weight = 0, payload = nil, stickiness = nil, overrides = [])
|
8
8
|
self.name = name
|
9
9
|
self.weight = weight
|
10
10
|
self.payload = payload
|
11
|
-
|
11
|
+
self.stickiness = stickiness
|
12
12
|
self.overrides = (overrides || [])
|
13
13
|
.select{ |v| v.is_a?(Hash) && v.has_key?('contextName') }
|
14
14
|
.map{ |v| VariantOverride.new(v.fetch('contextName', ''), v.fetch('values', [])) } || []
|
@@ -19,7 +19,8 @@ module Unleash
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def to_s
|
22
|
-
"<VariantDefinition: name=#{self.name},weight=#{self.weight},payload=#{self.payload},
|
22
|
+
"<VariantDefinition: name=#{self.name},weight=#{self.weight},payload=#{self.payload},stickiness=#{self.stickiness}" \
|
23
|
+
",overrides=#{self.overrides}>"
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
data/lib/unleash/version.rb
CHANGED
data/lib/unleash.rb
CHANGED
@@ -24,11 +24,6 @@ module Unleash
|
|
24
24
|
attr_accessor :configuration, :toggle_fetcher, :toggles, :toggle_metrics, :reporter, :logger
|
25
25
|
end
|
26
26
|
|
27
|
-
def self.initialize
|
28
|
-
self.toggles = []
|
29
|
-
self.toggle_metrics = {}
|
30
|
-
end
|
31
|
-
|
32
27
|
# Support for configuration via yield:
|
33
28
|
def self.configure
|
34
29
|
self.configuration ||= Unleash::Configuration.new
|
data/unleash-client.gemspec
CHANGED
@@ -31,6 +31,7 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.add_development_dependency "rspec-json_expectations", "~> 2.2"
|
32
32
|
spec.add_development_dependency "webmock", "~> 3.8"
|
33
33
|
|
34
|
-
spec.add_development_dependency "coveralls", "~> 0.8"
|
35
34
|
spec.add_development_dependency "rubocop", "~> 0.80"
|
35
|
+
spec.add_development_dependency "simplecov", "~> 0.21.2"
|
36
|
+
spec.add_development_dependency "simplecov-lcov", "~> 0.8.0"
|
36
37
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unleash
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Renato Arruda
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-12-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: murmurhash3
|
@@ -95,33 +95,47 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '3.8'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: rubocop
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0.
|
103
|
+
version: '0.80'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0.
|
110
|
+
version: '0.80'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: simplecov
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version:
|
117
|
+
version: 0.21.2
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version:
|
124
|
+
version: 0.21.2
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: simplecov-lcov
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 0.8.0
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 0.8.0
|
125
139
|
description: |-
|
126
140
|
This is the ruby client for Unleash, a powerful feature toggle system
|
127
141
|
that gives you a great overview over all feature toggles across all your applications and services.
|
@@ -132,10 +146,10 @@ executables:
|
|
132
146
|
extensions: []
|
133
147
|
extra_rdoc_files: []
|
134
148
|
files:
|
149
|
+
- ".github/workflows/pull_request.yml"
|
135
150
|
- ".gitignore"
|
136
151
|
- ".rspec"
|
137
152
|
- ".rubocop.yml"
|
138
|
-
- ".travis.yml"
|
139
153
|
- Gemfile
|
140
154
|
- LICENSE
|
141
155
|
- README.md
|
data/.travis.yml
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
sudo: false
|
2
|
-
language: ruby
|
3
|
-
rvm:
|
4
|
-
- jruby
|
5
|
-
- 3.0
|
6
|
-
- 2.7
|
7
|
-
- 2.6
|
8
|
-
- 2.5
|
9
|
-
before_install:
|
10
|
-
- gem install bundler -v 2.1.4
|
11
|
-
- git clone --depth 5 --branch v3.3.0 https://github.com/Unleash/client-specification.git client-specification
|
12
|
-
|
13
|
-
notifications:
|
14
|
-
slack:
|
15
|
-
secure: x593zOjdl2yVB8uP54v8CmuCOat8GFHnK99NPvPHKvif5U7PGe0YOgYh4DC1+Jc9vfjn1ke+0++m+Gif4quowpeOaA/t45xpB494lyziXsBulYml245jRp9yzoUmIIt7KxHhv4rlo3Q1ztMJgh6a5yDCornKHW2bKTkLsvqVTwxBRatLOrt6K9O8FivO/NaqgcoXl7Rw0fOx/bsZtx2IAFueTCH19NoqW1mk9KFEZ96YqJSvuqmfDC0AO7siq03WKlB++nPlKe1QcrlPalCrcsSzrYNhYJ3akBTt/ZbE1v6YJv2L+zUqRnAPTY2H+qp8WejFQtdhIjfeJ/SWox0iWv/Wy/mTFfj+EhFO9Aq+xhMjJ1OOLtNAPoYJyatEVgJkILb6M26igTFcuI60xBbGNmh5ZYeyRdn5/xFb7G2zyJ2Swc3PvN1uLzMHfTF0R7WzGq4CRNGIOjrHTGncyB3IGAONOdJdM3iT9XKY6cdlRK0VkQjEsEMe0eNv2fxxLVSGna4sdJoTND6LhJ6qCfuS9DEDXwoRdLxAXxefycCh9VNp7gloMJx8IbHYxOW0BFZqc3hxNU9X2SwOj6j72DZMrdYDg2aPAW69HG0iMontQ37Di87JEW2F2Cpgb49+4twByrQNIx+st+DGNce1vpc0DN+KuJVdIcmha654lT7Ffe8=
|