optimizely-sdk 2.1.1 → 3.0.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 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