optimizely-sdk 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/optimizely.rb +179 -0
- data/lib/optimizely/audience.rb +37 -0
- data/lib/optimizely/bucketer.rb +143 -0
- data/lib/optimizely/condition.rb +118 -0
- data/lib/optimizely/error_handler.rb +24 -0
- data/lib/optimizely/event_builder.rb +200 -0
- data/lib/optimizely/event_dispatcher.rb +18 -0
- data/lib/optimizely/exceptions.rb +83 -0
- data/lib/optimizely/helpers/constants.rb +173 -0
- data/lib/optimizely/helpers/group.rb +14 -0
- data/lib/optimizely/helpers/validator.rb +60 -0
- data/lib/optimizely/logger.rb +30 -0
- data/lib/optimizely/params.rb +14 -0
- data/lib/optimizely/project_config.rb +255 -0
- data/lib/optimizely/version.rb +3 -0
- data/lib/start.rb +6 -0
- metadata +161 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 901e065c0246efcd95969e7b04b84dc9aab2d9e7
|
4
|
+
data.tar.gz: c7a3e774508632a1161d827178e49ffa039d560f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4ff15bc14a26529b3332e41b772befc96559e0d9230d3d41c7c198f757f2a7aede6ec5030064421a9cb87ca676f76d997310ac8211af9fc36554713eb262413c
|
7
|
+
data.tar.gz: 9b6ccd16b467ee4d4f2092e775ef73218fa6ac44f70db80f670884c74aebae09f9623353431806d0f2620e08d4471f6d47cf522d1a32274ac046e57286668819
|
data/lib/optimizely.rb
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
require_relative 'optimizely/audience'
|
2
|
+
require_relative 'optimizely/bucketer'
|
3
|
+
require_relative 'optimizely/error_handler'
|
4
|
+
require_relative 'optimizely/event_builder'
|
5
|
+
require_relative 'optimizely/event_dispatcher'
|
6
|
+
require_relative 'optimizely/exceptions'
|
7
|
+
require_relative 'optimizely/helpers/group'
|
8
|
+
require_relative 'optimizely/helpers/validator'
|
9
|
+
require_relative 'optimizely/logger'
|
10
|
+
require_relative 'optimizely/project_config'
|
11
|
+
|
12
|
+
module Optimizely
|
13
|
+
class Project
|
14
|
+
attr_accessor :config
|
15
|
+
attr_accessor :bucketer
|
16
|
+
attr_accessor :event_builder
|
17
|
+
attr_accessor :event_dispatcher
|
18
|
+
attr_accessor :logger
|
19
|
+
attr_accessor :error_handler
|
20
|
+
|
21
|
+
def initialize(datafile, event_dispatcher = nil, logger = nil, error_handler = nil)
|
22
|
+
# Constructor for Projects.
|
23
|
+
#
|
24
|
+
# datafile - JSON string representing the project.
|
25
|
+
# event_dispatcher - Provides a dispatch_event method which if given a URL and params sends a request to it.
|
26
|
+
# logger - Optional param which provides a log method to log messages. By default nothing would be logged.
|
27
|
+
# error_handler - Optional param which provides a handle_error method to handle exceptions.
|
28
|
+
# By default all exceptions will be suppressed.
|
29
|
+
|
30
|
+
@logger = logger || NoOpLogger.new
|
31
|
+
@error_handler = error_handler || NoOpErrorHandler.new
|
32
|
+
@event_dispatcher = event_dispatcher || EventDispatcher.new
|
33
|
+
validate_inputs(datafile)
|
34
|
+
|
35
|
+
@config = ProjectConfig.new(datafile, @logger, @error_handler)
|
36
|
+
@bucketer = Bucketer.new(@config)
|
37
|
+
@event_builder = EventBuilder.new(@config, @bucketer)
|
38
|
+
end
|
39
|
+
|
40
|
+
def activate(experiment_key, user_id, attributes = nil)
|
41
|
+
# Buckets visitor and sends impression event to Optimizely.
|
42
|
+
#
|
43
|
+
# experiment_key - Experiment which needs to be activated.
|
44
|
+
# user_id - String ID for user.
|
45
|
+
# attributes - Hash representing user attributes and values to be recorded.
|
46
|
+
#
|
47
|
+
# Returns variation key representing the variation the user will be bucketed in.
|
48
|
+
# Returns nil if experiment is not Running or if user is not in experiment.
|
49
|
+
|
50
|
+
if attributes && !attributes_valid?(attributes)
|
51
|
+
@logger.log(Logger::INFO, "Not activating user '#{user_id}'.")
|
52
|
+
return nil
|
53
|
+
end
|
54
|
+
|
55
|
+
unless preconditions_valid?(experiment_key, user_id, attributes)
|
56
|
+
@logger.log(Logger::INFO, "Not activating user '#{user_id}'.")
|
57
|
+
return nil
|
58
|
+
end
|
59
|
+
|
60
|
+
variation_id = @bucketer.bucket(experiment_key, user_id)
|
61
|
+
|
62
|
+
if not variation_id
|
63
|
+
@logger.log(Logger::INFO, "Not activating user '#{user_id}'.")
|
64
|
+
return nil
|
65
|
+
end
|
66
|
+
|
67
|
+
# Create and dispatch impression event
|
68
|
+
impression_event = @event_builder.create_impression_event(experiment_key, variation_id, user_id, attributes)
|
69
|
+
@logger.log(Logger::INFO,
|
70
|
+
'Dispatching impression event to URL %s with params %s.' % [impression_event.url,
|
71
|
+
impression_event.params])
|
72
|
+
@event_dispatcher.dispatch_event(impression_event.url, impression_event.params)
|
73
|
+
|
74
|
+
@config.get_variation_key_from_id(experiment_key, variation_id)
|
75
|
+
end
|
76
|
+
|
77
|
+
def get_variation(experiment_key, user_id, attributes = nil)
|
78
|
+
# Gets variation where visitor will be bucketed.
|
79
|
+
#
|
80
|
+
# experiment_key - Experiment for which visitor variation needs to be determined.
|
81
|
+
# user_id - String ID for user.
|
82
|
+
# attributes - Hash representing user attributes.
|
83
|
+
#
|
84
|
+
# Returns variation key where visitor will be bucketed.
|
85
|
+
# Returns nil if experiment is not Running or if user is not in experiment.
|
86
|
+
|
87
|
+
if attributes && !attributes_valid?(attributes)
|
88
|
+
@logger.log(Logger::INFO, "Not activating user '#{user_id}.")
|
89
|
+
return nil
|
90
|
+
end
|
91
|
+
|
92
|
+
unless preconditions_valid?(experiment_key, user_id, attributes)
|
93
|
+
@logger.log(Logger::INFO, "Not activating user '#{user_id}.")
|
94
|
+
return nil
|
95
|
+
end
|
96
|
+
|
97
|
+
variation_id = @bucketer.bucket(experiment_key, user_id)
|
98
|
+
@config.get_variation_key_from_id(experiment_key, variation_id)
|
99
|
+
end
|
100
|
+
|
101
|
+
def track(event_key, user_id, attributes = nil, event_value = nil)
|
102
|
+
# Send conversion event to Optimizely.
|
103
|
+
#
|
104
|
+
# event_key - Goal key representing the event which needs to be recorded.
|
105
|
+
# user_id - String ID for user.
|
106
|
+
# attributes - Hash representing visitor attributes and values which need to be recorded.
|
107
|
+
# event_value - Value associated with the event. Can be used to represent revenue in cents.
|
108
|
+
|
109
|
+
# Create and dispatch conversion event
|
110
|
+
|
111
|
+
return nil if attributes && !attributes_valid?(attributes)
|
112
|
+
|
113
|
+
experiment_ids = @config.get_experiment_ids_for_goal(event_key)
|
114
|
+
if experiment_ids.empty?
|
115
|
+
@config.logger.log(Logger::INFO, "Not tracking user '#{user_id}' for experiment '#{experiment_key}'.")
|
116
|
+
return nil
|
117
|
+
end
|
118
|
+
|
119
|
+
# Filter out experiments that are not running or that do not include the user in audience conditions
|
120
|
+
valid_experiment_keys = []
|
121
|
+
experiment_ids.each do |experiment_id|
|
122
|
+
experiment_key = @config.experiment_id_map[experiment_id]['key']
|
123
|
+
unless preconditions_valid?(experiment_key, user_id, attributes)
|
124
|
+
@config.logger.log(Logger::INFO, "Not tracking user '#{user_id}' for experiment '#{experiment_key}'.")
|
125
|
+
next
|
126
|
+
end
|
127
|
+
valid_experiment_keys.push(experiment_key)
|
128
|
+
end
|
129
|
+
|
130
|
+
conversion_event = @event_builder.create_conversion_event(event_key, user_id, attributes,
|
131
|
+
event_value, valid_experiment_keys)
|
132
|
+
@logger.log(Logger::INFO,
|
133
|
+
'Dispatching conversion event to URL %s with params %s.' % [conversion_event.url,
|
134
|
+
conversion_event.params])
|
135
|
+
@event_dispatcher.dispatch_event(conversion_event.url, conversion_event.params)
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def preconditions_valid?(experiment_key, user_id, attributes)
|
141
|
+
# Validates preconditions for bucketing a user.
|
142
|
+
#
|
143
|
+
# experiment_key - String key for an experiment.
|
144
|
+
# user_id - String ID of user.
|
145
|
+
# attributes - Hash of user attributes.
|
146
|
+
#
|
147
|
+
# Returns boolean representing whether all preconditions are valid.
|
148
|
+
|
149
|
+
unless @config.experiment_running?(experiment_key)
|
150
|
+
@logger.log(Logger::INFO, "Experiment '#{experiment_key} is not running.")
|
151
|
+
return false
|
152
|
+
end
|
153
|
+
|
154
|
+
unless Audience.user_in_experiment?(@config, experiment_key, attributes)
|
155
|
+
@logger.log(Logger::INFO,
|
156
|
+
"User '#{user_id} does not meet the conditions to be in experiment '#{experiment_key}.")
|
157
|
+
return false
|
158
|
+
end
|
159
|
+
|
160
|
+
true
|
161
|
+
end
|
162
|
+
|
163
|
+
def attributes_valid?(attributes)
|
164
|
+
unless Helpers::Validator.attributes_valid?(attributes)
|
165
|
+
@logger.log(Logger::ERROR, 'Provided attributes are in an invalid format.')
|
166
|
+
@error_handler.handle_error(InvalidAttributeFormatError)
|
167
|
+
return false
|
168
|
+
end
|
169
|
+
true
|
170
|
+
end
|
171
|
+
|
172
|
+
def validate_inputs(datafile)
|
173
|
+
raise InvalidDatafileError unless Helpers::Validator.datafile_valid?(datafile)
|
174
|
+
raise InvalidLoggerError unless Helpers::Validator.logger_valid?(@logger)
|
175
|
+
raise InvalidErrorHandlerError unless Helpers::Validator.error_handler_valid?(@error_handler)
|
176
|
+
raise InvalidEventDispatcherError unless Helpers::Validator.event_dispatcher_valid?(@event_dispatcher)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'json'
|
2
|
+
require_relative './condition'
|
3
|
+
|
4
|
+
module Optimizely
|
5
|
+
module Audience
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def user_in_experiment?(config, experiment_key, attributes)
|
9
|
+
# Determine for given experiment if user satisfies the audiences for the experiment.
|
10
|
+
#
|
11
|
+
# config - Representation of the Optimizely project config.
|
12
|
+
# experiment_key - Key representing experiment for which visitor is to be bucketed.
|
13
|
+
# attributes - Hash representing user attributes which will be used in determining if
|
14
|
+
# the audience conditions are met.
|
15
|
+
#
|
16
|
+
# Returns boolean representing if user satisfies audience conditions for any of the audiences or not.
|
17
|
+
|
18
|
+
audience_ids = config.get_audience_ids_for_experiment(experiment_key)
|
19
|
+
|
20
|
+
# Return true if there are no audiences
|
21
|
+
return true if audience_ids.empty?
|
22
|
+
|
23
|
+
# Return false if there are audiences but no attributes
|
24
|
+
return false unless attributes
|
25
|
+
|
26
|
+
# Return true if any one of the audience conditions are met
|
27
|
+
@condition_evaluator = ConditionEvaluator.new(attributes)
|
28
|
+
audience_ids.each do |audience_id|
|
29
|
+
audience_conditions = config.get_audience_conditions_from_id(audience_id)
|
30
|
+
audience_conditions = JSON.load(audience_conditions)
|
31
|
+
return true if @condition_evaluator.evaluate(audience_conditions)
|
32
|
+
end
|
33
|
+
|
34
|
+
false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'murmurhash3'
|
2
|
+
require_relative 'helpers/group'
|
3
|
+
|
4
|
+
module Optimizely
|
5
|
+
class Bucketer
|
6
|
+
# Optimizely bucketing algorithm that evenly distributes visitors.
|
7
|
+
|
8
|
+
BUCKETING_ID_TEMPLATE = '%{user_id}%{entity_id}'
|
9
|
+
HASH_SEED = 1
|
10
|
+
MAX_HASH_VALUE = 2**32
|
11
|
+
MAX_TRAFFIC_VALUE = 10_000
|
12
|
+
UNSIGNED_MAX_32_BIT_VALUE = 0xFFFFFFFF
|
13
|
+
|
14
|
+
def initialize(config)
|
15
|
+
# Bucketer init method to set bucketing seed and project config data.
|
16
|
+
#
|
17
|
+
# config - ProjectConfig data to be used in making bucketing decisions.
|
18
|
+
|
19
|
+
@bucket_seed = HASH_SEED
|
20
|
+
@config = config
|
21
|
+
end
|
22
|
+
|
23
|
+
def bucket(experiment_key, user_id)
|
24
|
+
# Determines ID of variation to be shown for a given experiment key and user ID.
|
25
|
+
#
|
26
|
+
# experiment_key - String Key representing experiment for which visitor is to be bucketed.
|
27
|
+
# user_id - String ID for user.
|
28
|
+
#
|
29
|
+
# Returns String variation ID in which visitor with ID user_id has been placed. Nil if no variation.
|
30
|
+
|
31
|
+
# handle forced variations if applicable
|
32
|
+
forced_variations = @config.get_forced_variations(experiment_key)
|
33
|
+
forced_variation_key = forced_variations[user_id]
|
34
|
+
if forced_variation_key
|
35
|
+
forced_variation_id = @config.get_variation_id_from_key(experiment_key, forced_variation_key)
|
36
|
+
if forced_variation_id
|
37
|
+
@config.logger.log(Logger::INFO, "User '#{user_id}' is forced in variation '#{forced_variation_key}'.")
|
38
|
+
return forced_variation_id
|
39
|
+
else
|
40
|
+
@config.logger.log(
|
41
|
+
Logger::INFO,
|
42
|
+
"Variation key '#{forced_variation_key}' is not in datafile. Not activating user '#{user_id}'."
|
43
|
+
)
|
44
|
+
return nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# check if experiment is in a group; if so, check if user is bucketed into specified experiment
|
49
|
+
experiment_id = @config.get_experiment_id(experiment_key)
|
50
|
+
group_id = @config.get_experiment_group_id(experiment_key)
|
51
|
+
if group_id
|
52
|
+
group = @config.group_key_map.fetch(group_id)
|
53
|
+
if Helpers::Group.random_policy?(group)
|
54
|
+
bucketing_id = sprintf(BUCKETING_ID_TEMPLATE, user_id: user_id, entity_id: group_id)
|
55
|
+
traffic_allocations = group.fetch('trafficAllocation')
|
56
|
+
bucket_value = generate_bucket_value(bucketing_id)
|
57
|
+
@config.logger.log(Logger::DEBUG, "Assigned experiment bucket #{bucket_value} to user '#{user_id}'.")
|
58
|
+
bucketed_experiment_id = find_bucket(bucket_value, traffic_allocations)
|
59
|
+
|
60
|
+
# return if the user is not bucketed into any experiment
|
61
|
+
unless bucketed_experiment_id
|
62
|
+
@config.logger.log(Logger::INFO, "User '#{user_id}' is in no experiment.")
|
63
|
+
return nil
|
64
|
+
end
|
65
|
+
|
66
|
+
# return if the user is bucketed into a different experiment than the one specified
|
67
|
+
if bucketed_experiment_id != experiment_id
|
68
|
+
@config.logger.log(
|
69
|
+
Logger::INFO,
|
70
|
+
"User '#{user_id}' is not in experiment '#{experiment_key}' of group #{group_id}."
|
71
|
+
)
|
72
|
+
return nil
|
73
|
+
end
|
74
|
+
|
75
|
+
# continue bucketing if the user is bucketed into the experiment specified
|
76
|
+
@config.logger.log(
|
77
|
+
Logger::INFO,
|
78
|
+
"User '#{user_id}' is in experiment '#{experiment_key}' of group #{group_id}."
|
79
|
+
)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
bucketing_id = sprintf(BUCKETING_ID_TEMPLATE, user_id: user_id, entity_id: experiment_id)
|
84
|
+
bucket_value = generate_bucket_value(bucketing_id)
|
85
|
+
@config.logger.log(Logger::DEBUG, "Assigned variation bucket #{bucket_value} to user '#{user_id}'.")
|
86
|
+
traffic_allocations = @config.get_traffic_allocation(experiment_key)
|
87
|
+
variation_id = find_bucket(bucket_value, traffic_allocations)
|
88
|
+
if variation_id
|
89
|
+
variation_key = @config.get_variation_key_from_id(experiment_key, variation_id)
|
90
|
+
@config.logger.log(
|
91
|
+
Logger::INFO,
|
92
|
+
"User '#{user_id}' is in variation '#{variation_key}' of experiment '#{experiment_key}'."
|
93
|
+
)
|
94
|
+
return variation_id
|
95
|
+
end
|
96
|
+
|
97
|
+
@config.logger.log(Logger::INFO, "User '#{user_id}' is in no variation.")
|
98
|
+
nil
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def find_bucket(bucket_value, traffic_allocations)
|
104
|
+
# Helper function to find the matching entity ID for a given bucketing value in a list of traffic allocations.
|
105
|
+
#
|
106
|
+
# bucket_value - Integer bucket value
|
107
|
+
# traffic_allocations - Array of traffic allocations
|
108
|
+
#
|
109
|
+
# Returns entity ID corresponding to the provided bucket value or nil if no match is found.
|
110
|
+
|
111
|
+
traffic_allocations.each do |traffic_allocation|
|
112
|
+
current_end_of_range = traffic_allocation['endOfRange']
|
113
|
+
if bucket_value < current_end_of_range
|
114
|
+
entity_id = traffic_allocation['entityId']
|
115
|
+
return entity_id
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
|
122
|
+
def generate_bucket_value(bucketing_id)
|
123
|
+
# Helper function to generate bucket value in half-closed interval [0, MAX_TRAFFIC_VALUE).
|
124
|
+
#
|
125
|
+
# bucketing_id - String ID for bucketing.
|
126
|
+
#
|
127
|
+
# Returns bucket value corresponding to the provided bucketing ID.
|
128
|
+
|
129
|
+
ratio = (generate_unsigned_hash_code_32_bit(bucketing_id)).to_f / MAX_HASH_VALUE
|
130
|
+
(ratio * MAX_TRAFFIC_VALUE).to_i
|
131
|
+
end
|
132
|
+
|
133
|
+
def generate_unsigned_hash_code_32_bit(bucketing_id)
|
134
|
+
# Helper function to retreive hash code
|
135
|
+
#
|
136
|
+
# bucketing_id - String ID for bucketing.
|
137
|
+
#
|
138
|
+
# Returns hash code which is a 32 bit unsigned integer.
|
139
|
+
|
140
|
+
MurmurHash3::V32.str_hash(bucketing_id, @bucket_seed) & UNSIGNED_MAX_32_BIT_VALUE
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Optimizely
|
4
|
+
class ConditionalOperatorTypes
|
5
|
+
AND = 'and'
|
6
|
+
OR = 'or'
|
7
|
+
NOT = 'not'
|
8
|
+
end
|
9
|
+
|
10
|
+
class ConditionEvaluator
|
11
|
+
DEFAULT_OPERATOR_TYPES = [
|
12
|
+
ConditionalOperatorTypes::AND,
|
13
|
+
ConditionalOperatorTypes::OR,
|
14
|
+
ConditionalOperatorTypes::NOT
|
15
|
+
]
|
16
|
+
|
17
|
+
attr_reader :user_attributes
|
18
|
+
|
19
|
+
def initialize(user_attributes)
|
20
|
+
@user_attributes = user_attributes
|
21
|
+
end
|
22
|
+
|
23
|
+
def and_evaluator(conditions)
|
24
|
+
# Evaluates an array of conditions as if the evaluator had been applied
|
25
|
+
# to each entry and the results AND-ed together.
|
26
|
+
#
|
27
|
+
# conditions - Array of conditions ex: [operand_1, operand_2]
|
28
|
+
#
|
29
|
+
# Returns boolean true if all operands evaluate to true.
|
30
|
+
|
31
|
+
conditions.each do |condition|
|
32
|
+
result = evaluate(condition)
|
33
|
+
return result if result == false
|
34
|
+
end
|
35
|
+
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
def or_evaluator(conditions)
|
40
|
+
# Evaluates an array of conditions as if the evaluator had been applied
|
41
|
+
# to each entry and the results AND-ed together.
|
42
|
+
#
|
43
|
+
# conditions - Array of conditions ex: [operand_1, operand_2]
|
44
|
+
#
|
45
|
+
# Returns boolean true if any operand evaluates to true.
|
46
|
+
|
47
|
+
conditions.each do |condition|
|
48
|
+
result = evaluate(condition)
|
49
|
+
return result if result == true
|
50
|
+
end
|
51
|
+
|
52
|
+
false
|
53
|
+
end
|
54
|
+
|
55
|
+
def not_evaluator(single_condition)
|
56
|
+
# Evaluates an array of conditions as if the evaluator had been applied
|
57
|
+
# to a single entry and NOT was applied to the result.
|
58
|
+
#
|
59
|
+
# single_condition - Array of a single condition ex: [operand_1]
|
60
|
+
#
|
61
|
+
# Returns boolean true if the operand evaluates to false.
|
62
|
+
|
63
|
+
return false if single_condition.length != 1
|
64
|
+
|
65
|
+
!evaluate(single_condition[0])
|
66
|
+
end
|
67
|
+
|
68
|
+
def evaluator(condition_array)
|
69
|
+
# Method to compare single audience condition against provided user data i.e. attributes.
|
70
|
+
#
|
71
|
+
# condition_array - Array consisting of condition key and corresponding value.
|
72
|
+
#
|
73
|
+
# Returns boolean indicating the result of comparing the condition value against the user attributes.
|
74
|
+
|
75
|
+
condition_array[1] == @user_attributes[condition_array[0]]
|
76
|
+
end
|
77
|
+
|
78
|
+
def evaluate(conditions)
|
79
|
+
# Top level method to evaluate audience conditions.
|
80
|
+
#
|
81
|
+
# conditions - Nested array of and/or conditions.
|
82
|
+
# Example: ['and', operand_1, ['or', operand_2, operand_3]]
|
83
|
+
#
|
84
|
+
# Returns boolean result of evaluating the conditions evaluated.
|
85
|
+
|
86
|
+
if conditions.is_a? Array
|
87
|
+
operator_type = conditions[0]
|
88
|
+
return false unless DEFAULT_OPERATOR_TYPES.include?(operator_type)
|
89
|
+
case operator_type
|
90
|
+
when ConditionalOperatorTypes::AND
|
91
|
+
return and_evaluator(conditions[1..-1])
|
92
|
+
when ConditionalOperatorTypes::OR
|
93
|
+
return or_evaluator(conditions[1..-1])
|
94
|
+
when ConditionalOperatorTypes::NOT
|
95
|
+
return not_evaluator(conditions[1..-1])
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Create array of condition key and corresponding value of audience condition.
|
100
|
+
condition_array = audience_condition_deserializer(conditions)
|
101
|
+
|
102
|
+
# Compare audience condition against provided user data i.e. attributes.
|
103
|
+
evaluator(condition_array)
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def audience_condition_deserializer(condition)
|
109
|
+
# Deserializer defining how hashes need to be decoded for audience conditions.
|
110
|
+
#
|
111
|
+
# condition - Hash representing one audience condition.
|
112
|
+
#
|
113
|
+
# Returns array consisting of condition key and corresponding value.
|
114
|
+
|
115
|
+
[condition['name'], condition['value']]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|