unleash 3.2.5 → 4.0.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 +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=
|