optimizely-sdk 0.0.12
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 +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
@@ -0,0 +1,60 @@
|
|
1
|
+
require_relative 'constants'
|
2
|
+
require 'json-schema'
|
3
|
+
|
4
|
+
module Optimizely
|
5
|
+
module Helpers
|
6
|
+
module Validator
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def attributes_valid?(attributes)
|
10
|
+
# Determines if provided attributes are valid.
|
11
|
+
#
|
12
|
+
# attributes - User attributes to be validated.
|
13
|
+
#
|
14
|
+
# Returns boolean depending on validity of attributes.
|
15
|
+
|
16
|
+
attributes.is_a?(Hash)
|
17
|
+
end
|
18
|
+
|
19
|
+
def datafile_valid?(datafile)
|
20
|
+
# Determines if a given datafile is valid.
|
21
|
+
#
|
22
|
+
# datafile - String JSON representing the project.
|
23
|
+
#
|
24
|
+
# Returns boolean depending on validity of datafile.
|
25
|
+
|
26
|
+
JSON::Validator.validate(Helpers::Constants::JSON_SCHEMA, datafile)
|
27
|
+
end
|
28
|
+
|
29
|
+
def error_handler_valid?(error_handler)
|
30
|
+
# Determines if a given error handler is valid.
|
31
|
+
#
|
32
|
+
# error_handler - error_handler to be validated.
|
33
|
+
#
|
34
|
+
# Returns boolean depending on whether error_handler has a handle_error method.
|
35
|
+
|
36
|
+
error_handler.respond_to?(:handle_error)
|
37
|
+
end
|
38
|
+
|
39
|
+
def event_dispatcher_valid?(event_dispatcher)
|
40
|
+
# Determines if a given event dispatcher is valid.
|
41
|
+
#
|
42
|
+
# event_dispatcher - event_dispatcher to be validated.
|
43
|
+
#
|
44
|
+
# Returns boolean depending on whether event_dispatcher has a dispatch_event method.
|
45
|
+
|
46
|
+
event_dispatcher.respond_to?(:dispatch_event)
|
47
|
+
end
|
48
|
+
|
49
|
+
def logger_valid?(logger)
|
50
|
+
# Determines if a given logger is valid.
|
51
|
+
#
|
52
|
+
# logger - logger to be validated.
|
53
|
+
#
|
54
|
+
# Returns boolean depending on whether logger has a log method.
|
55
|
+
|
56
|
+
logger.respond_to?(:log)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Optimizely
|
4
|
+
class BaseLogger
|
5
|
+
# Class encapsulating logging functionality. Override with your own logger providing log method.
|
6
|
+
|
7
|
+
def log(_level, _message)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class NoOpLogger < BaseLogger
|
12
|
+
# Class providing log method which logs nothing.
|
13
|
+
|
14
|
+
def log(_level, _message)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class SimpleLogger < BaseLogger
|
19
|
+
# Simple wrapper around Logger.
|
20
|
+
|
21
|
+
def initialize(min_level = Logger::INFO)
|
22
|
+
@logger = Logger.new(STDOUT)
|
23
|
+
@logger.level = min_level
|
24
|
+
end
|
25
|
+
|
26
|
+
def log(level, message)
|
27
|
+
@logger.add(level, message)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Optimizely
|
4
|
+
class ProjectConfig
|
5
|
+
# Representation of the Optimizely project config.
|
6
|
+
|
7
|
+
PROJECT_CONFIG_LINK_TEMPLATE = 'https://cdn.optimizely.com/json/%{project_id}.json'
|
8
|
+
REVENUE_GOAL_KEY = 'Total Revenue'
|
9
|
+
REQUEST_TIMEOUT = 10
|
10
|
+
RUNNING_EXPERIMENT_STATUS = ['Running']
|
11
|
+
|
12
|
+
# Gets project config attributes.
|
13
|
+
attr_reader :error_handler
|
14
|
+
attr_accessor :logger
|
15
|
+
|
16
|
+
attr_reader :account_id
|
17
|
+
attr_reader :project_id
|
18
|
+
attr_reader :attributes
|
19
|
+
attr_reader :events
|
20
|
+
attr_reader :experiments
|
21
|
+
attr_reader :groups
|
22
|
+
attr_reader :revision
|
23
|
+
attr_reader :audiences
|
24
|
+
|
25
|
+
attr_reader :attribute_key_map
|
26
|
+
attr_reader :audience_id_map
|
27
|
+
attr_reader :event_key_map
|
28
|
+
attr_reader :experiment_id_map
|
29
|
+
attr_reader :experiment_key_map
|
30
|
+
attr_reader :group_key_map
|
31
|
+
attr_reader :audience_id_map
|
32
|
+
attr_reader :variation_id_map
|
33
|
+
attr_reader :variation_key_map
|
34
|
+
|
35
|
+
def initialize(datafile, logger, error_handler)
|
36
|
+
# ProjectConfig init method to fetch and set project config data
|
37
|
+
#
|
38
|
+
# datafile - JSON string representing the project
|
39
|
+
|
40
|
+
config = JSON.load(datafile)
|
41
|
+
|
42
|
+
@error_handler = error_handler
|
43
|
+
@logger = logger
|
44
|
+
@account_id = config['accountId']
|
45
|
+
@project_id = config['projectId']
|
46
|
+
@attributes = config['dimensions']
|
47
|
+
@events = config['events']
|
48
|
+
@experiments = config['experiments']
|
49
|
+
@revision = config['revision']
|
50
|
+
@audiences = config['audiences']
|
51
|
+
@groups = config.fetch('groups', [])
|
52
|
+
|
53
|
+
# Utility maps for quick lookup
|
54
|
+
@attribute_key_map = generate_key_map(@attributes, 'key')
|
55
|
+
@event_key_map = generate_key_map(@events, 'key')
|
56
|
+
@group_key_map = generate_key_map(@groups, 'id')
|
57
|
+
@group_key_map.each do |key, group|
|
58
|
+
exps = group.fetch('experiments')
|
59
|
+
exps.each do |exp|
|
60
|
+
@experiments.push(exp.merge('groupId' => key))
|
61
|
+
end
|
62
|
+
end
|
63
|
+
@experiment_key_map = generate_key_map(@experiments, 'key')
|
64
|
+
@experiment_id_map = generate_key_map(@experiments, 'id')
|
65
|
+
@audience_id_map = generate_key_map(@audiences, 'id')
|
66
|
+
@variation_id_map = {}
|
67
|
+
@variation_key_map = {}
|
68
|
+
@experiment_key_map.each do |key, exp|
|
69
|
+
variations = exp.fetch('variations')
|
70
|
+
@variation_id_map[key] = generate_key_map(variations, 'id')
|
71
|
+
@variation_key_map[key] = generate_key_map(variations, 'key')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def experiment_running?(experiment_key)
|
76
|
+
# Determine if experiment coresponding to given key is running
|
77
|
+
#
|
78
|
+
# experiment_key - String key representing the experiment
|
79
|
+
#
|
80
|
+
# Returns true if experiment is running
|
81
|
+
experiment = @experiment_key_map[experiment_key]
|
82
|
+
return RUNNING_EXPERIMENT_STATUS.include?(experiment['status']) if experiment
|
83
|
+
@logger.log Logger::ERROR, "Experiment key '#{experiment_key}' is not in datafile."
|
84
|
+
@error_handler.handle_error InvalidExperimentError
|
85
|
+
nil
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_experiment_id(experiment_key)
|
89
|
+
# Retrieves experiment ID for a given key
|
90
|
+
#
|
91
|
+
# experiment_key - String key representing the experiment
|
92
|
+
#
|
93
|
+
# Returns String ID
|
94
|
+
|
95
|
+
experiment = @experiment_key_map[experiment_key]
|
96
|
+
return experiment['id'] if experiment
|
97
|
+
@logger.log Logger::ERROR, "Experiment key '#{experiment_key}' is not in datafile."
|
98
|
+
@error_handler.handle_error InvalidExperimentError
|
99
|
+
nil
|
100
|
+
end
|
101
|
+
|
102
|
+
def get_goal_keys
|
103
|
+
# Retrieves all goals in the project except 'Total Revenue'
|
104
|
+
#
|
105
|
+
# Returns array of all goal keys except 'Total Revenue'
|
106
|
+
|
107
|
+
goal_keys = @event_key_map.keys
|
108
|
+
goal_keys.delete(REVENUE_GOAL_KEY) if goal_keys.include?(REVENUE_GOAL_KEY)
|
109
|
+
goal_keys
|
110
|
+
end
|
111
|
+
|
112
|
+
def get_revenue_goal_id
|
113
|
+
# Get ID of the revenue goal for the project
|
114
|
+
#
|
115
|
+
# Returns revenue goal ID
|
116
|
+
|
117
|
+
revenue_goal = @event_key_map[REVENUE_GOAL_KEY]
|
118
|
+
return revenue_goal['id'] if revenue_goal
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
|
122
|
+
def get_experiment_ids_for_goal(goal_key)
|
123
|
+
# Get experiment IDs for the provided goal key.
|
124
|
+
#
|
125
|
+
# goal_key - Goal key for which experiment IDs are to be retrieved.
|
126
|
+
#
|
127
|
+
# Returns array of all experiment IDs for the goal.
|
128
|
+
|
129
|
+
goal = @event_key_map[goal_key]
|
130
|
+
return goal['experimentIds'] if goal
|
131
|
+
@logger.log Logger::ERROR, "Event '#{goal_key}' is not in datafile."
|
132
|
+
@error_handler.handle_error InvalidGoalError
|
133
|
+
[]
|
134
|
+
end
|
135
|
+
|
136
|
+
def get_traffic_allocation(experiment_key)
|
137
|
+
# Retrieves traffic allocation for a given experiment Key
|
138
|
+
#
|
139
|
+
# experiment_key - String Key representing the experiment
|
140
|
+
#
|
141
|
+
# Returns traffic allocation for the experiment or nil
|
142
|
+
|
143
|
+
experiment = @experiment_key_map[experiment_key]
|
144
|
+
return experiment['trafficAllocation'] if experiment
|
145
|
+
@logger.log Logger::ERROR, "Experiment key '#{experiment_key}' is not in datafile."
|
146
|
+
@error_handler.handle_error InvalidExperimentError
|
147
|
+
nil
|
148
|
+
end
|
149
|
+
|
150
|
+
def get_audience_ids_for_experiment(experiment_key)
|
151
|
+
# Get audience IDs for the experiment
|
152
|
+
#
|
153
|
+
# experiment_key - Experiment key for which audience IDs are to be determined
|
154
|
+
#
|
155
|
+
# Returns audience IDs corresponding to the experiment.
|
156
|
+
|
157
|
+
experiment = @experiment_key_map[experiment_key]
|
158
|
+
return experiment['audienceIds'] if experiment
|
159
|
+
@logger.log Logger::ERROR, "Experiment key '#{experiment_key}' is not in datafile."
|
160
|
+
@error_handler.handle_error InvalidExperimentError
|
161
|
+
nil
|
162
|
+
end
|
163
|
+
|
164
|
+
def get_audience_conditions_from_id(audience_id)
|
165
|
+
# Get audience conditions for the provided audience ID
|
166
|
+
#
|
167
|
+
# audience_id - ID of the audience
|
168
|
+
#
|
169
|
+
# Returns conditions for the audience
|
170
|
+
|
171
|
+
audience = @audience_id_map[audience_id]
|
172
|
+
return audience['conditions'] if audience
|
173
|
+
@logger.log Logger::ERROR, "Audience '#{audience_id}' is not in datafile."
|
174
|
+
@error_handler.handle_error InvalidAudienceError
|
175
|
+
nil
|
176
|
+
end
|
177
|
+
|
178
|
+
def get_variation_key_from_id(experiment_key, variation_id)
|
179
|
+
# Get variation key given experiment key and variation ID
|
180
|
+
#
|
181
|
+
# experiment_key - Key representing parent experiment of variation
|
182
|
+
# variation_id - ID of the variation
|
183
|
+
#
|
184
|
+
# Returns key of the variation
|
185
|
+
|
186
|
+
variation_id_map = @variation_id_map[experiment_key]
|
187
|
+
if variation_id_map
|
188
|
+
variation = variation_id_map[variation_id]
|
189
|
+
return variation['key'] if variation
|
190
|
+
@logger.log Logger::ERROR, "Variation id '#{variation_id}' is not in datafile."
|
191
|
+
@error_handler.handle_error InvalidVariationError
|
192
|
+
return nil
|
193
|
+
end
|
194
|
+
|
195
|
+
@logger.log Logger::ERROR, "Experiment key '#{experiment_key}' is not in datafile."
|
196
|
+
@error_handler.handle_error InvalidExperimentError
|
197
|
+
nil
|
198
|
+
end
|
199
|
+
|
200
|
+
def get_variation_id_from_key(experiment_key, variation_key)
|
201
|
+
# Get variation ID given experiment key and variation key
|
202
|
+
#
|
203
|
+
# experiment_key - Key representing parent experiment of variation
|
204
|
+
# variation_key - Key of the variation
|
205
|
+
#
|
206
|
+
# Returns ID of the variation
|
207
|
+
|
208
|
+
variation_key_map = @variation_key_map[experiment_key]
|
209
|
+
if variation_key_map
|
210
|
+
variation = variation_key_map[variation_key]
|
211
|
+
return variation['id'] if variation
|
212
|
+
@logger.log Logger::ERROR, "Variation key '#{variation_key}' is not in datafile."
|
213
|
+
@error_handler.handle_error InvalidVariationError
|
214
|
+
return nil
|
215
|
+
end
|
216
|
+
|
217
|
+
@logger.log Logger::ERROR, "Experiment key '#{experiment_key}' is not in datafile."
|
218
|
+
@error_handler.handle_error InvalidExperimentError
|
219
|
+
nil
|
220
|
+
end
|
221
|
+
|
222
|
+
def get_forced_variations(experiment_key)
|
223
|
+
# Retrieves forced variations for a given experiment Key
|
224
|
+
#
|
225
|
+
# experiment_key - String Key representing the experiment
|
226
|
+
#
|
227
|
+
# Returns forced variations for the experiment or nil
|
228
|
+
|
229
|
+
experiment = @experiment_key_map[experiment_key]
|
230
|
+
return experiment['forcedVariations'] if experiment
|
231
|
+
@logger.log Logger::ERROR, "Experiment key '#{experiment_key}' is not in datafile."
|
232
|
+
@error_handler.handle_error InvalidExperimentError
|
233
|
+
end
|
234
|
+
|
235
|
+
def get_experiment_group_id(experiment_key)
|
236
|
+
experiment = @experiment_key_map[experiment_key]
|
237
|
+
return experiment['groupId'] if experiment
|
238
|
+
@logger.log Logger::ERROR, "Experiment key '#{experiment_key}' is not in datafile."
|
239
|
+
@error_handler.handle_error InvalidExperimentError
|
240
|
+
end
|
241
|
+
|
242
|
+
private
|
243
|
+
|
244
|
+
def generate_key_map(array, key)
|
245
|
+
# Helper method to generate map from key to hash in array of hashes
|
246
|
+
#
|
247
|
+
# array - Array consisting of hash
|
248
|
+
# key - Key in each hash which will be key in the map
|
249
|
+
#
|
250
|
+
# Returns map mapping key to hash
|
251
|
+
|
252
|
+
Hash[array.map { |obj| [obj[key], obj] }]
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
data/lib/start.rb
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
require "./optimizely.rb"
|
2
|
+
require 'json'
|
3
|
+
require 'httparty'
|
4
|
+
# conf = '{"projectId": "53020", "events": [{"key": "Total Revenue", "id": "5641834680287232", "experimentIds": ["53033"]}], "experiments": [], "dimensions": [], "audiences": [], "revision": "5", "groups": [{"policy": "random", "id": "53025", "experiments": [{"variations": [{"key": "BLAH_1", "id": "53027"}, {"key": "BLAH_2", "id": "53028"}], "trafficAllocation": [{"endOfRange": 5000, "entityId": "53027"}, {"endOfRange": 10000, "entityId": "53028"}], "forcedVariations": {}, "audienceIds": [], "percentageIncluded": 3000, "status": "Running", "key": "exp_1", "id": "53026"}, {"variations": [{"key": "BLAH_1", "id": "53034"}, {"key": "BLAH_2", "id": "53035"}], "trafficAllocation": [{"endOfRange": 3000, "entityId": "53034"}, {"endOfRange": 10000, "entityId": "53035"}], "forcedVariations": {}, "audienceIds": [], "percentageIncluded": 4000, "status": "Running", "key": "exp_2", "id": "53033"}], "trafficAllocation": [{"endOfRange": 3000, "entityId": "53026"}, {"endOfRange": 7000, "entityId": "53033"}]}], "accountId": "23001", "version": "1"}'
|
5
|
+
conf = HTTParty.get('https://cdn.optimizely.com/json/6377970066.json').body
|
6
|
+
@optly = Optimizely::Project.new(conf, nil, Optimizely::SimpleLogger.new)
|
metadata
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: optimizely-sdk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.12
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrew Delikat
|
8
|
+
- Haley Bash
|
9
|
+
- Optimizely
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2016-07-07 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: bundler
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - "~>"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.10'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - "~>"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '1.10'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: rake
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - "~>"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '10.0'
|
36
|
+
type: :development
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - "~>"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '10.0'
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: rspec
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 3.4.0
|
50
|
+
type: :development
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - "~>"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 3.4.0
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: rubocop
|
59
|
+
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - "~>"
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 0.41.1
|
64
|
+
type: :development
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - "~>"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: 0.41.1
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: murmurhash3
|
73
|
+
requirement: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - "~>"
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.1.6
|
78
|
+
type: :runtime
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - "~>"
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 0.1.6
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: httparty
|
87
|
+
requirement: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - "~>"
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: 0.13.7
|
92
|
+
type: :runtime
|
93
|
+
prerelease: false
|
94
|
+
version_requirements: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - "~>"
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: 0.13.7
|
99
|
+
- !ruby/object:Gem::Dependency
|
100
|
+
name: json-schema
|
101
|
+
requirement: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - "~>"
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: 2.6.2
|
106
|
+
type: :runtime
|
107
|
+
prerelease: false
|
108
|
+
version_requirements: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - "~>"
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 2.6.2
|
113
|
+
description: A Ruby SDK for Optimizely's server-side testing product. For more information,
|
114
|
+
please visit http://developers.optimizely.com/server.
|
115
|
+
email:
|
116
|
+
- server-side-testing@optimizely.com
|
117
|
+
executables: []
|
118
|
+
extensions: []
|
119
|
+
extra_rdoc_files: []
|
120
|
+
files:
|
121
|
+
- lib/optimizely.rb
|
122
|
+
- lib/optimizely/audience.rb
|
123
|
+
- lib/optimizely/bucketer.rb
|
124
|
+
- lib/optimizely/condition.rb
|
125
|
+
- lib/optimizely/error_handler.rb
|
126
|
+
- lib/optimizely/event_builder.rb
|
127
|
+
- lib/optimizely/event_dispatcher.rb
|
128
|
+
- lib/optimizely/exceptions.rb
|
129
|
+
- lib/optimizely/helpers/constants.rb
|
130
|
+
- lib/optimizely/helpers/group.rb
|
131
|
+
- lib/optimizely/helpers/validator.rb
|
132
|
+
- lib/optimizely/logger.rb
|
133
|
+
- lib/optimizely/params.rb
|
134
|
+
- lib/optimizely/project_config.rb
|
135
|
+
- lib/optimizely/version.rb
|
136
|
+
- lib/start.rb
|
137
|
+
homepage: http://developers.optimizely.com/server
|
138
|
+
licenses:
|
139
|
+
- Apache-2.0
|
140
|
+
metadata: {}
|
141
|
+
post_install_message:
|
142
|
+
rdoc_options: []
|
143
|
+
require_paths:
|
144
|
+
- lib
|
145
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - ">="
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '0'
|
155
|
+
requirements: []
|
156
|
+
rubyforge_project:
|
157
|
+
rubygems_version: 2.2.5
|
158
|
+
signing_key:
|
159
|
+
specification_version: 4
|
160
|
+
summary: Ruby SDK for Optimizely's testing framework
|
161
|
+
test_files: []
|