unleash 3.2.5 → 4.2.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 +73 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +114 -6
- data/README.md +247 -27
- data/bin/unleash-client +15 -5
- data/examples/bootstrap.rb +51 -0
- data/examples/default-toggles.json +42 -0
- data/examples/simple.rb +4 -3
- data/lib/unleash/bootstrap/configuration.rb +25 -0
- data/lib/unleash/bootstrap/handler.rb +22 -0
- data/lib/unleash/bootstrap/provider/base.rb +14 -0
- data/lib/unleash/bootstrap/provider/from_file.rb +14 -0
- data/lib/unleash/bootstrap/provider/from_url.rb +19 -0
- data/lib/unleash/client.rb +25 -15
- data/lib/unleash/configuration.rb +25 -10
- data/lib/unleash/constraint.rb +88 -10
- data/lib/unleash/context.rb +3 -2
- data/lib/unleash/feature_toggle.rb +21 -14
- data/lib/unleash/metrics_reporter.rb +18 -4
- data/lib/unleash/scheduled_executor.rb +5 -2
- data/lib/unleash/strategy/application_hostname.rb +1 -0
- data/lib/unleash/strategy/flexible_rollout.rb +5 -5
- data/lib/unleash/strategy/remote_address.rb +17 -1
- data/lib/unleash/toggle_fetcher.rb +33 -13
- data/lib/unleash/util/http.rb +7 -6
- data/lib/unleash/variant_definition.rb +5 -4
- data/lib/unleash/version.rb +1 -1
- data/lib/unleash.rb +0 -5
- data/unleash-client.gemspec +2 -1
- metadata +31 -10
- data/.travis.yml +0 -15
@@ -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
|
@@ -135,7 +139,9 @@ module Unleash
|
|
135
139
|
Constraint.new(
|
136
140
|
c.fetch('contextName'),
|
137
141
|
c.fetch('operator'),
|
138
|
-
c.fetch('values')
|
142
|
+
c.fetch('values', nil) || c.fetch('value', nil),
|
143
|
+
inverted: c.fetch('inverted', false),
|
144
|
+
case_insensitive: c.fetch('caseInsensitive', false)
|
139
145
|
)
|
140
146
|
end
|
141
147
|
)
|
@@ -150,6 +156,7 @@ module Unleash
|
|
150
156
|
v.fetch('name', ''),
|
151
157
|
v.fetch('weight', 0),
|
152
158
|
v.fetch('payload', nil),
|
159
|
+
v.fetch('stickiness', nil),
|
153
160
|
v.fetch('overrides', [])
|
154
161
|
)
|
155
162
|
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
|
@@ -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"
|
@@ -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
|
@@ -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
|
-
|
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
|
-
|
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,7 +42,9 @@ module Unleash
|
|
36
42
|
# rename to refresh_from_server! ??
|
37
43
|
def fetch
|
38
44
|
Unleash.logger.debug "fetch()"
|
39
|
-
|
45
|
+
return if Unleash.configuration.disable_client
|
46
|
+
|
47
|
+
response = Unleash::Util::Http.get(Unleash.configuration.fetch_toggles_uri, etag)
|
40
48
|
|
41
49
|
if response.code == '304'
|
42
50
|
Unleash.logger.debug "No changes according to the unleash server, nothing to do."
|
@@ -46,14 +54,7 @@ module Unleash
|
|
46
54
|
end
|
47
55
|
|
48
56
|
self.etag = response['ETag']
|
49
|
-
|
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)
|
@@ -106,10 +107,11 @@ module Unleash
|
|
106
107
|
|
107
108
|
def read!
|
108
109
|
Unleash.logger.debug "read!()"
|
109
|
-
|
110
|
+
backup_file = Unleash.configuration.backup_file
|
111
|
+
return nil unless File.exist?(backup_file)
|
110
112
|
|
111
113
|
begin
|
112
|
-
file = File.new(
|
114
|
+
file = File.new(backup_file, "r")
|
113
115
|
file_content = file.read
|
114
116
|
|
115
117
|
backup_as_hash = JSON.parse(file_content)
|
@@ -125,5 +127,23 @@ module Unleash
|
|
125
127
|
file&.close
|
126
128
|
end
|
127
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
|
128
148
|
end
|
129
149
|
end
|
data/lib/unleash/util/http.rb
CHANGED
@@ -4,17 +4,15 @@ 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, headers_override = nil)
|
9
8
|
http = http_connection(uri)
|
10
9
|
|
11
|
-
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))
|
12
11
|
|
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)
|
@@ -32,10 +30,13 @@ module Unleash
|
|
32
30
|
http
|
33
31
|
end
|
34
32
|
|
35
|
-
|
33
|
+
# @param etag [String, nil]
|
34
|
+
# @param headers_override [Hash, nil]
|
35
|
+
def self.http_headers(etag = nil, headers_override = nil)
|
36
36
|
Unleash.logger.debug "ETag: #{etag}" unless etag.nil?
|
37
37
|
|
38
38
|
headers = (Unleash.configuration.http_headers || {}).dup
|
39
|
+
headers = headers_override if headers_override.is_a?(Hash)
|
39
40
|
headers['Content-Type'] = 'application/json'
|
40
41
|
headers['If-None-Match'] = etag unless etag.nil?
|
41
42
|
|
@@ -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.2.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:
|
11
|
+
date: 2022-03-18 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
|
@@ -143,9 +157,16 @@ files:
|
|
143
157
|
- bin/console
|
144
158
|
- bin/setup
|
145
159
|
- bin/unleash-client
|
160
|
+
- examples/bootstrap.rb
|
161
|
+
- examples/default-toggles.json
|
146
162
|
- examples/simple.rb
|
147
163
|
- lib/unleash.rb
|
148
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
|
149
170
|
- lib/unleash/client.rb
|
150
171
|
- lib/unleash/configuration.rb
|
151
172
|
- lib/unleash/constraint.rb
|
@@ -190,7 +211,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
190
211
|
- !ruby/object:Gem::Version
|
191
212
|
version: '0'
|
192
213
|
requirements: []
|
193
|
-
rubygems_version: 3.
|
214
|
+
rubygems_version: 3.3.6
|
194
215
|
signing_key:
|
195
216
|
specification_version: 4
|
196
217
|
summary: Unleash feature toggle client.
|
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=
|