unleash 5.1.1 → 6.0.5.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 = 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)
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
- ",app_name=#{@app_name},environment=#{@environment},current_time=#{@current_time}>"
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
- now = Time.now
17
+ metrics = Unleash&.engine&.get_metrics()
18
+ return nil if metrics.nil? || metrics.empty?
19
19
 
20
- start = self.last_time
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': nil,
23
+ 'yggdrasilVersion': "0.13.2",
30
24
  'specVersion': Unleash::CLIENT_SPECIFICATION_VERSION,
31
- 'bucket': {
32
- 'start': start.iso8601(Unleash::TIME_RESOLUTION),
33
- 'stop': stop.iso8601(Unleash::TIME_RESOLUTION),
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
- if bucket_empty? && (Time.now - self.last_time < LONGEST_WITHOUT_A_REPORT) # and last time is less then 10 minutes...
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, self.generate_report.to_json)
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
@@ -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 default_strategy_names.include?(strategy.name)
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 default_strategy_names
72
- DEFAULT_STRATEGIES.map{ |strategy_class| strategy_class.new.name }
22
+ def custom_strategies
23
+ @strategies.values
73
24
  end
74
25
 
75
- DEFAULT_STRATEGIES = [
76
- Unleash::Strategy::ApplicationHostname,
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 :toggle_cache, :toggle_lock, :toggle_resource, :etag, :retry_count, :segment_cache
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
- synchronize_with_local_cache!(features)
52
+ update_engine_state!(response.body)
62
53
 
63
- update_running_client!
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(self.toggle_cache.to_json)
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 synchronize_with_local_cache!(features)
88
- if self.toggle_cache != features
89
- self.toggle_lock.synchronize do
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
- def update_running_client!
99
- if Unleash.toggles != self.toggles["features"] || Unleash.segment_cache != self.toggles["segments"]
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
- backup_as_hash = JSON.parse(File.read(backup_file))
112
- synchronize_with_local_cache!(backup_as_hash)
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
- synchronize_with_local_cache! get_features bootstrap_payload
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Unleash
2
- VERSION = "5.1.1".freeze
2
+ VERSION = "6.0.5.pre".freeze
3
3
  end
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, :toggles, :toggle_metrics, :reporter, :segment_cache, :logger
13
+ attr_accessor :configuration, :toggle_fetcher, :reporter, :logger, :engine
14
14
  end
15
15
 
16
16
  self.configuration = Unleash::Configuration.new
@@ -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.6"
27
28
 
28
29
  spec.add_development_dependency "bundler", "~> 2.1"
29
30
  spec.add_development_dependency "rake", "~> 12.3"
@@ -0,0 +1,21 @@
1
+ # Migrating to Unleash-Client-Ruby 6.0.0
2
+
3
+ This guide highlights the key changes you should be aware of when upgrading to v6.0.0 of the Unleash client.
4
+
5
+ ## Custom strategy changes
6
+
7
+ In version 6+, custom strategies cannot override the built-in strategies. Specifically, strategies `applicationHostname`, `default`, `flexibleRollout`, `gradualRolloutRandom`, `gradualRolloutSessionId`, `gradualRolloutUserId`, `remoteAddress` or `userWithId` throw an error on startup. Previously, creating a custom strategy would only generate a warning in the logs.
8
+
9
+ The deprecated `register_custom_strategies` method has been removed. You can continue to [register custom strategies](./README.md#custom-strategies) using configuration.
10
+
11
+ ## Direct access to strategy objects
12
+
13
+ **Note:** If you're not using the method `known_strategies` this section doesn't affect you
14
+
15
+ The objects for base strategies are no longer directly accessible via the SDK. The `known_strategies` method only returns custom strategies registered by the user. To check if a custom strategy will override either a built-in or custom strategy, use the `includes?` method (returns false if the name is available).
16
+
17
+ It is strongly discouraged to access or modify any properties of the built-in strategies other than the name. In version 6+, this is a hard requirement.
18
+
19
+ ## ARM requirements
20
+
21
+ Version 6.0.0 introduces a new dependency on a native binary. Currently, only ARM binaries for macOS are distributed. If you require ARM support for Linux or Windows, please open a GitHub issue.
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: 5.1.1
4
+ version: 6.0.5.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Renato Arruda
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 1980-01-01 00:00:00.000000000 Z
11
+ date: 2024-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: murmurhash3
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.1.7
27
+ - !ruby/object:Gem::Dependency
28
+ name: yggdrasil-engine
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.6
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.0.6
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -166,7 +180,6 @@ files:
166
180
  - examples/extending_unleash_with_opentelemetry.rb
167
181
  - examples/simple.rb
168
182
  - lib/unleash.rb
169
- - lib/unleash/activation_strategy.rb
170
183
  - lib/unleash/bootstrap/configuration.rb
171
184
  - lib/unleash/bootstrap/handler.rb
172
185
  - lib/unleash/bootstrap/provider/base.rb
@@ -174,36 +187,22 @@ files:
174
187
  - lib/unleash/bootstrap/provider/from_url.rb
175
188
  - lib/unleash/client.rb
176
189
  - lib/unleash/configuration.rb
177
- - lib/unleash/constraint.rb
178
190
  - lib/unleash/context.rb
179
- - lib/unleash/feature_toggle.rb
180
- - lib/unleash/metrics.rb
181
191
  - lib/unleash/metrics_reporter.rb
182
192
  - lib/unleash/scheduled_executor.rb
183
193
  - lib/unleash/spec_version.rb
184
194
  - lib/unleash/strategies.rb
185
- - lib/unleash/strategy/application_hostname.rb
186
- - lib/unleash/strategy/base.rb
187
- - lib/unleash/strategy/default.rb
188
- - lib/unleash/strategy/flexible_rollout.rb
189
- - lib/unleash/strategy/gradual_rollout_random.rb
190
- - lib/unleash/strategy/gradual_rollout_sessionid.rb
191
- - lib/unleash/strategy/gradual_rollout_userid.rb
192
- - lib/unleash/strategy/remote_address.rb
193
- - lib/unleash/strategy/user_with_id.rb
194
- - lib/unleash/strategy/util.rb
195
195
  - lib/unleash/toggle_fetcher.rb
196
196
  - lib/unleash/util/http.rb
197
197
  - lib/unleash/variant.rb
198
- - lib/unleash/variant_definition.rb
199
- - lib/unleash/variant_override.rb
200
198
  - lib/unleash/version.rb
201
199
  - unleash-client.gemspec
200
+ - v6_MIGRATION_GUIDE.md
202
201
  homepage: https://github.com/unleash/unleash-client-ruby
203
202
  licenses:
204
203
  - Apache-2.0
205
204
  metadata: {}
206
- post_install_message:
205
+ post_install_message:
207
206
  rdoc_options: []
208
207
  require_paths:
209
208
  - lib
@@ -218,8 +217,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
218
217
  - !ruby/object:Gem::Version
219
218
  version: '0'
220
219
  requirements: []
221
- rubygems_version: 3.5.16
222
- signing_key:
220
+ rubygems_version: 3.5.6
221
+ signing_key:
223
222
  specification_version: 4
224
223
  summary: Unleash feature toggle client.
225
224
  test_files: []
@@ -1,44 +0,0 @@
1
- module Unleash
2
- class ActivationStrategy
3
- attr_accessor :name, :params, :constraints, :disabled, :variant_definitions
4
-
5
- def initialize(name, params, constraints = [], variant_definitions = [])
6
- self.name = name
7
- self.disabled = false
8
-
9
- if params.is_a?(Hash)
10
- self.params = params
11
- elsif params.nil?
12
- self.params = {}
13
- else
14
- Unleash.logger.warn "Invalid params provided for ActivationStrategy (params:#{params})"
15
- self.params = {}
16
- end
17
-
18
- if constraints.is_a?(Array) && constraints.all?{ |c| c.is_a?(Constraint) }
19
- self.constraints = constraints
20
- else
21
- Unleash.logger.warn "Invalid constraints provided for ActivationStrategy (constraints: #{constraints})"
22
- self.disabled = true
23
- self.constraints = []
24
- end
25
-
26
- self.variant_definitions = valid_variant_definitions(variant_definitions)
27
- end
28
-
29
- def matches_context?(context)
30
- self.constraints.any?{ |c| c.matches_context? context }
31
- end
32
-
33
- private
34
-
35
- def valid_variant_definitions(variant_definitions)
36
- if variant_definitions.is_a?(Array) && variant_definitions.all?{ |variant_definition| variant_definition.is_a?(VariantDefinition) }
37
- variant_definitions
38
- else
39
- Unleash.logger.warn "Invalid variant_definitions provided for ActivationStrategy (variant_definitions: #{variant_definitions})"
40
- []
41
- end
42
- end
43
- end
44
- end