unleash 5.1.1 → 6.0.0.pre
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/CHANGELOG.md +6 -0
- data/lib/unleash/client.rb +21 -22
- data/lib/unleash/configuration.rb +2 -2
- data/lib/unleash/context.rb +35 -9
- data/lib/unleash/metrics_reporter.rb +12 -26
- data/lib/unleash/strategies.rb +14 -73
- data/lib/unleash/toggle_fetcher.rb +22 -56
- data/lib/unleash/variant.rb +6 -0
- data/lib/unleash/version.rb +1 -1
- data/lib/unleash.rb +1 -1
- data/unleash-client.gemspec +1 -0
- metadata +20 -22
- data/lib/unleash/activation_strategy.rb +0 -44
- data/lib/unleash/constraint.rb +0 -117
- data/lib/unleash/feature_toggle.rb +0 -253
- data/lib/unleash/metrics.rb +0 -41
- data/lib/unleash/strategy/application_hostname.rb +0 -26
- data/lib/unleash/strategy/base.rb +0 -16
- data/lib/unleash/strategy/default.rb +0 -13
- data/lib/unleash/strategy/flexible_rollout.rb +0 -64
- data/lib/unleash/strategy/gradual_rollout_random.rb +0 -24
- data/lib/unleash/strategy/gradual_rollout_sessionid.rb +0 -21
- data/lib/unleash/strategy/gradual_rollout_userid.rb +0 -21
- data/lib/unleash/strategy/remote_address.rb +0 -36
- data/lib/unleash/strategy/user_with_id.rb +0 -20
- data/lib/unleash/strategy/util.rb +0 -17
- data/lib/unleash/variant_definition.rb +0 -26
- data/lib/unleash/variant_override.rb +0 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 518b79d43627fa6a3de4b59eb7e6f445ebb09a7f8e96f963765ada55c827ac5b
|
4
|
+
data.tar.gz: c1c284bee319185697a6eb16d0b10d62c141372b708baa34ad4de682795db1cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c17939c596ca7523d6d6abec6f36041555fcfbed36bfadcb105a7d6704822a85c3e673f53db23cedcdf560181591b69ec8b8f4bc009a29e6f4cbf39a943b0dda
|
7
|
+
data.tar.gz: d61f27c3cf5e726bbcc85401512a3009b9ccfbaa289b12ebf930af30fe6606c340eed26b57aba84425cafcc792120bcf003e36512d56168ca7d2cac75eff9d90
|
data/CHANGELOG.md
CHANGED
@@ -13,6 +13,12 @@ Note: These changes are not considered notable:
|
|
13
13
|
|
14
14
|
## [Unreleased]
|
15
15
|
|
16
|
+
## [6.0.0.pre] - 2024-09-25
|
17
|
+
#### Changed
|
18
|
+
- No longer possible to override built in strategies with custom strategies (#152)
|
19
|
+
- No longer possible to access built in strategy's objects, these are now native code (#152)
|
20
|
+
- Core of the SDK swapped for Yggdrasil engine (#152)
|
21
|
+
|
16
22
|
## [5.1.1] - 2024-09-23
|
17
23
|
### Fixed
|
18
24
|
- increased accuracy of rollout distribution (#200)
|
data/lib/unleash/client.rb
CHANGED
@@ -2,7 +2,7 @@ require 'unleash/configuration'
|
|
2
2
|
require 'unleash/toggle_fetcher'
|
3
3
|
require 'unleash/metrics_reporter'
|
4
4
|
require 'unleash/scheduled_executor'
|
5
|
-
require 'unleash/
|
5
|
+
require 'unleash/variant'
|
6
6
|
require 'unleash/util/http'
|
7
7
|
require 'logger'
|
8
8
|
require 'time'
|
@@ -11,14 +11,17 @@ module Unleash
|
|
11
11
|
class Client
|
12
12
|
attr_accessor :fetcher_scheduled_executor, :metrics_scheduled_executor
|
13
13
|
|
14
|
+
# rubocop:disable Metrics/AbcSize
|
14
15
|
def initialize(*opts)
|
15
16
|
Unleash.configuration = Unleash::Configuration.new(*opts) unless opts.empty?
|
16
17
|
Unleash.configuration.validate!
|
17
18
|
|
18
19
|
Unleash.logger = Unleash.configuration.logger.clone
|
19
20
|
Unleash.logger.level = Unleash.configuration.log_level
|
21
|
+
Unleash.engine = YggdrasilEngine.new
|
22
|
+
Unleash.engine.register_custom_strategies(Unleash.configuration.strategies.custom_strategies)
|
20
23
|
|
21
|
-
Unleash.toggle_fetcher = Unleash::ToggleFetcher.new
|
24
|
+
Unleash.toggle_fetcher = Unleash::ToggleFetcher.new Unleash.engine
|
22
25
|
if Unleash.configuration.disable_client
|
23
26
|
Unleash.logger.warn "Unleash::Client is disabled! Will only return default (or bootstrapped if available) results!"
|
24
27
|
Unleash.logger.warn "Unleash::Client is disabled! Metrics and MetricsReporter are also disabled!"
|
@@ -30,6 +33,7 @@ module Unleash
|
|
30
33
|
start_toggle_fetcher
|
31
34
|
start_metrics unless Unleash.configuration.disable_metrics
|
32
35
|
end
|
36
|
+
# rubocop:enable Metrics/AbcSize
|
33
37
|
|
34
38
|
def is_enabled?(feature, context = nil, default_value_param = false, &fallback_blk)
|
35
39
|
Unleash.logger.debug "Unleash::Client.is_enabled? feature: #{feature} with context #{context}"
|
@@ -40,15 +44,16 @@ module Unleash
|
|
40
44
|
default_value_param
|
41
45
|
end
|
42
46
|
|
43
|
-
|
44
|
-
if
|
47
|
+
toggle_enabled = Unleash.engine.enabled?(feature, context)
|
48
|
+
if toggle_enabled.nil?
|
45
49
|
Unleash.logger.debug "Unleash::Client.is_enabled? feature: #{feature} not found"
|
50
|
+
Unleash.engine.count_toggle(feature, false)
|
46
51
|
return default_value
|
47
52
|
end
|
48
53
|
|
49
|
-
|
54
|
+
Unleash.engine.count_toggle(feature, toggle_enabled)
|
50
55
|
|
51
|
-
|
56
|
+
toggle_enabled
|
52
57
|
end
|
53
58
|
|
54
59
|
def is_disabled?(feature, context = nil, default_value_param = true, &fallback_blk)
|
@@ -71,23 +76,19 @@ module Unleash
|
|
71
76
|
end
|
72
77
|
|
73
78
|
def get_variant(feature, context = Unleash::Context.new, fallback_variant = disabled_variant)
|
74
|
-
Unleash.
|
75
|
-
|
76
|
-
toggle_as_hash = Unleash&.toggles&.select{ |toggle| toggle['name'] == feature }&.first
|
77
|
-
|
78
|
-
if toggle_as_hash.nil?
|
79
|
-
Unleash.logger.debug "Unleash::Client.get_variant feature: #{feature} not found"
|
80
|
-
return fallback_variant
|
81
|
-
end
|
82
|
-
|
83
|
-
toggle = Unleash::FeatureToggle.new(toggle_as_hash, Unleash&.segment_cache)
|
84
|
-
variant = toggle.get_variant(context, fallback_variant)
|
79
|
+
variant = Unleash.engine.get_variant(feature, context)
|
85
80
|
|
86
81
|
if variant.nil?
|
87
82
|
Unleash.logger.debug "Unleash::Client.get_variant variants for feature: #{feature} not found"
|
83
|
+
Unleash.engine.count_toggle(feature, false)
|
88
84
|
return fallback_variant
|
89
85
|
end
|
90
86
|
|
87
|
+
variant = Variant.new(variant)
|
88
|
+
|
89
|
+
Unleash.engine.count_variant(feature, variant.name)
|
90
|
+
Unleash.engine.count_toggle(feature, variant.feature_enabled)
|
91
|
+
|
91
92
|
# TODO: Add to README: name, payload, enabled (bool)
|
92
93
|
|
93
94
|
variant
|
@@ -96,7 +97,6 @@ module Unleash
|
|
96
97
|
# safe shutdown: also flush metrics to server and toggles to disk
|
97
98
|
def shutdown
|
98
99
|
unless Unleash.configuration.disable_client
|
99
|
-
Unleash.toggle_fetcher.save!
|
100
100
|
Unleash.reporter.post unless Unleash.configuration.disable_metrics
|
101
101
|
shutdown!
|
102
102
|
end
|
@@ -117,12 +117,12 @@ module Unleash
|
|
117
117
|
'appName': Unleash.configuration.app_name,
|
118
118
|
'instanceId': Unleash.configuration.instance_id,
|
119
119
|
'sdkVersion': "unleash-client-ruby:" + Unleash::VERSION,
|
120
|
-
'strategies': Unleash.strategies.
|
120
|
+
'strategies': Unleash.strategies.known_strategies,
|
121
121
|
'started': Time.now.iso8601(Unleash::TIME_RESOLUTION),
|
122
122
|
'interval': Unleash.configuration.metrics_interval_in_millis,
|
123
123
|
'platformName': RUBY_ENGINE,
|
124
124
|
'platformVersion': RUBY_VERSION,
|
125
|
-
'yggdrasilVersion':
|
125
|
+
'yggdrasilVersion': "0.13.2",
|
126
126
|
'specVersion': Unleash::CLIENT_SPECIFICATION_VERSION
|
127
127
|
}
|
128
128
|
end
|
@@ -140,7 +140,6 @@ module Unleash
|
|
140
140
|
end
|
141
141
|
|
142
142
|
def start_metrics
|
143
|
-
Unleash.toggle_metrics = Unleash::Metrics.new
|
144
143
|
Unleash.reporter = Unleash::MetricsReporter.new
|
145
144
|
self.metrics_scheduled_executor = Unleash::ScheduledExecutor.new(
|
146
145
|
'MetricsReporter',
|
@@ -166,7 +165,7 @@ module Unleash
|
|
166
165
|
end
|
167
166
|
|
168
167
|
def disabled_variant
|
169
|
-
@disabled_variant ||= Unleash::
|
168
|
+
@disabled_variant ||= Unleash::Variant.disabled_variant
|
170
169
|
end
|
171
170
|
|
172
171
|
def first_fetch_is_eager
|
@@ -40,9 +40,9 @@ module Unleash
|
|
40
40
|
def validate!
|
41
41
|
return if self.disable_client
|
42
42
|
|
43
|
-
raise ArgumentError, "
|
43
|
+
raise ArgumentError, "app_name is a required parameter." if self.app_name.nil?
|
44
44
|
|
45
|
-
validate_custom_http_headers!(self.custom_http_headers)
|
45
|
+
validate_custom_http_headers!(self.custom_http_headers) unless self.url.nil?
|
46
46
|
end
|
47
47
|
|
48
48
|
def refresh_backup_file!
|
data/lib/unleash/context.rb
CHANGED
@@ -7,20 +7,36 @@ module Unleash
|
|
7
7
|
def initialize(params = {})
|
8
8
|
raise ArgumentError, "Unleash::Context must be initialized with a hash." unless params.is_a?(Hash)
|
9
9
|
|
10
|
-
self.app_name
|
11
|
-
self.environment = value_for(
|
12
|
-
self.user_id
|
13
|
-
self.session_id
|
14
|
-
self.remote_address = value_for(
|
15
|
-
self.current_time = value_for(
|
16
|
-
|
17
|
-
properties = value_for(
|
10
|
+
self.app_name = value_for("appName", params, Unleash&.configuration&.app_name)
|
11
|
+
self.environment = value_for("environment", params, Unleash&.configuration&.environment || "default")
|
12
|
+
self.user_id = value_for("userId", params)&.to_s
|
13
|
+
self.session_id = value_for("sessionId", params)
|
14
|
+
self.remote_address = value_for("remoteAddress", params)
|
15
|
+
self.current_time = value_for("currentTime", params, Time.now.utc.iso8601.to_s)
|
16
|
+
|
17
|
+
properties = value_for("properties", params)
|
18
18
|
self.properties = properties.is_a?(Hash) ? properties.transform_keys(&:to_sym) : {}
|
19
19
|
end
|
20
20
|
|
21
21
|
def to_s
|
22
22
|
"<Context: user_id=#{@user_id},session_id=#{@session_id},remote_address=#{@remote_address},properties=#{@properties}" \
|
23
|
-
|
23
|
+
",app_name=#{@app_name},environment=#{@environment},current_time=#{@current_time}>"
|
24
|
+
end
|
25
|
+
|
26
|
+
def as_json
|
27
|
+
{
|
28
|
+
appName: to_safe_value(self.app_name),
|
29
|
+
environment: to_safe_value(self.environment),
|
30
|
+
userId: to_safe_value(self.user_id),
|
31
|
+
sessionId: to_safe_value(self.session_id),
|
32
|
+
remoteAddress: to_safe_value(self.remote_address),
|
33
|
+
currentTime: to_safe_value(self.current_time),
|
34
|
+
properties: self.properties.transform_values{ |value| to_safe_value(value) }
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_json(*options)
|
39
|
+
as_json(*options).to_json(*options)
|
24
40
|
end
|
25
41
|
|
26
42
|
def to_h
|
@@ -52,6 +68,16 @@ module Unleash
|
|
52
68
|
params.values_at(key, key.to_sym, underscore(key), underscore(key).to_sym).compact.first || default_value
|
53
69
|
end
|
54
70
|
|
71
|
+
def to_safe_value(value)
|
72
|
+
return nil if value.nil?
|
73
|
+
|
74
|
+
if value.is_a?(Time)
|
75
|
+
value.utc.iso8601
|
76
|
+
else
|
77
|
+
value.to_s
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
55
81
|
# converts CamelCase to snake_case
|
56
82
|
def underscore(camel_cased_word)
|
57
83
|
camel_cased_word.to_s.gsub(/(.)([A-Z])/, '\1_\2').downcase
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'unleash/configuration'
|
2
|
-
require 'unleash/metrics'
|
3
2
|
require 'net/http'
|
4
3
|
require 'json'
|
5
4
|
require 'time'
|
@@ -15,52 +14,39 @@ module Unleash
|
|
15
14
|
end
|
16
15
|
|
17
16
|
def generate_report
|
18
|
-
|
17
|
+
metrics = Unleash&.engine&.get_metrics()
|
18
|
+
return nil if metrics.nil? || metrics.empty?
|
19
19
|
|
20
|
-
|
21
|
-
stop = now
|
22
|
-
self.last_time = now
|
23
|
-
|
24
|
-
report = {
|
25
|
-
'appName': Unleash.configuration.app_name,
|
26
|
-
'instanceId': Unleash.configuration.instance_id,
|
20
|
+
{
|
27
21
|
'platformName': RUBY_ENGINE,
|
28
22
|
'platformVersion': RUBY_VERSION,
|
29
|
-
'yggdrasilVersion':
|
23
|
+
'yggdrasilVersion': "0.13.2",
|
30
24
|
'specVersion': Unleash::CLIENT_SPECIFICATION_VERSION,
|
31
|
-
'
|
32
|
-
|
33
|
-
|
34
|
-
'toggles': Unleash.toggle_metrics.features
|
35
|
-
}
|
25
|
+
'appName': Unleash.configuration.app_name,
|
26
|
+
'instanceId': Unleash.configuration.instance_id,
|
27
|
+
'bucket': metrics
|
36
28
|
}
|
37
|
-
Unleash.toggle_metrics.reset
|
38
|
-
|
39
|
-
report
|
40
29
|
end
|
41
30
|
|
42
31
|
def post
|
43
32
|
Unleash.logger.debug "post() Report"
|
44
33
|
|
45
|
-
|
34
|
+
bucket = self.generate_report
|
35
|
+
if bucket.nil? && (Time.now - self.last_time < LONGEST_WITHOUT_A_REPORT) # and last time is less then 10 minutes...
|
46
36
|
Unleash.logger.debug "Report not posted to server, as it would have been empty. (and has been empty for up to 10 min)"
|
47
37
|
|
48
38
|
return
|
49
39
|
end
|
50
40
|
|
51
|
-
response = Unleash::Util::Http.post(Unleash.configuration.client_metrics_uri,
|
41
|
+
response = Unleash::Util::Http.post(Unleash.configuration.client_metrics_uri, bucket.to_json)
|
52
42
|
|
53
43
|
if ['200', '202'].include? response.code
|
54
44
|
Unleash.logger.debug "Report sent to unleash server successfully. Server responded with http code #{response.code}"
|
55
45
|
else
|
46
|
+
# :nocov:
|
56
47
|
Unleash.logger.error "Error when sending report to unleash server. Server responded with http code #{response.code}."
|
48
|
+
# :nocov:
|
57
49
|
end
|
58
50
|
end
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
def bucket_empty?
|
63
|
-
Unleash.toggle_metrics.features.empty?
|
64
|
-
end
|
65
51
|
end
|
66
52
|
end
|
data/lib/unleash/strategies.rb
CHANGED
@@ -1,92 +1,33 @@
|
|
1
|
-
require 'unleash/strategy/base'
|
2
|
-
Gem.find_files('unleash/strategy/**/*.rb').each{ |path| require path }
|
3
|
-
|
4
1
|
module Unleash
|
2
|
+
class DefaultOverrideError < RuntimeError
|
3
|
+
end
|
4
|
+
|
5
5
|
class Strategies
|
6
|
+
attr_accessor :strategies
|
7
|
+
|
6
8
|
def initialize
|
7
9
|
@strategies = {}
|
8
|
-
register_strategies
|
9
|
-
end
|
10
|
-
|
11
|
-
def keys
|
12
|
-
@strategies.keys
|
13
10
|
end
|
14
11
|
|
15
12
|
def includes?(name)
|
16
|
-
@strategies.has_key?(name.to_s)
|
17
|
-
end
|
18
|
-
|
19
|
-
def fetch(name)
|
20
|
-
raise Unleash::Strategy::NotImplemented, "Strategy is not implemented" unless (strategy = @strategies[name.to_s])
|
21
|
-
|
22
|
-
strategy
|
13
|
+
@strategies.has_key?(name.to_s) || DEFAULT_STRATEGIES.include?(name.to_s)
|
23
14
|
end
|
24
15
|
|
25
16
|
def add(strategy)
|
26
|
-
if
|
27
|
-
Unleash.logger.error "WARNING: Overriding built in strategy '#{strategy.name}'. OVERIDING BUILT IN STRATEGIES IS \
|
28
|
-
DEPRECATED AND WILL BE REMOVED IN A FUTURE RELEASE."
|
29
|
-
end
|
30
|
-
self.internal_add(strategy)
|
31
|
-
end
|
32
|
-
|
33
|
-
def []=(key, strategy)
|
34
|
-
warn_deprecated_registration(strategy, 'modifying Unleash::STRATEGIES')
|
35
|
-
@strategies[key.to_s] = strategy
|
36
|
-
end
|
17
|
+
raise DefaultOverrideError, "Cannot override a default strategy" if DEFAULT_STRATEGIES.include?(strategy.name)
|
37
18
|
|
38
|
-
def [](key)
|
39
|
-
@strategies[key.to_s]
|
40
|
-
end
|
41
|
-
|
42
|
-
def register_strategies
|
43
|
-
register_base_strategies
|
44
|
-
register_custom_strategies
|
45
|
-
end
|
46
|
-
|
47
|
-
protected
|
48
|
-
|
49
|
-
# Deprecated: Use Unleash.configuration to add custom strategies
|
50
|
-
def register_custom_strategies
|
51
|
-
Unleash::Strategy.constants
|
52
|
-
.select{ |c| Unleash::Strategy.const_get(c).is_a? Class }
|
53
|
-
.reject{ |c| ['NotImplemented', 'Base'].include?(c.to_s) } # Reject abstract classes
|
54
|
-
.map{ |c| Object.const_get("Unleash::Strategy::#{c}") }
|
55
|
-
.reject{ |c| DEFAULT_STRATEGIES.include?(c) } # Reject base classes
|
56
|
-
.each do |c|
|
57
|
-
strategy = c.new
|
58
|
-
warn_deprecated_registration(strategy, 'adding custom class into Unleash::Strategy namespace')
|
59
|
-
self.internal_add(strategy)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def register_base_strategies
|
64
|
-
DEFAULT_STRATEGIES.each{ |c| self.internal_add(c.new) }
|
65
|
-
end
|
66
|
-
|
67
|
-
def internal_add(strategy)
|
68
19
|
@strategies[strategy.name] = strategy
|
69
20
|
end
|
70
21
|
|
71
|
-
def
|
72
|
-
|
22
|
+
def custom_strategies
|
23
|
+
@strategies.values
|
73
24
|
end
|
74
25
|
|
75
|
-
|
76
|
-
|
77
|
-
Unleash::Strategy::Default,
|
78
|
-
Unleash::Strategy::FlexibleRollout,
|
79
|
-
Unleash::Strategy::GradualRolloutRandom,
|
80
|
-
Unleash::Strategy::GradualRolloutSessionId,
|
81
|
-
Unleash::Strategy::GradualRolloutUserId,
|
82
|
-
Unleash::Strategy::RemoteAddress,
|
83
|
-
Unleash::Strategy::UserWithId
|
84
|
-
].freeze
|
85
|
-
|
86
|
-
def warn_deprecated_registration(strategy, method)
|
87
|
-
warn "[DEPRECATED] Registering custom Unleash strategy by #{method} is deprecated.
|
88
|
-
Please use Unleash configuration to register custom strategy: " \
|
89
|
-
"`Unleash.configure {|c| c.strategies.add(#{strategy.class.name}.new) }`"
|
26
|
+
def known_strategies
|
27
|
+
@strategies.keys.map{ |key| { name: key } }
|
90
28
|
end
|
29
|
+
|
30
|
+
DEFAULT_STRATEGIES = ['applicationHostname', 'default', 'flexibleRollout', 'gradualRolloutRandom', 'gradualRolloutSessionId',
|
31
|
+
'gradualRolloutUserId', 'remoteAddress', 'userWithId'].freeze
|
91
32
|
end
|
92
33
|
end
|
@@ -2,15 +2,15 @@ require 'unleash/configuration'
|
|
2
2
|
require 'unleash/bootstrap/handler'
|
3
3
|
require 'net/http'
|
4
4
|
require 'json'
|
5
|
+
require 'yggdrasil_engine'
|
5
6
|
|
6
7
|
module Unleash
|
7
8
|
class ToggleFetcher
|
8
|
-
attr_accessor :
|
9
|
+
attr_accessor :toggle_engine, :toggle_lock, :toggle_resource, :etag, :retry_count
|
9
10
|
|
10
|
-
def initialize
|
11
|
+
def initialize(engine)
|
12
|
+
self.toggle_engine = engine
|
11
13
|
self.etag = nil
|
12
|
-
self.toggle_cache = nil
|
13
|
-
self.segment_cache = nil
|
14
14
|
self.toggle_lock = Mutex.new
|
15
15
|
self.toggle_resource = ConditionVariable.new
|
16
16
|
self.retry_count = 0
|
@@ -32,14 +32,6 @@ module Unleash
|
|
32
32
|
# once initialized, somewhere else you will want to start a loop with fetch()
|
33
33
|
end
|
34
34
|
|
35
|
-
def toggles
|
36
|
-
self.toggle_lock.synchronize do
|
37
|
-
# wait for resource, only if it is null
|
38
|
-
self.toggle_resource.wait(self.toggle_lock) if self.toggle_cache.nil?
|
39
|
-
return self.toggle_cache
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
35
|
# rename to refresh_from_server! ??
|
44
36
|
def fetch
|
45
37
|
Unleash.logger.debug "fetch()"
|
@@ -55,16 +47,14 @@ module Unleash
|
|
55
47
|
end
|
56
48
|
|
57
49
|
self.etag = response['ETag']
|
58
|
-
features = get_features(response.body)
|
59
50
|
|
60
51
|
# always synchronize with the local cache when fetching:
|
61
|
-
|
52
|
+
update_engine_state!(response.body)
|
62
53
|
|
63
|
-
|
64
|
-
save!
|
54
|
+
save! response.body
|
65
55
|
end
|
66
56
|
|
67
|
-
def save!
|
57
|
+
def save!(toggle_data)
|
68
58
|
Unleash.logger.debug "Will save toggles to disk now"
|
69
59
|
|
70
60
|
backup_file = Unleash.configuration.backup_file
|
@@ -72,7 +62,7 @@ module Unleash
|
|
72
62
|
|
73
63
|
self.toggle_lock.synchronize do
|
74
64
|
File.open(backup_file_tmp, "w") do |file|
|
75
|
-
file.write(
|
65
|
+
file.write(toggle_data)
|
76
66
|
end
|
77
67
|
File.rename(backup_file_tmp, backup_file)
|
78
68
|
end
|
@@ -84,23 +74,13 @@ module Unleash
|
|
84
74
|
|
85
75
|
private
|
86
76
|
|
87
|
-
def
|
88
|
-
|
89
|
-
self.
|
90
|
-
self.toggle_cache = features
|
91
|
-
end
|
92
|
-
|
93
|
-
# notify all threads waiting for this resource to no longer wait
|
94
|
-
self.toggle_resource.broadcast
|
77
|
+
def update_engine_state!(toggle_data)
|
78
|
+
self.toggle_lock.synchronize do
|
79
|
+
self.toggle_engine.take_state(toggle_data)
|
95
80
|
end
|
96
|
-
end
|
97
81
|
|
98
|
-
|
99
|
-
|
100
|
-
Unleash.logger.info "Updating toggles to main client, there has been a change in the server."
|
101
|
-
Unleash.toggles = self.toggles["features"]
|
102
|
-
Unleash.segment_cache = self.toggles["segments"]
|
103
|
-
end
|
82
|
+
# notify all threads waiting for this resource to no longer wait
|
83
|
+
self.toggle_resource.broadcast
|
104
84
|
end
|
105
85
|
|
106
86
|
def read!
|
@@ -108,42 +88,28 @@ module Unleash
|
|
108
88
|
backup_file = Unleash.configuration.backup_file
|
109
89
|
return nil unless File.exist?(backup_file)
|
110
90
|
|
111
|
-
|
112
|
-
|
113
|
-
update_running_client!
|
91
|
+
backup_data = File.read(backup_file)
|
92
|
+
update_engine_state!(backup_data)
|
114
93
|
rescue IOError => e
|
94
|
+
# :nocov:
|
115
95
|
Unleash.logger.error "Unable to read the backup_file: #{e}"
|
96
|
+
# :nocov:
|
116
97
|
rescue JSON::ParserError => e
|
98
|
+
# :nocov:
|
117
99
|
Unleash.logger.error "Unable to parse JSON from existing backup_file: #{e}"
|
100
|
+
# :nocov:
|
118
101
|
rescue StandardError => e
|
102
|
+
# :nocov:
|
119
103
|
Unleash.logger.error "Unable to extract valid data from backup_file. Exception thrown: #{e}"
|
104
|
+
# :nocov:
|
120
105
|
end
|
121
106
|
|
122
107
|
def bootstrap
|
123
108
|
bootstrap_payload = Unleash::Bootstrap::Handler.new(Unleash.configuration.bootstrap_config).retrieve_toggles
|
124
|
-
|
125
|
-
update_running_client!
|
109
|
+
update_engine_state! bootstrap_payload
|
126
110
|
|
127
111
|
# reset Unleash.configuration.bootstrap_data to free up memory, as we will never use it again
|
128
112
|
Unleash.configuration.bootstrap_config = nil
|
129
113
|
end
|
130
|
-
|
131
|
-
def build_segment_map(segments_array)
|
132
|
-
return {} if segments_array.nil?
|
133
|
-
|
134
|
-
segments_array.map{ |segment| [segment["id"], segment] }.to_h
|
135
|
-
end
|
136
|
-
|
137
|
-
# @param response_body [String]
|
138
|
-
def get_features(response_body)
|
139
|
-
response_hash = JSON.parse(response_body)
|
140
|
-
|
141
|
-
if response_hash['version'] >= 1
|
142
|
-
return { "features" => response_hash["features"], "segments" => build_segment_map(response_hash["segments"]) }
|
143
|
-
end
|
144
|
-
|
145
|
-
raise NotImplemented, "Version of features provided by unleash server" \
|
146
|
-
" is unsupported by this client."
|
147
|
-
end
|
148
114
|
end
|
149
115
|
end
|
data/lib/unleash/variant.rb
CHANGED
@@ -14,12 +14,18 @@ module Unleash
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def to_s
|
17
|
+
# :nocov:
|
17
18
|
"<Variant: name=#{self.name},enabled=#{self.enabled},payload=#{self.payload},feature_enabled=#{self.feature_enabled}>"
|
19
|
+
# :nocov:
|
18
20
|
end
|
19
21
|
|
20
22
|
def ==(other)
|
21
23
|
self.name == other.name && self.enabled == other.enabled && self.payload == other.payload \
|
22
24
|
&& self.feature_enabled == other.feature_enabled
|
23
25
|
end
|
26
|
+
|
27
|
+
def self.disabled_variant
|
28
|
+
Variant.new(name: 'disabled', enabled: false, feature_enabled: false)
|
29
|
+
end
|
24
30
|
end
|
25
31
|
end
|
data/lib/unleash/version.rb
CHANGED
data/lib/unleash.rb
CHANGED
@@ -10,7 +10,7 @@ module Unleash
|
|
10
10
|
TIME_RESOLUTION = 3
|
11
11
|
|
12
12
|
class << self
|
13
|
-
attr_accessor :configuration, :toggle_fetcher, :
|
13
|
+
attr_accessor :configuration, :toggle_fetcher, :reporter, :logger, :engine
|
14
14
|
end
|
15
15
|
|
16
16
|
self.configuration = Unleash::Configuration.new
|
data/unleash-client.gemspec
CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.required_ruby_version = ">= 2.6"
|
25
25
|
|
26
26
|
spec.add_dependency "murmurhash3", "~> 0.1.7"
|
27
|
+
spec.add_dependency "yggdrasil-engine", "~> 0.0.5"
|
27
28
|
|
28
29
|
spec.add_development_dependency "bundler", "~> 2.1"
|
29
30
|
spec.add_development_dependency "rake", "~> 12.3"
|