unleash 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ce69a2f4588a0d5e459e76aa8d78894497736f484ae20f5d987d50b34aced50
4
- data.tar.gz: 24222c2c023197dc4f283ee4e09316f233245b2e82ca4c9ad934fe5519de75d5
3
+ metadata.gz: 14f988b84b07e45401ad61a5814c1ca44185fc9358cef33f050b1d3bae45dd4e
4
+ data.tar.gz: afda9cf356088ef976047fe58d38d3b457fb1d8797cb7f519de6bdb953f43267
5
5
  SHA512:
6
- metadata.gz: 6e056f24c5f840b211f04bc3c605ccf0846d1ff8717cc824d1e50307e636f28c50f91a8fe85f648de9f16377d5441d5feea459a15ad1eae00c35a6238ebb0355
7
- data.tar.gz: 51b49947d4aea7d1901bf0083ef366604b5d1cd7859cab1cfaceacc420fb056c4188dd4512c1a7feddc5f9711e0c5ad2660e91c1e681cd57e47307f29948790c
6
+ metadata.gz: c1ac85ea8b774a230212583b33b9fdab3110fadab2fa1652810ea0913312c1fd20abe4471761b5ccd399d66a52995c0ddb6b803ee42aa4c4432add8a5524e51d
7
+ data.tar.gz: b03da5caf2bf25683b2d61599a37f15582d26664553ef72096dfd3073aceb96224eaa46fd19c948f9f4fc143e3226ee176a4e66d762016aedf736fb903edae85
@@ -15,7 +15,9 @@ jobs:
15
15
  - ubuntu
16
16
  - macos
17
17
  ruby-version:
18
- - jruby
18
+ - jruby-9.2
19
+ - jruby-9.3
20
+ - 3.1
19
21
  - 3.0
20
22
  - 2.7
21
23
  - 2.6
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Unleash::Client
2
2
 
3
- [![Build Status](https://travis-ci.org/Unleash/unleash-client-ruby.svg?branch=master)](https://travis-ci.org/Unleash/unleash-client-ruby)
4
- [![Coverage Status](https://coveralls.io/repos/github/Unleash/unleash-client-ruby/badge.svg?branch=master)](https://coveralls.io/github/Unleash/unleash-client-ruby?branch=master)
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,11 +10,13 @@ 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
 
@@ -69,7 +71,7 @@ Argument | Description | Required? | Type | Default Value|
69
71
  `environment` | Environment the program is running on. Could be for example `prod` or `dev`. Not yet in use. | N | String | `default` |
70
72
  `project_name` | Name of the project to retrieve features from. If not set, all feature flags will be retrieved. | N | String | nil |
71
73
  `refresh_interval` | How often the unleash client should check with the server for configuration changes. | N | Integer | 15 |
72
- `metrics_interval` | How often the unleash client should send metrics to server. | N | Integer | 30 |
74
+ `metrics_interval` | How often the unleash client should send metrics to server. | N | Integer | 60 |
73
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` |
74
76
  `disable_metrics` | Disables sending metrics to Unleash server. | N | Boolean | `false` |
75
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 | {} |
@@ -78,9 +80,14 @@ Argument | Description | Required? | Type | Default Value|
78
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` |
79
81
  `logger` | Specify a custom `Logger` class to handle logs for the Unleash client. | N | Class | `Logger.new(STDOUT)` |
80
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` |
81
84
 
82
- For in a more in depth look, please see `lib/unleash/configuration.rb`.
85
+ For a more in-depth look, please see `lib/unleash/configuration.rb`.
83
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
84
91
 
85
92
  ## Usage in a plain Ruby Application
86
93
 
@@ -88,7 +95,7 @@ For in a more in depth look, please see `lib/unleash/configuration.rb`.
88
95
  require 'unleash'
89
96
  require 'unleash/context'
90
97
 
91
- @unleash = Unleash::Client.new(app_name: 'my_ruby_app', url: 'http://unleash.herokuapp.com/api', custom_http_headers: {'Authorization': '<API token>'})
98
+ @unleash = Unleash::Client.new(app_name: 'my_ruby_app', url: 'http://unleash.herokuapp.com/api', custom_http_headers: { 'Authorization': '<API token>' })
92
99
 
93
100
  feature_name = "AwesomeFeature"
94
101
  unleash_context = Unleash::Context.new
@@ -266,6 +273,62 @@ variant = UNLEASH.get_variant "ColorVariants", @unleash_context, fallback_varian
266
273
  puts "variant color is: #{variant.payload.fetch('color')}"
267
274
  ```
268
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.
269
332
 
270
333
  #### Client methods
271
334
 
@@ -304,7 +367,6 @@ This client comes with the all the required strategies out of the box:
304
367
  * UnknownStrategy
305
368
  * UserWithIdStrategy
306
369
 
307
-
308
370
  ## Development
309
371
 
310
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.
@@ -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
+
@@ -0,0 +1,25 @@
1
+ module Unleash
2
+ module Bootstrap
3
+ class Configuration
4
+ attr_accessor :data, :file_path, :url, :url_headers, :block
5
+
6
+ def initialize(opts = {})
7
+ self.file_path = resolve_value_indifferently(opts, 'file_path') || ENV['UNLEASH_BOOTSTRAP_FILE'] || nil
8
+ self.url = resolve_value_indifferently(opts, 'url') || ENV['UNLEASH_BOOTSTRAP_URL'] || nil
9
+ self.url_headers = resolve_value_indifferently(opts, 'url_headers')
10
+ self.data = resolve_value_indifferently(opts, 'data')
11
+ self.block = resolve_value_indifferently(opts, 'block')
12
+ end
13
+
14
+ def valid?
15
+ ![self.data, self.file_path, self.url, self.block].all?(&:nil?)
16
+ end
17
+
18
+ private
19
+
20
+ def resolve_value_indifferently(opts, key)
21
+ opts[key] || opts[key.to_sym]
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ require 'unleash/bootstrap/provider/from_url'
2
+ require 'unleash/bootstrap/provider/from_file'
3
+
4
+ module Unleash
5
+ module Bootstrap
6
+ class Handler
7
+ attr_accessor :configuration
8
+
9
+ def initialize(configuration)
10
+ self.configuration = configuration
11
+ end
12
+
13
+ # @return [String] JSON string representing data returned from an Unleash server
14
+ def retrieve_toggles
15
+ return configuration.data unless self.configuration.data.nil?
16
+ return configuration.block.call if self.configuration.block.is_a?(Proc)
17
+ return Provider::FromFile.read(configuration.file_path) unless self.configuration.file_path.nil?
18
+ return Provider::FromUrl.read(configuration.url, configuration.url_headers) unless self.configuration.url.nil?
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ module Unleash
2
+ module Bootstrap
3
+ module Provider
4
+ class NotImplemented < RuntimeError
5
+ end
6
+
7
+ class Base
8
+ def read
9
+ raise NotImplemented, "Bootstrap is not implemented"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require 'unleash/bootstrap/provider/base'
2
+
3
+ module Unleash
4
+ module Bootstrap
5
+ module Provider
6
+ class FromFile < Base
7
+ # @param file_path [String]
8
+ def self.read(file_path)
9
+ File.read(file_path)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ require 'unleash/bootstrap/provider/base'
2
+
3
+ module Unleash
4
+ module Bootstrap
5
+ module Provider
6
+ class FromUrl < Base
7
+ # @param url [String]
8
+ # @param headers [Hash, nil] HTTP headers to use. If not set, the unleash client SDK ones will be used.
9
+ def self.read(url, headers = nil)
10
+ response = Unleash::Util::Http.get(URI.parse(url), nil, headers)
11
+
12
+ return nil if response.code != '200'
13
+
14
+ response.body
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -18,8 +18,9 @@ module Unleash
18
18
  Unleash.logger = Unleash.configuration.logger.clone
19
19
  Unleash.logger.level = Unleash.configuration.log_level
20
20
 
21
+ Unleash.toggle_fetcher = Unleash::ToggleFetcher.new
21
22
  if Unleash.configuration.disable_client
22
- Unleash.logger.warn "Unleash::Client is disabled! Will only return default results!"
23
+ Unleash.logger.warn "Unleash::Client is disabled! Will only return default (or bootstrapped if available) results!"
23
24
  return
24
25
  end
25
26
 
@@ -37,11 +38,6 @@ module Unleash
37
38
  default_value_param
38
39
  end
39
40
 
40
- if Unleash.configuration.disable_client
41
- Unleash.logger.warn "unleash_client is disabled! Always returning #{default_value} for feature #{feature}!"
42
- return default_value
43
- end
44
-
45
41
  toggle_as_hash = Unleash&.toggles&.select{ |toggle| toggle['name'] == feature }&.first
46
42
 
47
43
  if toggle_as_hash.nil?
@@ -121,11 +117,11 @@ module Unleash
121
117
  end
122
118
 
123
119
  def start_toggle_fetcher
124
- Unleash.toggle_fetcher = Unleash::ToggleFetcher.new
125
120
  self.fetcher_scheduled_executor = Unleash::ScheduledExecutor.new(
126
121
  'ToggleFetcher',
127
122
  Unleash.configuration.refresh_interval,
128
- Unleash.configuration.retry_limit
123
+ Unleash.configuration.retry_limit,
124
+ first_fetch_is_eager
129
125
  )
130
126
  self.fetcher_scheduled_executor.run do
131
127
  Unleash.toggle_fetcher.fetch
@@ -161,5 +157,9 @@ module Unleash
161
157
  def disabled_variant
162
158
  @disabled_variant ||= Unleash::FeatureToggle.disabled_variant
163
159
  end
160
+
161
+ def first_fetch_is_eager
162
+ Unleash.configuration.use_bootstrap?
163
+ end
164
164
  end
165
165
  end
@@ -1,5 +1,6 @@
1
1
  require 'securerandom'
2
2
  require 'tmpdir'
3
+ require 'unleash/bootstrap/configuration'
3
4
 
4
5
  module Unleash
5
6
  class Configuration
@@ -18,7 +19,8 @@ module Unleash
18
19
  :metrics_interval,
19
20
  :backup_file,
20
21
  :logger,
21
- :log_level
22
+ :log_level,
23
+ :bootstrap_config
22
24
 
23
25
  def initialize(opts = {})
24
26
  ensure_valid_opts(opts)
@@ -70,6 +72,10 @@ module Unleash
70
72
  self.url.delete_suffix '/'
71
73
  end
72
74
 
75
+ def use_bootstrap?
76
+ self.bootstrap_config&.valid?
77
+ end
78
+
73
79
  private
74
80
 
75
81
  def ensure_valid_opts(opts)
@@ -87,11 +93,12 @@ module Unleash
87
93
  self.disable_client = false
88
94
  self.disable_metrics = false
89
95
  self.refresh_interval = 10
90
- self.metrics_interval = 30
96
+ self.metrics_interval = 60
91
97
  self.timeout = 30
92
- self.retry_limit = 1
98
+ self.retry_limit = 5
93
99
  self.backup_file = nil
94
100
  self.log_level = Logger::WARN
101
+ self.bootstrap_config = nil
95
102
 
96
103
  self.custom_http_headers = {}
97
104
  end
@@ -1,19 +1,22 @@
1
1
  module Unleash
2
2
  class ScheduledExecutor
3
- attr_accessor :name, :interval, :max_exceptions, :retry_count, :thread
3
+ attr_accessor :name, :interval, :max_exceptions, :retry_count, :thread, :immediate_execution
4
4
 
5
- def initialize(name, interval, max_exceptions = 5)
5
+ def initialize(name, interval, max_exceptions = 5, immediate_execution = false)
6
6
  self.name = name || ''
7
7
  self.interval = interval
8
8
  self.max_exceptions = max_exceptions
9
9
  self.retry_count = 0
10
10
  self.thread = nil
11
+ self.immediate_execution = immediate_execution
11
12
  end
12
13
 
13
14
  def run(&blk)
14
15
  self.thread = Thread.new do
15
16
  Thread.current[:name] = self.name
16
17
 
18
+ run_blk{ blk.call } if self.immediate_execution
19
+
17
20
  Unleash.logger.debug "thread #{name} loop starting"
18
21
  loop do
19
22
  Unleash.logger.debug "thread #{name} sleeping for #{interval} seconds"
@@ -13,7 +13,23 @@ module Unleash
13
13
  return false unless params.fetch(PARAM, nil).is_a? String
14
14
  return false unless context.class.name == 'Unleash::Context'
15
15
 
16
- params[PARAM].split(',').map(&:strip).include?(context.remote_address)
16
+ remote_address = ipaddr_or_nil_from_str(context.remote_address)
17
+
18
+ params[PARAM]
19
+ .split(',')
20
+ .map(&:strip)
21
+ .map{ |ipblock| ipaddr_or_nil_from_str(ipblock) }
22
+ .compact
23
+ .map{ |ipb| ipb.include? remote_address }
24
+ .any?
25
+ end
26
+
27
+ private
28
+
29
+ def ipaddr_or_nil_from_str(ip)
30
+ IPAddr.new(ip)
31
+ rescue StandardError
32
+ nil
17
33
  end
18
34
  end
19
35
  end
@@ -1,4 +1,5 @@
1
1
  require 'unleash/configuration'
2
+ require 'unleash/bootstrap/handler'
2
3
  require 'net/http'
3
4
  require 'json'
4
5
 
@@ -13,10 +14,15 @@ module Unleash
13
14
  self.toggle_resource = ConditionVariable.new
14
15
  self.retry_count = 0
15
16
 
16
- # start by fetching synchronously, and failing back to reading the backup file.
17
17
  begin
18
- fetch
18
+ # if bootstrap configuration is available, initialize. An immediate API read is also triggered
19
+ if Unleash.configuration.use_bootstrap?
20
+ bootstrap
21
+ else
22
+ fetch
23
+ end
19
24
  rescue StandardError => e
25
+ # fail back to reading the backup file
20
26
  Unleash.logger.warn "ToggleFetcher was unable to fetch from the network, attempting to read from backup file."
21
27
  Unleash.logger.debug "Exception Caught: #{e}"
22
28
  read!
@@ -36,6 +42,8 @@ module Unleash
36
42
  # rename to refresh_from_server! ??
37
43
  def fetch
38
44
  Unleash.logger.debug "fetch()"
45
+ return if Unleash.configuration.disable_client
46
+
39
47
  response = Unleash::Util::Http.get(Unleash.configuration.fetch_toggles_uri, etag)
40
48
 
41
49
  if response.code == '304'
@@ -46,14 +54,7 @@ module Unleash
46
54
  end
47
55
 
48
56
  self.etag = response['ETag']
49
- response_hash = JSON.parse(response.body)
50
-
51
- if response_hash['version'] >= 1
52
- features = response_hash['features']
53
- else
54
- raise NotImplemented, "Version of features provided by unleash server" \
55
- " is unsupported by this client."
56
- end
57
+ features = get_features(response.body)
57
58
 
58
59
  # always synchronize with the local cache when fetching:
59
60
  synchronize_with_local_cache!(features)
@@ -126,5 +127,23 @@ module Unleash
126
127
  file&.close
127
128
  end
128
129
  end
130
+
131
+ def bootstrap
132
+ bootstrap_payload = Unleash::Bootstrap::Handler.new(Unleash.configuration.bootstrap_config).retrieve_toggles
133
+ synchronize_with_local_cache! get_features bootstrap_payload
134
+ update_running_client!
135
+
136
+ # reset Unleash.configuration.bootstrap_data to free up memory, as we will never use it again
137
+ Unleash.configuration.bootstrap_config = nil
138
+ end
139
+
140
+ # @param response_body [String]
141
+ def get_features(response_body)
142
+ response_hash = JSON.parse(response_body)
143
+ return response_hash['features'] if response_hash['version'] >= 1
144
+
145
+ raise NotImplemented, "Version of features provided by unleash server" \
146
+ " is unsupported by this client."
147
+ end
129
148
  end
130
149
  end
@@ -4,10 +4,10 @@ require 'uri'
4
4
  module Unleash
5
5
  module Util
6
6
  module Http
7
- def self.get(uri, etag = nil)
7
+ def self.get(uri, etag = nil, headers_override = nil)
8
8
  http = http_connection(uri)
9
9
 
10
- request = Net::HTTP::Get.new(uri.request_uri, http_headers(etag))
10
+ request = Net::HTTP::Get.new(uri.request_uri, http_headers(etag, headers_override))
11
11
 
12
12
  http.request(request)
13
13
  end
@@ -30,10 +30,13 @@ module Unleash
30
30
  http
31
31
  end
32
32
 
33
- def self.http_headers(etag = nil)
33
+ # @param etag [String, nil]
34
+ # @param headers_override [Hash, nil]
35
+ def self.http_headers(etag = nil, headers_override = nil)
34
36
  Unleash.logger.debug "ETag: #{etag}" unless etag.nil?
35
37
 
36
38
  headers = (Unleash.configuration.http_headers || {}).dup
39
+ headers = headers_override if headers_override.is_a?(Hash)
37
40
  headers['Content-Type'] = 'application/json'
38
41
  headers['If-None-Match'] = etag unless etag.nil?
39
42
 
@@ -1,3 +1,3 @@
1
1
  module Unleash
2
- VERSION = "4.0.0".freeze
2
+ VERSION = "4.1.0".freeze
3
3
  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.0.0
4
+ version: 4.1.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-12-16 00:00:00.000000000 Z
11
+ date: 2022-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: murmurhash3
@@ -157,9 +157,16 @@ files:
157
157
  - bin/console
158
158
  - bin/setup
159
159
  - bin/unleash-client
160
+ - examples/bootstrap.rb
161
+ - examples/default-toggles.json
160
162
  - examples/simple.rb
161
163
  - lib/unleash.rb
162
164
  - lib/unleash/activation_strategy.rb
165
+ - lib/unleash/bootstrap/configuration.rb
166
+ - lib/unleash/bootstrap/handler.rb
167
+ - lib/unleash/bootstrap/provider/base.rb
168
+ - lib/unleash/bootstrap/provider/from_file.rb
169
+ - lib/unleash/bootstrap/provider/from_url.rb
163
170
  - lib/unleash/client.rb
164
171
  - lib/unleash/configuration.rb
165
172
  - lib/unleash/constraint.rb
@@ -204,7 +211,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
204
211
  - !ruby/object:Gem::Version
205
212
  version: '0'
206
213
  requirements: []
207
- rubygems_version: 3.2.3
214
+ rubygems_version: 3.3.6
208
215
  signing_key:
209
216
  specification_version: 4
210
217
  summary: Unleash feature toggle client.