optimizely-sdk 2.1.1 → 3.0.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
  SHA1:
3
- metadata.gz: 0fde3ad17097c2d63569bade1f3e4095bd8b8d36
4
- data.tar.gz: ac971b0cff7db95a591b742a19cc9e9b82e6016d
3
+ metadata.gz: a6269e81defa38267703852ceb2e5d29de7ddad0
4
+ data.tar.gz: 2bd5d9f48a01e25dea810ca7b42bfc1c63c2bf40
5
5
  SHA512:
6
- metadata.gz: d6cfdff4bef0344791fea78458ef57d7aa81e7de2fdaf3eb843e8ae6196f72766f0257bdef37a00feeae802390fd6016e8db96e2b90d5484fbd0d2317a94bdb4
7
- data.tar.gz: f721214b5c3e6a71fb1ef21609d5180e232720ad201fca0c31b2baf62c925a76aea13c4c75ea9bdb312f2bb1ec555f93b6067e31fe931df4843b95db230c6655
6
+ metadata.gz: a81f892eef13d1f9c28a17daf44dc320a6c075e43b24f89e70b7f5d58ef91a2db35013e86b5de2b9be90360dcf53fd8757e7c54652f30b5ded0c71e2fe2bb95d
7
+ data.tar.gz: 77570a8ef7bb7dcfcbed470742686603cb361dc8fb593d737525e6b93e330084833d4c3b277c535a3c93131d1e10cf18296db70e9bb1246e915ead1456b78af9
data/lib/optimizely.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2016-2018, Optimizely and contributors
4
+ # Copyright 2016-2019, 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.
@@ -201,24 +201,14 @@ module Optimizely
201
201
 
202
202
  return nil unless user_inputs_valid?(attributes, event_tags)
203
203
 
204
- experiment_ids = @config.get_experiment_ids_for_event(event_key)
205
- if experiment_ids.empty?
206
- @config.logger.log(Logger::INFO, "Not tracking user '#{user_id}'.")
204
+ event = @config.get_event_from_key(event_key)
205
+ unless event
206
+ @config.logger.log(Logger::INFO, "Not tracking user '#{user_id}' for event '#{event_key}'.")
207
207
  return nil
208
208
  end
209
209
 
210
- # Filter out experiments that are not running or that do not include the user in audience conditions
211
-
212
- experiment_variation_map = get_valid_experiments_for_event(event_key, user_id, attributes)
213
-
214
- # Don't track events without valid experiments attached
215
- if experiment_variation_map.empty?
216
- @logger.log(Logger::INFO, "There are no valid experiments for event '#{event_key}' to track.")
217
- return nil
218
- end
219
-
220
- conversion_event = @event_builder.create_conversion_event(event_key, user_id, attributes,
221
- event_tags, experiment_variation_map)
210
+ conversion_event = @event_builder.create_conversion_event(event, user_id, attributes, event_tags)
211
+ @config.logger.log(Logger::INFO, "Tracking event '#{event_key}' for user '#{user_id}'.")
222
212
  @logger.log(Logger::INFO,
223
213
  "Dispatching conversion event to URL #{conversion_event.url} with params #{conversion_event.params}.")
224
214
  begin
@@ -258,6 +248,8 @@ module Optimizely
258
248
  }, @logger, Logger::ERROR
259
249
  )
260
250
 
251
+ return false unless user_inputs_valid?(attributes)
252
+
261
253
  feature_flag = @config.get_feature_flag_from_key(feature_flag_key)
262
254
  unless feature_flag
263
255
  @logger.log(Logger::ERROR, "No feature flag was found for key '#{feature_flag_key}'.")
@@ -305,7 +297,13 @@ module Optimizely
305
297
  return enabled_features
306
298
  end
307
299
 
308
- return enabled_features unless Optimizely::Helpers::Validator.inputs_valid?({user_id: user_id}, @logger, Logger::ERROR)
300
+ return enabled_features unless Optimizely::Helpers::Validator.inputs_valid?(
301
+ {
302
+ user_id: user_id
303
+ }, @logger, Logger::ERROR
304
+ )
305
+
306
+ return enabled_features unless user_inputs_valid?(attributes)
309
307
 
310
308
  @config.feature_flags.each do |feature|
311
309
  enabled_features.push(feature['key']) if is_feature_enabled(
@@ -444,12 +442,14 @@ module Optimizely
444
442
  {
445
443
  feature_flag_key: feature_flag_key,
446
444
  variable_key: variable_key,
447
- variable_type: variable_type,
448
- user_id: user_id
445
+ user_id: user_id,
446
+ variable_type: variable_type
449
447
  },
450
448
  @logger, Logger::ERROR
451
449
  )
452
450
 
451
+ return nil unless user_inputs_valid?(attributes)
452
+
453
453
  feature_flag = @config.get_feature_flag_from_key(feature_flag_key)
454
454
  unless feature_flag
455
455
  @logger.log(Logger::INFO, "No feature flag was found for key '#{feature_flag_key}'.")
@@ -492,34 +492,6 @@ module Optimizely
492
492
  variable_value
493
493
  end
494
494
 
495
- def get_valid_experiments_for_event(event_key, user_id, attributes)
496
- # Get the experiments that we should be tracking for the given event.
497
- #
498
- # event_key - Event key representing the event which needs to be recorded.
499
- # user_id - String ID for user.
500
- # attributes - Map of attributes of the user.
501
- #
502
- # Returns Map where each object contains the ID of the experiment to track and the ID of the variation the user
503
- # is bucketed into.
504
-
505
- valid_experiments = {}
506
- experiment_ids = @config.get_experiment_ids_for_event(event_key)
507
- experiment_ids.each do |experiment_id|
508
- experiment_key = @config.get_experiment_key(experiment_id)
509
- variation_key = get_variation(experiment_key, user_id, attributes)
510
-
511
- if variation_key.nil?
512
- @logger.log(Logger::INFO, "Not tracking user '#{user_id}' for experiment '#{experiment_key}'.")
513
- next
514
- end
515
-
516
- variation_id = @config.get_variation_id_from_key(experiment_key, variation_key)
517
- valid_experiments[experiment_id] = variation_id
518
- end
519
-
520
- valid_experiments
521
- end
522
-
523
495
  def user_inputs_valid?(attributes = nil, event_tags = nil)
524
496
  # Helper method to validate user inputs.
525
497
  #
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2016-2017, Optimizely and contributors
4
+ # Copyright 2016-2017, 2019, 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.
@@ -16,7 +16,9 @@
16
16
  # limitations under the License.
17
17
  #
18
18
  require 'json'
19
- require_relative './condition'
19
+ require_relative './custom_attribute_condition_evaluator'
20
+ require_relative 'condition_tree_evaluator'
21
+ require_relative 'helpers/constants'
20
22
 
21
23
  module Optimizely
22
24
  module Audience
@@ -30,26 +32,78 @@ module Optimizely
30
32
  # attributes - Hash representing user attributes which will be used in determining if
31
33
  # the audience conditions are met.
32
34
  #
33
- # Returns boolean representing if user satisfies audience conditions for any of the audiences or not.
35
+ # Returns boolean representing if user satisfies audience conditions for the audiences or not.
34
36
 
35
- audience_ids = experiment['audienceIds']
37
+ audience_conditions = experiment['audienceConditions'] || experiment['audienceIds']
38
+
39
+ config.logger.log(
40
+ Logger::DEBUG,
41
+ format(
42
+ Helpers::Constants::AUDIENCE_EVALUATION_LOGS['EVALUATING_AUDIENCES_COMBINED'],
43
+ experiment['key'],
44
+ audience_conditions
45
+ )
46
+ )
36
47
 
37
48
  # Return true if there are no audiences
38
- return true if audience_ids.empty?
49
+ if audience_conditions.empty?
50
+ config.logger.log(
51
+ Logger::INFO,
52
+ format(
53
+ Helpers::Constants::AUDIENCE_EVALUATION_LOGS['AUDIENCE_EVALUATION_RESULT_COMBINED'],
54
+ experiment['key'],
55
+ 'TRUE'
56
+ )
57
+ )
58
+ return true
59
+ end
60
+
61
+ attributes ||= {}
39
62
 
40
- # Return false if there are audiences but no attributes
41
- return false unless attributes
63
+ custom_attr_condition_evaluator = CustomAttributeConditionEvaluator.new(attributes, config.logger)
64
+
65
+ evaluate_custom_attr = lambda do |condition|
66
+ return custom_attr_condition_evaluator.evaluate(condition)
67
+ end
42
68
 
43
- # Return true if any one of the audience conditions are met
44
- @condition_evaluator = ConditionEvaluator.new(attributes)
45
- audience_ids.each do |audience_id|
69
+ evaluate_audience = lambda do |audience_id|
46
70
  audience = config.get_audience_from_id(audience_id)
71
+ return nil unless audience
72
+
47
73
  audience_conditions = audience['conditions']
48
- audience_conditions = JSON.parse(audience_conditions)
49
- return true if @condition_evaluator.evaluate(audience_conditions)
74
+ config.logger.log(
75
+ Logger::DEBUG,
76
+ format(
77
+ Helpers::Constants::AUDIENCE_EVALUATION_LOGS['EVALUATING_AUDIENCE'],
78
+ audience_id,
79
+ audience_conditions
80
+ )
81
+ )
82
+
83
+ audience_conditions = JSON.parse(audience_conditions) if audience_conditions.is_a?(String)
84
+ result = ConditionTreeEvaluator.evaluate(audience_conditions, evaluate_custom_attr)
85
+ result_str = result.nil? ? 'UNKNOWN' : result.to_s.upcase
86
+ config.logger.log(
87
+ Logger::INFO,
88
+ format(Helpers::Constants::AUDIENCE_EVALUATION_LOGS['AUDIENCE_EVALUATION_RESULT'], audience_id, result_str)
89
+ )
90
+ result
50
91
  end
51
92
 
52
- false
93
+ eval_result = ConditionTreeEvaluator.evaluate(audience_conditions, evaluate_audience)
94
+
95
+ eval_result ||= false
96
+
97
+ config.logger.log(
98
+ Logger::INFO,
99
+ format(
100
+ Helpers::Constants::AUDIENCE_EVALUATION_LOGS['AUDIENCE_EVALUATION_RESULT_COMBINED'],
101
+ experiment['key'],
102
+ eval_result.to_s.upcase
103
+ )
104
+ )
105
+
106
+ eval_result
53
107
  end
54
108
  end
55
109
  end
@@ -93,7 +93,10 @@ module Optimizely
93
93
 
94
94
  # Handle the case when the traffic range is empty due to sticky bucketing
95
95
  if variation_id == ''
96
- @config.logger.log(Logger::DEBUG, 'Bucketed into an empty traffic range. Returning nil.')
96
+ @config.logger.log(
97
+ Logger::DEBUG,
98
+ 'Bucketed into an empty traffic range. Returning nil.'
99
+ )
97
100
  end
98
101
 
99
102
  @config.logger.log(Logger::INFO, "User '#{user_id}' is in no variation.")
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2019, Optimizely and contributors
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ module Optimizely
19
+ module ConditionTreeEvaluator
20
+ # Operator types
21
+ AND_CONDITION = 'and'
22
+ OR_CONDITION = 'or'
23
+ NOT_CONDITION = 'not'
24
+
25
+ EVALUATORS_BY_OPERATOR_TYPE = {
26
+ AND_CONDITION => :and_evaluator,
27
+ OR_CONDITION => :or_evaluator,
28
+ NOT_CONDITION => :not_evaluator
29
+ }.freeze
30
+
31
+ module_function
32
+
33
+ def evaluate(conditions, leaf_evaluator)
34
+ # Top level method to evaluate audience conditions.
35
+ #
36
+ # conditions - Nested array of and/or conditions.
37
+ # Example: ['and', operand_1, ['or', operand_2, operand_3]]
38
+ #
39
+ # leaf_evaluator - Function which will be called to evaluate leaf condition values.
40
+ #
41
+ # Returns boolean if the given user attributes match/don't match the given conditions,
42
+ # nil if the given conditions are invalid or can't be evaluated.
43
+
44
+ if conditions.is_a? Array
45
+ first_operator = conditions[0]
46
+ rest_of_conditions = conditions[1..-1]
47
+
48
+ # Operator to apply is not explicit - assume 'or'
49
+ unless EVALUATORS_BY_OPERATOR_TYPE.include?(conditions[0])
50
+ first_operator = OR_CONDITION
51
+ rest_of_conditions = conditions
52
+ end
53
+
54
+ return send(EVALUATORS_BY_OPERATOR_TYPE[first_operator], rest_of_conditions, leaf_evaluator)
55
+ end
56
+
57
+ leaf_evaluator.call(conditions)
58
+ end
59
+
60
+ def and_evaluator(conditions, leaf_evaluator)
61
+ # Evaluates an array of conditions as if the evaluator had been applied
62
+ # to each entry and the results AND-ed together.
63
+ #
64
+ # conditions - Array of conditions ex: [operand_1, operand_2]
65
+ #
66
+ # leaf_evaluator - Function which will be called to evaluate leaf condition values.
67
+ #
68
+ # Returns boolean if the user attributes match/don't match the given conditions,
69
+ # nil if the user attributes and conditions can't be evaluated.
70
+
71
+ found_nil = false
72
+ conditions.each do |condition|
73
+ result = evaluate(condition, leaf_evaluator)
74
+ return result if result == false
75
+
76
+ found_nil = true if result.nil?
77
+ end
78
+
79
+ found_nil ? nil : true
80
+ end
81
+
82
+ def not_evaluator(single_condition, leaf_evaluator)
83
+ # Evaluates an array of conditions as if the evaluator had been applied
84
+ # to a single entry and NOT was applied to the result.
85
+ #
86
+ # single_condition - Array of a single condition ex: [operand_1]
87
+ #
88
+ # leaf_evaluator - Function which will be called to evaluate leaf condition values.
89
+ #
90
+ # Returns boolean if the user attributes match/don't match the given conditions,
91
+ # nil if the user attributes and conditions can't be evaluated.
92
+
93
+ return nil if single_condition.empty?
94
+
95
+ result = evaluate(single_condition[0], leaf_evaluator)
96
+ result.nil? ? nil : !result
97
+ end
98
+
99
+ def or_evaluator(conditions, leaf_evaluator)
100
+ # Evaluates an array of conditions as if the evaluator had been applied
101
+ # to each entry and the results OR-ed together.
102
+ #
103
+ # conditions - Array of conditions ex: [operand_1, operand_2]
104
+ #
105
+ # leaf_evaluator - Function which will be called to evaluate leaf condition values.
106
+ #
107
+ # Returns boolean if the user attributes match/don't match the given conditions,
108
+ # nil if the user attributes and conditions can't be evaluated.
109
+
110
+ found_nil = false
111
+ conditions.each do |condition|
112
+ result = evaluate(condition, leaf_evaluator)
113
+ return result if result == true
114
+
115
+ found_nil = true if result.nil?
116
+ end
117
+
118
+ found_nil ? nil : false
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,273 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2019, Optimizely and contributors
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ require_relative 'helpers/constants'
19
+ require_relative 'helpers/validator'
20
+
21
+ module Optimizely
22
+ class CustomAttributeConditionEvaluator
23
+ CUSTOM_ATTRIBUTE_CONDITION_TYPE = 'custom_attribute'
24
+
25
+ # Conditional match types
26
+ EXACT_MATCH_TYPE = 'exact'
27
+ EXISTS_MATCH_TYPE = 'exists'
28
+ GREATER_THAN_MATCH_TYPE = 'gt'
29
+ LESS_THAN_MATCH_TYPE = 'lt'
30
+ SUBSTRING_MATCH_TYPE = 'substring'
31
+
32
+ EVALUATORS_BY_MATCH_TYPE = {
33
+ EXACT_MATCH_TYPE => :exact_evaluator,
34
+ EXISTS_MATCH_TYPE => :exists_evaluator,
35
+ GREATER_THAN_MATCH_TYPE => :greater_than_evaluator,
36
+ LESS_THAN_MATCH_TYPE => :less_than_evaluator,
37
+ SUBSTRING_MATCH_TYPE => :substring_evaluator
38
+ }.freeze
39
+
40
+ attr_reader :user_attributes
41
+
42
+ def initialize(user_attributes, logger)
43
+ @user_attributes = user_attributes
44
+ @logger = logger
45
+ end
46
+
47
+ def evaluate(leaf_condition)
48
+ # Top level method to evaluate audience conditions.
49
+ #
50
+ # conditions - Nested array of and/or conditions.
51
+ # Example: ['and', operand_1, ['or', operand_2, operand_3]]
52
+ #
53
+ # Returns boolean if the given user attributes match/don't match the given conditions,
54
+ # nil if the given conditions can't be evaluated.
55
+
56
+ unless leaf_condition['type'] == CUSTOM_ATTRIBUTE_CONDITION_TYPE
57
+ @logger.log(
58
+ Logger::WARN,
59
+ format(Helpers::Constants::AUDIENCE_EVALUATION_LOGS['UNKNOWN_CONDITION_TYPE'], leaf_condition)
60
+ )
61
+ return nil
62
+ end
63
+
64
+ condition_match = leaf_condition['match'] || EXACT_MATCH_TYPE
65
+
66
+ if !@user_attributes.key?(leaf_condition['name']) && condition_match != EXISTS_MATCH_TYPE
67
+ @logger.log(
68
+ Logger::DEBUG,
69
+ format(
70
+ Helpers::Constants::AUDIENCE_EVALUATION_LOGS['MISSING_ATTRIBUTE_VALUE'],
71
+ leaf_condition,
72
+ leaf_condition['name']
73
+ )
74
+ )
75
+ return nil
76
+ end
77
+
78
+ if @user_attributes[leaf_condition['name']].nil? && condition_match != EXISTS_MATCH_TYPE
79
+ @logger.log(
80
+ Logger::DEBUG,
81
+ format(
82
+ Helpers::Constants::AUDIENCE_EVALUATION_LOGS['NULL_ATTRIBUTE_VALUE'],
83
+ leaf_condition,
84
+ leaf_condition['name']
85
+ )
86
+ )
87
+ return nil
88
+ end
89
+
90
+ unless EVALUATORS_BY_MATCH_TYPE.include?(condition_match)
91
+ @logger.log(
92
+ Logger::WARN,
93
+ format(Helpers::Constants::AUDIENCE_EVALUATION_LOGS['UNKNOWN_MATCH_TYPE'], leaf_condition)
94
+ )
95
+ return nil
96
+ end
97
+
98
+ send(EVALUATORS_BY_MATCH_TYPE[condition_match], leaf_condition)
99
+ end
100
+
101
+ def exact_evaluator(condition)
102
+ # Evaluate the given exact match condition for the given user attributes.
103
+ #
104
+ # Returns boolean true if numbers values matched, i.e 2 is equal to 2.0
105
+ # true if the user attribute value is equal (===) to the condition value,
106
+ # false if the user attribute value is not equal (!==) to the condition value,
107
+ # nil if the condition value or user attribute value has an invalid type,
108
+ # or if there is a mismatch between the user attribute type and the condition value type.
109
+
110
+ condition_value = condition['value']
111
+
112
+ user_provided_value = @user_attributes[condition['name']]
113
+
114
+ if !value_type_valid_for_exact_conditions?(condition_value) ||
115
+ (condition_value.is_a?(Numeric) && !Helpers::Validator.finite_number?(condition_value))
116
+ @logger.log(
117
+ Logger::WARN,
118
+ format(Helpers::Constants::AUDIENCE_EVALUATION_LOGS['UNKNOWN_CONDITION_VALUE'], condition)
119
+ )
120
+ return nil
121
+ end
122
+
123
+ if !value_type_valid_for_exact_conditions?(user_provided_value) ||
124
+ !Helpers::Validator.same_types?(condition_value, user_provided_value)
125
+ @logger.log(
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
135
+ end
136
+
137
+ if user_provided_value.is_a?(Numeric) && !Helpers::Validator.finite_number?(user_provided_value)
138
+ @logger.log(
139
+ Logger::WARN,
140
+ format(
141
+ Helpers::Constants::AUDIENCE_EVALUATION_LOGS['INFINITE_ATTRIBUTE_VALUE'],
142
+ condition,
143
+ condition['name']
144
+ )
145
+ )
146
+ return nil
147
+ end
148
+
149
+ condition_value == user_provided_value
150
+ end
151
+
152
+ def exists_evaluator(condition)
153
+ # Evaluate the given exists match condition for the given user attributes.
154
+ # Returns boolean true if both:
155
+ # 1) the user attributes have a value for the given condition, and
156
+ # 2) the user attribute value is neither nil nor undefined
157
+ # Returns false otherwise
158
+
159
+ !@user_attributes[condition['name']].nil?
160
+ end
161
+
162
+ def greater_than_evaluator(condition)
163
+ # Evaluate the given greater than match condition for the given user attributes.
164
+ # Returns boolean true if the user attribute value is greater than the condition value,
165
+ # false if the user attribute value is less than or equal to the condition value,
166
+ # nil if the condition value isn't a number or the user attribute value isn't a number.
167
+
168
+ condition_value = condition['value']
169
+ user_provided_value = @user_attributes[condition['name']]
170
+
171
+ return nil unless valid_numeric_values?(user_provided_value, condition_value, condition)
172
+
173
+ user_provided_value > condition_value
174
+ end
175
+
176
+ def less_than_evaluator(condition)
177
+ # Evaluate the given less than match condition for the given user attributes.
178
+ # Returns boolean true if the user attribute value is less than the condition value,
179
+ # false if the user attribute value is greater than or equal to the condition value,
180
+ # nil if the condition value isn't a number or the user attribute value isn't a number.
181
+
182
+ condition_value = condition['value']
183
+ user_provided_value = @user_attributes[condition['name']]
184
+
185
+ return nil unless valid_numeric_values?(user_provided_value, condition_value, condition)
186
+
187
+ user_provided_value < condition_value
188
+ end
189
+
190
+ def substring_evaluator(condition)
191
+ # Evaluate the given substring match condition for the given user attributes.
192
+ # Returns boolean true if the condition value is a substring of the user attribute value,
193
+ # false if the condition value is not a substring of the user attribute value,
194
+ # nil if the condition value isn't a string or the user attribute value isn't a string.
195
+
196
+ condition_value = condition['value']
197
+ user_provided_value = @user_attributes[condition['name']]
198
+
199
+ unless condition_value.is_a?(String)
200
+ @logger.log(
201
+ Logger::WARN,
202
+ format(Helpers::Constants::AUDIENCE_EVALUATION_LOGS['UNKNOWN_CONDITION_VALUE'], condition)
203
+ )
204
+ return nil
205
+ end
206
+
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
219
+
220
+ user_provided_value.include? condition_value
221
+ end
222
+
223
+ private
224
+
225
+ def valid_numeric_values?(user_value, condition_value, condition)
226
+ # Returns true if user and condition values are valid numeric.
227
+ # false otherwise.
228
+
229
+ unless Helpers::Validator.finite_number?(condition_value)
230
+ @logger.log(
231
+ Logger::WARN,
232
+ format(Helpers::Constants::AUDIENCE_EVALUATION_LOGS['UNKNOWN_CONDITION_VALUE'], condition)
233
+ )
234
+ return false
235
+ end
236
+
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
249
+
250
+ unless Helpers::Validator.finite_number?(user_value)
251
+ @logger.log(
252
+ Logger::WARN,
253
+ format(
254
+ Helpers::Constants::AUDIENCE_EVALUATION_LOGS['INFINITE_ATTRIBUTE_VALUE'],
255
+ condition,
256
+ condition['name']
257
+ )
258
+ )
259
+ return false
260
+ end
261
+
262
+ true
263
+ end
264
+
265
+ def value_type_valid_for_exact_conditions?(value)
266
+ # Returns true if the value is valid for exact conditions. Valid values include
267
+ # strings or booleans or is a number.
268
+ # false otherwise.
269
+
270
+ (Helpers::Validator.boolean? value) || (value.is_a? String) || value.is_a?(Numeric)
271
+ end
272
+ end
273
+ end