optimizely-sdk 3.3.2.rc1 → 3.6.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/lib/optimizely.rb +198 -36
- data/lib/optimizely/audience.rb +22 -13
- data/lib/optimizely/bucketer.rb +3 -8
- data/lib/optimizely/config/datafile_project_config.rb +17 -3
- data/lib/optimizely/config/proxy_config.rb +34 -0
- data/lib/optimizely/config_manager/async_scheduler.rb +6 -2
- data/lib/optimizely/config_manager/http_project_config_manager.rb +45 -25
- data/lib/optimizely/config_manager/static_project_config_manager.rb +6 -2
- data/lib/optimizely/custom_attribute_condition_evaluator.rb +133 -37
- data/lib/optimizely/decision_service.rb +30 -29
- data/lib/optimizely/event/batch_event_processor.rb +47 -39
- data/lib/optimizely/event_dispatcher.rb +8 -14
- data/lib/optimizely/exceptions.rb +17 -9
- data/lib/optimizely/helpers/constants.rb +18 -5
- data/lib/optimizely/helpers/http_utils.rb +64 -0
- data/lib/optimizely/helpers/variable_type.rb +8 -1
- data/lib/optimizely/optimizely_config.rb +117 -0
- data/lib/optimizely/optimizely_factory.rb +54 -5
- data/lib/optimizely/project_config.rb +3 -1
- data/lib/optimizely/semantic_version.rb +166 -0
- data/lib/optimizely/version.rb +1 -1
- metadata +9 -20
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2020, Optimizely and contributors
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
#
|
18
|
+
|
19
|
+
module Optimizely
|
20
|
+
class ProxyConfig
|
21
|
+
attr_reader :host, :port, :username, :password
|
22
|
+
|
23
|
+
def initialize(host, port = nil, username = nil, password = nil)
|
24
|
+
# host - DNS name or IP address of proxy
|
25
|
+
# port - port to use to acess the proxy
|
26
|
+
# username - username if authorization is required
|
27
|
+
# password - password if authorization is required
|
28
|
+
@host = host
|
29
|
+
@port = port
|
30
|
+
@username = username
|
31
|
+
@password = password
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#
|
4
|
-
# Copyright 2019, Optimizely and contributors
|
4
|
+
# Copyright 2019-2020, Optimizely and contributors
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
# you may not use this file except in compliance with the License.
|
@@ -19,13 +19,14 @@ module Optimizely
|
|
19
19
|
class AsyncScheduler
|
20
20
|
attr_reader :running
|
21
21
|
|
22
|
-
def initialize(callback, interval, auto_update, logger = nil)
|
22
|
+
def initialize(callback, interval, auto_update, logger = nil, error_handler = nil)
|
23
23
|
# Sets up AsyncScheduler to execute a callback periodically.
|
24
24
|
#
|
25
25
|
# callback - Main function to be executed periodically.
|
26
26
|
# interval - How many seconds to wait between executions.
|
27
27
|
# auto_update - boolean indicates to run infinitely or only once.
|
28
28
|
# logger - Optional Provides a logger instance.
|
29
|
+
# error_handler - Optional Provides a handle_error method to handle exceptions.
|
29
30
|
|
30
31
|
@callback = callback
|
31
32
|
@interval = interval
|
@@ -33,6 +34,7 @@ module Optimizely
|
|
33
34
|
@running = false
|
34
35
|
@thread = nil
|
35
36
|
@logger = logger || NoOpLogger.new
|
37
|
+
@error_handler = error_handler || NoOpErrorHandler.new
|
36
38
|
end
|
37
39
|
|
38
40
|
def start!
|
@@ -54,6 +56,7 @@ module Optimizely
|
|
54
56
|
Logger::ERROR,
|
55
57
|
"Couldn't create a new thread for async scheduler. #{e.message}"
|
56
58
|
)
|
59
|
+
@error_handler.handle_error(e)
|
57
60
|
end
|
58
61
|
end
|
59
62
|
|
@@ -80,6 +83,7 @@ module Optimizely
|
|
80
83
|
Logger::ERROR,
|
81
84
|
"Something went wrong when executing passed callback. #{e.message}"
|
82
85
|
)
|
86
|
+
@error_handler.handle_error(e)
|
83
87
|
stop!
|
84
88
|
end
|
85
89
|
break unless @auto_update
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#
|
4
|
-
# Copyright 2019, Optimizely and contributors
|
4
|
+
# Copyright 2019-2020, Optimizely and contributors
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
# you may not use this file except in compliance with the License.
|
@@ -19,18 +19,21 @@ require_relative '../config/datafile_project_config'
|
|
19
19
|
require_relative '../error_handler'
|
20
20
|
require_relative '../exceptions'
|
21
21
|
require_relative '../helpers/constants'
|
22
|
+
require_relative '../helpers/http_utils'
|
22
23
|
require_relative '../logger'
|
23
24
|
require_relative '../notification_center'
|
24
25
|
require_relative '../project_config'
|
26
|
+
require_relative '../optimizely_config'
|
25
27
|
require_relative 'project_config_manager'
|
26
28
|
require_relative 'async_scheduler'
|
27
|
-
|
29
|
+
|
28
30
|
require 'json'
|
31
|
+
|
29
32
|
module Optimizely
|
30
33
|
class HTTPProjectConfigManager < ProjectConfigManager
|
31
34
|
# Config manager that polls for the datafile and updated ProjectConfig based on an update interval.
|
32
35
|
|
33
|
-
attr_reader :stopped
|
36
|
+
attr_reader :stopped, :optimizely_config
|
34
37
|
|
35
38
|
# Initialize config manager. One of sdk_key or url has to be set to be able to use.
|
36
39
|
#
|
@@ -48,6 +51,8 @@ module Optimizely
|
|
48
51
|
# error_handler - Provides a handle_error method to handle exceptions.
|
49
52
|
# skip_json_validation - Optional boolean param which allows skipping JSON schema
|
50
53
|
# validation upon object invocation. By default JSON schema validation will be performed.
|
54
|
+
# datafile_access_token - access token used to fetch private datafiles
|
55
|
+
# proxy_config - Optional proxy config instancea to configure making web requests through a proxy server.
|
51
56
|
def initialize(
|
52
57
|
sdk_key: nil,
|
53
58
|
url: nil,
|
@@ -60,10 +65,13 @@ module Optimizely
|
|
60
65
|
logger: nil,
|
61
66
|
error_handler: nil,
|
62
67
|
skip_json_validation: false,
|
63
|
-
notification_center: nil
|
68
|
+
notification_center: nil,
|
69
|
+
datafile_access_token: nil,
|
70
|
+
proxy_config: nil
|
64
71
|
)
|
65
72
|
@logger = logger || NoOpLogger.new
|
66
73
|
@error_handler = error_handler || NoOpErrorHandler.new
|
74
|
+
@access_token = datafile_access_token
|
67
75
|
@datafile_url = get_datafile_url(sdk_key, url, url_template)
|
68
76
|
@polling_interval = nil
|
69
77
|
polling_interval(polling_interval)
|
@@ -73,12 +81,14 @@ module Optimizely
|
|
73
81
|
@skip_json_validation = skip_json_validation
|
74
82
|
@notification_center = notification_center.is_a?(Optimizely::NotificationCenter) ? notification_center : NotificationCenter.new(@logger, @error_handler)
|
75
83
|
@config = datafile.nil? ? nil : DatafileProjectConfig.create(datafile, @logger, @error_handler, @skip_json_validation)
|
84
|
+
@optimizely_config = @config.nil? ? nil : OptimizelyConfig.new(@config).config
|
76
85
|
@mutex = Mutex.new
|
77
86
|
@resource = ConditionVariable.new
|
78
87
|
@async_scheduler = AsyncScheduler.new(method(:fetch_datafile_config), @polling_interval, auto_update, @logger)
|
79
88
|
# Start async scheduler in the end to avoid race condition where scheduler executes
|
80
89
|
# callback which makes use of variables not yet initialized by the main thread.
|
81
90
|
@async_scheduler.start! if start_by_default == true
|
91
|
+
@proxy_config = proxy_config
|
82
92
|
@stopped = false
|
83
93
|
end
|
84
94
|
|
@@ -141,21 +151,20 @@ module Optimizely
|
|
141
151
|
end
|
142
152
|
|
143
153
|
def request_config
|
144
|
-
@logger.log(
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
headers = {
|
150
|
-
'Content-Type' => 'application/json'
|
151
|
-
}
|
154
|
+
@logger.log(Logger::DEBUG, "Fetching datafile from #{@datafile_url}")
|
155
|
+
headers = {}
|
156
|
+
headers['Content-Type'] = 'application/json'
|
157
|
+
headers['If-Modified-Since'] = @last_modified if @last_modified
|
158
|
+
headers['Authorization'] = "Bearer #{@access_token}" unless @access_token.nil?
|
152
159
|
|
153
|
-
|
160
|
+
# Cleaning headers before logging to avoid exposing authorization token
|
161
|
+
cleansed_headers = {}
|
162
|
+
headers.each { |key, value| cleansed_headers[key] = key == 'Authorization' ? '********' : value }
|
163
|
+
@logger.log(Logger::DEBUG, "Datafile request headers: #{cleansed_headers}")
|
154
164
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
timeout: Helpers::Constants::CONFIG_MANAGER['REQUEST_TIMEOUT']
|
165
|
+
begin
|
166
|
+
response = Helpers::HttpUtils.make_request(
|
167
|
+
@datafile_url, :get, nil, headers, Helpers::Constants::CONFIG_MANAGER['REQUEST_TIMEOUT'], @proxy_config
|
159
168
|
)
|
160
169
|
rescue StandardError => e
|
161
170
|
@logger.log(
|
@@ -165,6 +174,9 @@ module Optimizely
|
|
165
174
|
return nil
|
166
175
|
end
|
167
176
|
|
177
|
+
response_code = response.code.to_i
|
178
|
+
@logger.log(Logger::DEBUG, "Datafile response status code #{response_code}")
|
179
|
+
|
168
180
|
# Leave datafile and config unchanged if it has not been modified.
|
169
181
|
if response.code == '304'
|
170
182
|
@logger.log(
|
@@ -174,9 +186,14 @@ module Optimizely
|
|
174
186
|
return
|
175
187
|
end
|
176
188
|
|
177
|
-
|
178
|
-
|
179
|
-
|
189
|
+
if response_code >= 200 && response_code < 400
|
190
|
+
@logger.log(Logger::DEBUG, 'Successfully fetched datafile, generating Project config')
|
191
|
+
config = DatafileProjectConfig.create(response.body, @logger, @error_handler, @skip_json_validation)
|
192
|
+
@last_modified = response[Helpers::Constants::HTTP_HEADERS['LAST_MODIFIED']]
|
193
|
+
@logger.log(Logger::DEBUG, "Saved last modified header value from response: #{@last_modified}.")
|
194
|
+
else
|
195
|
+
@logger.log(Logger::DEBUG, "Datafile fetch failed, status: #{response.code}, message: #{response.message}")
|
196
|
+
end
|
180
197
|
|
181
198
|
config
|
182
199
|
end
|
@@ -192,6 +209,7 @@ module Optimizely
|
|
192
209
|
end
|
193
210
|
|
194
211
|
@config = config
|
212
|
+
@optimizely_config = OptimizelyConfig.new(config).config
|
195
213
|
|
196
214
|
@notification_center.send_notifications(NotificationCenter::NOTIFICATION_TYPES[:OPTIMIZELY_CONFIG_UPDATE])
|
197
215
|
|
@@ -281,17 +299,19 @@ module Optimizely
|
|
281
299
|
# SDK key to determine URL from which to fetch the datafile.
|
282
300
|
# Returns String representing URL to fetch datafile from.
|
283
301
|
if sdk_key.nil? && url.nil?
|
284
|
-
|
285
|
-
@
|
302
|
+
error_msg = 'Must provide at least one of sdk_key or url.'
|
303
|
+
@logger.log(Logger::ERROR, error_msg)
|
304
|
+
@error_handler.handle_error(InvalidInputsError.new(error_msg))
|
286
305
|
end
|
287
306
|
|
288
307
|
unless url
|
289
|
-
url_template ||= Helpers::Constants::CONFIG_MANAGER['DATAFILE_URL_TEMPLATE']
|
308
|
+
url_template ||= @access_token.nil? ? Helpers::Constants::CONFIG_MANAGER['DATAFILE_URL_TEMPLATE'] : Helpers::Constants::CONFIG_MANAGER['AUTHENTICATED_DATAFILE_URL_TEMPLATE']
|
290
309
|
begin
|
291
310
|
return (url_template % sdk_key)
|
292
311
|
rescue
|
293
|
-
|
294
|
-
@
|
312
|
+
error_msg = "Invalid url_template #{url_template} provided."
|
313
|
+
@logger.log(Logger::ERROR, error_msg)
|
314
|
+
@error_handler.handle_error(InvalidInputsError.new(error_msg))
|
295
315
|
end
|
296
316
|
end
|
297
317
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#
|
4
|
-
# Copyright 2019, Optimizely and contributors
|
4
|
+
# Copyright 2019-2020, Optimizely and contributors
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
# you may not use this file except in compliance with the License.
|
@@ -17,11 +17,13 @@
|
|
17
17
|
#
|
18
18
|
|
19
19
|
require_relative '../config/datafile_project_config'
|
20
|
+
require_relative '../optimizely_config'
|
20
21
|
require_relative 'project_config_manager'
|
22
|
+
|
21
23
|
module Optimizely
|
22
24
|
class StaticProjectConfigManager < ProjectConfigManager
|
23
25
|
# Implementation of ProjectConfigManager interface.
|
24
|
-
attr_reader :config
|
26
|
+
attr_reader :config, :optimizely_config
|
25
27
|
|
26
28
|
def initialize(datafile, logger, error_handler, skip_json_validation)
|
27
29
|
# Looks up and sets datafile and config based on response body.
|
@@ -38,6 +40,8 @@ module Optimizely
|
|
38
40
|
error_handler,
|
39
41
|
skip_json_validation
|
40
42
|
)
|
43
|
+
|
44
|
+
@optimizely_config = @config.nil? ? nil : OptimizelyConfig.new(@config).config
|
41
45
|
end
|
42
46
|
end
|
43
47
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#
|
4
|
-
# Copyright 2019, Optimizely and contributors
|
4
|
+
# Copyright 2019-2020, Optimizely and contributors
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
# you may not use this file except in compliance with the License.
|
@@ -15,8 +15,10 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
|
+
require_relative 'exceptions'
|
18
19
|
require_relative 'helpers/constants'
|
19
20
|
require_relative 'helpers/validator'
|
21
|
+
require_relative 'semantic_version'
|
20
22
|
|
21
23
|
module Optimizely
|
22
24
|
class CustomAttributeConditionEvaluator
|
@@ -26,15 +28,29 @@ module Optimizely
|
|
26
28
|
EXACT_MATCH_TYPE = 'exact'
|
27
29
|
EXISTS_MATCH_TYPE = 'exists'
|
28
30
|
GREATER_THAN_MATCH_TYPE = 'gt'
|
31
|
+
GREATER_EQUAL_MATCH_TYPE = 'ge'
|
29
32
|
LESS_THAN_MATCH_TYPE = 'lt'
|
33
|
+
LESS_EQUAL_MATCH_TYPE = 'le'
|
30
34
|
SUBSTRING_MATCH_TYPE = 'substring'
|
35
|
+
SEMVER_EQ = 'semver_eq'
|
36
|
+
SEMVER_GE = 'semver_ge'
|
37
|
+
SEMVER_GT = 'semver_gt'
|
38
|
+
SEMVER_LE = 'semver_le'
|
39
|
+
SEMVER_LT = 'semver_lt'
|
31
40
|
|
32
41
|
EVALUATORS_BY_MATCH_TYPE = {
|
33
42
|
EXACT_MATCH_TYPE => :exact_evaluator,
|
34
43
|
EXISTS_MATCH_TYPE => :exists_evaluator,
|
35
44
|
GREATER_THAN_MATCH_TYPE => :greater_than_evaluator,
|
45
|
+
GREATER_EQUAL_MATCH_TYPE => :greater_than_or_equal_evaluator,
|
36
46
|
LESS_THAN_MATCH_TYPE => :less_than_evaluator,
|
37
|
-
|
47
|
+
LESS_EQUAL_MATCH_TYPE => :less_than_or_equal_evaluator,
|
48
|
+
SUBSTRING_MATCH_TYPE => :substring_evaluator,
|
49
|
+
SEMVER_EQ => :semver_equal_evaluator,
|
50
|
+
SEMVER_GE => :semver_greater_than_or_equal_evaluator,
|
51
|
+
SEMVER_GT => :semver_greater_than_evaluator,
|
52
|
+
SEMVER_LE => :semver_less_than_or_equal_evaluator,
|
53
|
+
SEMVER_LT => :semver_less_than_evaluator
|
38
54
|
}.freeze
|
39
55
|
|
40
56
|
attr_reader :user_attributes
|
@@ -95,7 +111,35 @@ module Optimizely
|
|
95
111
|
return nil
|
96
112
|
end
|
97
113
|
|
98
|
-
|
114
|
+
begin
|
115
|
+
send(EVALUATORS_BY_MATCH_TYPE[condition_match], leaf_condition)
|
116
|
+
rescue InvalidAttributeType
|
117
|
+
condition_name = leaf_condition['name']
|
118
|
+
user_value = @user_attributes[condition_name]
|
119
|
+
|
120
|
+
@logger.log(
|
121
|
+
Logger::WARN,
|
122
|
+
format(
|
123
|
+
Helpers::Constants::AUDIENCE_EVALUATION_LOGS['UNEXPECTED_TYPE'],
|
124
|
+
leaf_condition,
|
125
|
+
user_value.class,
|
126
|
+
condition_name
|
127
|
+
)
|
128
|
+
)
|
129
|
+
return nil
|
130
|
+
rescue InvalidSemanticVersion
|
131
|
+
condition_name = leaf_condition['name']
|
132
|
+
|
133
|
+
@logger.log(
|
134
|
+
Logger::WARN,
|
135
|
+
format(
|
136
|
+
Helpers::Constants::AUDIENCE_EVALUATION_LOGS['INVALID_SEMANTIC_VERSION'],
|
137
|
+
leaf_condition,
|
138
|
+
condition_name
|
139
|
+
)
|
140
|
+
)
|
141
|
+
return nil
|
142
|
+
end
|
99
143
|
end
|
100
144
|
|
101
145
|
def exact_evaluator(condition)
|
@@ -122,16 +166,7 @@ module Optimizely
|
|
122
166
|
|
123
167
|
if !value_type_valid_for_exact_conditions?(user_provided_value) ||
|
124
168
|
!Helpers::Validator.same_types?(condition_value, user_provided_value)
|
125
|
-
|
126
|
-
Logger::WARN,
|
127
|
-
format(
|
128
|
-
Helpers::Constants::AUDIENCE_EVALUATION_LOGS['UNEXPECTED_TYPE'],
|
129
|
-
condition,
|
130
|
-
user_provided_value.class,
|
131
|
-
condition['name']
|
132
|
-
)
|
133
|
-
)
|
134
|
-
return nil
|
169
|
+
raise InvalidAttributeType
|
135
170
|
end
|
136
171
|
|
137
172
|
if user_provided_value.is_a?(Numeric) && !Helpers::Validator.finite_number?(user_provided_value)
|
@@ -173,6 +208,20 @@ module Optimizely
|
|
173
208
|
user_provided_value > condition_value
|
174
209
|
end
|
175
210
|
|
211
|
+
def greater_than_or_equal_evaluator(condition)
|
212
|
+
# Evaluate the given greater than or equal match condition for the given user attributes.
|
213
|
+
# Returns boolean true if the user attribute value is greater than or equal to the condition value,
|
214
|
+
# false if the user attribute value is less than the condition value,
|
215
|
+
# nil if the condition value isn't a number or the user attribute value isn't a number.
|
216
|
+
|
217
|
+
condition_value = condition['value']
|
218
|
+
user_provided_value = @user_attributes[condition['name']]
|
219
|
+
|
220
|
+
return nil unless valid_numeric_values?(user_provided_value, condition_value, condition)
|
221
|
+
|
222
|
+
user_provided_value >= condition_value
|
223
|
+
end
|
224
|
+
|
176
225
|
def less_than_evaluator(condition)
|
177
226
|
# Evaluate the given less than match condition for the given user attributes.
|
178
227
|
# Returns boolean true if the user attribute value is less than the condition value,
|
@@ -187,6 +236,20 @@ module Optimizely
|
|
187
236
|
user_provided_value < condition_value
|
188
237
|
end
|
189
238
|
|
239
|
+
def less_than_or_equal_evaluator(condition)
|
240
|
+
# Evaluate the given less than or equal match condition for the given user attributes.
|
241
|
+
# Returns boolean true if the user attribute value is less than or equal to the condition value,
|
242
|
+
# false if the user attribute value is greater than the condition value,
|
243
|
+
# nil if the condition value isn't a number or the user attribute value isn't a number.
|
244
|
+
|
245
|
+
condition_value = condition['value']
|
246
|
+
user_provided_value = @user_attributes[condition['name']]
|
247
|
+
|
248
|
+
return nil unless valid_numeric_values?(user_provided_value, condition_value, condition)
|
249
|
+
|
250
|
+
user_provided_value <= condition_value
|
251
|
+
end
|
252
|
+
|
190
253
|
def substring_evaluator(condition)
|
191
254
|
# Evaluate the given substring match condition for the given user attributes.
|
192
255
|
# Returns boolean true if the condition value is a substring of the user attribute value,
|
@@ -204,22 +267,66 @@ module Optimizely
|
|
204
267
|
return nil
|
205
268
|
end
|
206
269
|
|
207
|
-
unless user_provided_value.is_a?(String)
|
208
|
-
@logger.log(
|
209
|
-
Logger::WARN,
|
210
|
-
format(
|
211
|
-
Helpers::Constants::AUDIENCE_EVALUATION_LOGS['UNEXPECTED_TYPE'],
|
212
|
-
condition,
|
213
|
-
user_provided_value.class,
|
214
|
-
condition['name']
|
215
|
-
)
|
216
|
-
)
|
217
|
-
return nil
|
218
|
-
end
|
270
|
+
raise InvalidAttributeType unless user_provided_value.is_a?(String)
|
219
271
|
|
220
272
|
user_provided_value.include? condition_value
|
221
273
|
end
|
222
274
|
|
275
|
+
def semver_equal_evaluator(condition)
|
276
|
+
# Evaluate the given semantic version equal match target version for the user version.
|
277
|
+
# Returns boolean true if the user version is equal to the target version,
|
278
|
+
# false if the user version is not equal to the target version
|
279
|
+
|
280
|
+
target_version = condition['value']
|
281
|
+
user_version = @user_attributes[condition['name']]
|
282
|
+
|
283
|
+
SemanticVersion.compare_user_version_with_target_version(target_version, user_version).zero?
|
284
|
+
end
|
285
|
+
|
286
|
+
def semver_greater_than_evaluator(condition)
|
287
|
+
# Evaluate the given semantic version greater than match target version for the user version.
|
288
|
+
# Returns boolean true if the user version is greater than the target version,
|
289
|
+
# false if the user version is less than or equal to the target version
|
290
|
+
|
291
|
+
target_version = condition['value']
|
292
|
+
user_version = @user_attributes[condition['name']]
|
293
|
+
|
294
|
+
SemanticVersion.compare_user_version_with_target_version(target_version, user_version).positive?
|
295
|
+
end
|
296
|
+
|
297
|
+
def semver_greater_than_or_equal_evaluator(condition)
|
298
|
+
# Evaluate the given semantic version greater than or equal to match target version for the user version.
|
299
|
+
# Returns boolean true if the user version is greater than or equal to the target version,
|
300
|
+
# false if the user version is less than the target version
|
301
|
+
|
302
|
+
target_version = condition['value']
|
303
|
+
user_version = @user_attributes[condition['name']]
|
304
|
+
|
305
|
+
SemanticVersion.compare_user_version_with_target_version(target_version, user_version) >= 0
|
306
|
+
end
|
307
|
+
|
308
|
+
def semver_less_than_evaluator(condition)
|
309
|
+
# Evaluate the given semantic version less than match target version for the user version.
|
310
|
+
# Returns boolean true if the user version is less than the target version,
|
311
|
+
# false if the user version is greater than or equal to the target version
|
312
|
+
|
313
|
+
target_version = condition['value']
|
314
|
+
user_version = @user_attributes[condition['name']]
|
315
|
+
|
316
|
+
SemanticVersion.compare_user_version_with_target_version(target_version, user_version).negative?
|
317
|
+
end
|
318
|
+
|
319
|
+
def semver_less_than_or_equal_evaluator(condition)
|
320
|
+
# Evaluate the given semantic version less than or equal to match target version for the user version.
|
321
|
+
# Returns boolean true if the user version is less than or equal to the target version,
|
322
|
+
# false if the user version is greater than the target version
|
323
|
+
|
324
|
+
target_version = condition['value']
|
325
|
+
user_version = @user_attributes[condition['name']]
|
326
|
+
|
327
|
+
SemanticVersion.compare_user_version_with_target_version(target_version, user_version) <= 0
|
328
|
+
end
|
329
|
+
|
223
330
|
private
|
224
331
|
|
225
332
|
def valid_numeric_values?(user_value, condition_value, condition)
|
@@ -234,18 +341,7 @@ module Optimizely
|
|
234
341
|
return false
|
235
342
|
end
|
236
343
|
|
237
|
-
unless user_value.is_a?(Numeric)
|
238
|
-
@logger.log(
|
239
|
-
Logger::WARN,
|
240
|
-
format(
|
241
|
-
Helpers::Constants::AUDIENCE_EVALUATION_LOGS['UNEXPECTED_TYPE'],
|
242
|
-
condition,
|
243
|
-
user_value.class,
|
244
|
-
condition['name']
|
245
|
-
)
|
246
|
-
)
|
247
|
-
return false
|
248
|
-
end
|
344
|
+
raise InvalidAttributeType unless user_value.is_a?(Numeric)
|
249
345
|
|
250
346
|
unless Helpers::Validator.finite_number?(user_value)
|
251
347
|
@logger.log(
|