hackle-ruby-sdk 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/hackle/client.rb +186 -87
- data/lib/hackle/config.rb +59 -17
- data/lib/hackle/decision.rb +113 -0
- data/lib/hackle/event.rb +89 -0
- data/lib/hackle/internal/clock/clock.rb +47 -0
- data/lib/hackle/internal/concurrent/executors.rb +20 -0
- data/lib/hackle/internal/concurrent/schedule/scheduler.rb +12 -0
- data/lib/hackle/internal/concurrent/schedule/timer_scheduler.rb +30 -0
- data/lib/hackle/internal/config/parameter_config.rb +50 -0
- data/lib/hackle/internal/core/hackle_core.rb +182 -0
- data/lib/hackle/{decision → internal/evaluation/bucketer}/bucketer.rb +17 -15
- data/lib/hackle/internal/evaluation/evaluator/contextual/contextual_evaluator.rb +29 -0
- data/lib/hackle/internal/evaluation/evaluator/delegating/delegating_evaluator.rb +26 -0
- data/lib/hackle/internal/evaluation/evaluator/evaluator.rb +117 -0
- data/lib/hackle/internal/evaluation/evaluator/experiment/experiment_evaluation_flow_factory.rb +67 -0
- data/lib/hackle/internal/evaluation/evaluator/experiment/experiment_evaluator.rb +172 -0
- data/lib/hackle/internal/evaluation/evaluator/experiment/experiment_flow_evaluator.rb +241 -0
- data/lib/hackle/internal/evaluation/evaluator/experiment/experiment_resolver.rb +166 -0
- data/lib/hackle/internal/evaluation/evaluator/remoteconfig/remote_config_determiner.rb +48 -0
- data/lib/hackle/internal/evaluation/evaluator/remoteconfig/remote_config_evaluator.rb +174 -0
- data/lib/hackle/internal/evaluation/flow/evaluation_flow.rb +49 -0
- data/lib/hackle/internal/evaluation/flow/flow_evaluator.rb +11 -0
- data/lib/hackle/internal/evaluation/match/condition/condition_matcher.rb +11 -0
- data/lib/hackle/internal/evaluation/match/condition/condition_matcher_factory.rb +53 -0
- data/lib/hackle/internal/evaluation/match/condition/experiment/experiment_condition_matcher.rb +29 -0
- data/lib/hackle/internal/evaluation/match/condition/experiment/experiment_evaluator_matcher.rb +135 -0
- data/lib/hackle/internal/evaluation/match/condition/segment/segment_condition_matcher.rb +67 -0
- data/lib/hackle/internal/evaluation/match/condition/user/user_condition_matcher.rb +44 -0
- data/lib/hackle/internal/evaluation/match/operator/operator_matcher.rb +185 -0
- data/lib/hackle/internal/evaluation/match/operator/operator_matcher_factory.rb +31 -0
- data/lib/hackle/internal/evaluation/match/target/target_matcher.rb +31 -0
- data/lib/hackle/internal/evaluation/match/value/value_matcher.rb +96 -0
- data/lib/hackle/internal/evaluation/match/value/value_matcher_factory.rb +28 -0
- data/lib/hackle/internal/evaluation/match/value/value_operator_matcher.rb +59 -0
- data/lib/hackle/internal/event/user_event.rb +187 -0
- data/lib/hackle/internal/event/user_event_dispatcher.rb +156 -0
- data/lib/hackle/internal/event/user_event_factory.rb +58 -0
- data/lib/hackle/internal/event/user_event_processor.rb +181 -0
- data/lib/hackle/internal/http/http.rb +28 -0
- data/lib/hackle/internal/http/http_client.rb +48 -0
- data/lib/hackle/internal/identifiers/identifier_builder.rb +67 -0
- data/lib/hackle/internal/logger/logger.rb +31 -0
- data/lib/hackle/internal/model/action.rb +57 -0
- data/lib/hackle/internal/model/bucket.rb +58 -0
- data/lib/hackle/internal/model/container.rb +47 -0
- data/lib/hackle/internal/model/decision_reason.rb +31 -0
- data/lib/hackle/{models → internal/model}/event_type.rb +5 -8
- data/lib/hackle/internal/model/experiment.rb +194 -0
- data/lib/hackle/internal/model/parameter_configuration.rb +19 -0
- data/lib/hackle/internal/model/remote_config_parameter.rb +76 -0
- data/lib/hackle/internal/model/sdk.rb +23 -0
- data/lib/hackle/internal/model/segment.rb +61 -0
- data/lib/hackle/internal/model/target.rb +203 -0
- data/lib/hackle/internal/model/target_rule.rb +19 -0
- data/lib/hackle/internal/model/targeting.rb +45 -0
- data/lib/hackle/internal/model/value_type.rb +75 -0
- data/lib/hackle/internal/model/variation.rb +27 -0
- data/lib/hackle/internal/model/version.rb +153 -0
- data/lib/hackle/internal/properties/properties_builder.rb +101 -0
- data/lib/hackle/internal/user/hackle_user.rb +74 -0
- data/lib/hackle/internal/user/hackle_user_resolver.rb +27 -0
- data/lib/hackle/internal/workspace/http_workspace_fetcher.rb +50 -0
- data/lib/hackle/internal/workspace/polling_workspace_fetcher.rb +62 -0
- data/lib/hackle/internal/workspace/workspace.rb +353 -0
- data/lib/hackle/internal/workspace/workspace_fetcher.rb +18 -0
- data/lib/hackle/remote_config.rb +55 -0
- data/lib/hackle/user.rb +124 -0
- data/lib/hackle/version.rb +1 -11
- data/lib/hackle.rb +4 -69
- metadata +123 -53
- data/.gitignore +0 -11
- data/.rspec +0 -2
- data/.travis.yml +0 -7
- data/Gemfile +0 -6
- data/README.md +0 -33
- data/Rakefile +0 -6
- data/hackle-ruby-sdk.gemspec +0 -29
- data/lib/hackle/decision/decider.rb +0 -69
- data/lib/hackle/events/event_dispatcher.rb +0 -96
- data/lib/hackle/events/event_processor.rb +0 -126
- data/lib/hackle/events/user_event.rb +0 -61
- data/lib/hackle/http/http.rb +0 -37
- data/lib/hackle/models/bucket.rb +0 -26
- data/lib/hackle/models/event.rb +0 -26
- data/lib/hackle/models/experiment.rb +0 -69
- data/lib/hackle/models/slot.rb +0 -22
- data/lib/hackle/models/user.rb +0 -24
- data/lib/hackle/models/variation.rb +0 -21
- data/lib/hackle/workspaces/http_workspace_fetcher.rb +0 -24
- data/lib/hackle/workspaces/polling_workspace_fetcher.rb +0 -47
- data/lib/hackle/workspaces/workspace.rb +0 -100
@@ -1,61 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Hackle
|
4
|
-
|
5
|
-
class UserEvent
|
6
|
-
|
7
|
-
# @!attribute [r] timestamp
|
8
|
-
# @return [Integer]
|
9
|
-
# @!attribute [r] user
|
10
|
-
# @return [User]
|
11
|
-
attr_reader :timestamp, :user
|
12
|
-
|
13
|
-
# @param user [User]
|
14
|
-
def initialize(user:)
|
15
|
-
@timestamp = UserEvent.generate_timestamp
|
16
|
-
@user = user
|
17
|
-
end
|
18
|
-
|
19
|
-
class Exposure < UserEvent
|
20
|
-
|
21
|
-
# @!attribute [r] experiment
|
22
|
-
# @return [Experiment]
|
23
|
-
# @!attribute [r] variation
|
24
|
-
# @return [Variation]
|
25
|
-
attr_reader :experiment, :variation
|
26
|
-
|
27
|
-
# @param user [User]
|
28
|
-
# @param experiment [Experiment]
|
29
|
-
# @param variation [Variation]
|
30
|
-
def initialize(user:, experiment:, variation:)
|
31
|
-
super(user: user)
|
32
|
-
@experiment = experiment
|
33
|
-
@variation = variation
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
|
38
|
-
class Track < UserEvent
|
39
|
-
|
40
|
-
# @!attribute [r] event_type
|
41
|
-
# @return [EventType]
|
42
|
-
# @!attribute [r] event
|
43
|
-
# @return [Event]
|
44
|
-
attr_reader :event_type, :event
|
45
|
-
|
46
|
-
# @param user [User]
|
47
|
-
# @param event_type [EventType]
|
48
|
-
# @param event [Event]
|
49
|
-
def initialize(user:, event_type:, event:)
|
50
|
-
super(user: user)
|
51
|
-
@event_type = event_type
|
52
|
-
@event = event
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
# @return [Integer]
|
57
|
-
def self.generate_timestamp
|
58
|
-
(Time.now.to_f * 1000).to_i
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
data/lib/hackle/http/http.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'net/http'
|
4
|
-
|
5
|
-
module Hackle
|
6
|
-
class UnexpectedResponseError < StandardError
|
7
|
-
end
|
8
|
-
|
9
|
-
class HTTP
|
10
|
-
def self.client(base_uri:)
|
11
|
-
uri = URI.parse(base_uri)
|
12
|
-
client = Net::HTTP.new(uri.host, uri.port)
|
13
|
-
client.use_ssl = uri.scheme == 'https'
|
14
|
-
client.open_timeout = 5
|
15
|
-
client.read_timeout = 10
|
16
|
-
client
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.sdk_headers(sdk_info:)
|
20
|
-
{
|
21
|
-
'X-HACKLE-SDK-KEY' => sdk_info.key,
|
22
|
-
'X-HACKLE-SDK-NAME' => sdk_info.name,
|
23
|
-
'X-HACKLE-SDK-VERSION' => sdk_info.version
|
24
|
-
}
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.successful?(status_code:)
|
28
|
-
status_code >= 200 && status_code < 300
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.check_successful(status_code:)
|
32
|
-
unless successful?(status_code: status_code)
|
33
|
-
raise UnexpectedResponseError, "HTTP status code #{status_code}"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
data/lib/hackle/models/bucket.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
module Hackle
|
2
|
-
|
3
|
-
class Bucket
|
4
|
-
|
5
|
-
# @!attribute [r] seed
|
6
|
-
# @return [Integer]
|
7
|
-
# @!attribute [r] slot_size
|
8
|
-
# @return [Integer]
|
9
|
-
attr_reader :seed, :slot_size
|
10
|
-
|
11
|
-
# @param seed [Integer]
|
12
|
-
# @param slot_size [Integer]
|
13
|
-
# @param slots [Array]
|
14
|
-
def initialize(seed:, slot_size:, slots:)
|
15
|
-
@seed = seed
|
16
|
-
@slot_size = slot_size
|
17
|
-
@slots = slots
|
18
|
-
end
|
19
|
-
|
20
|
-
# @param slot_number [Integer]
|
21
|
-
# @return [Slot, nil]
|
22
|
-
def get_slot(slot_number:)
|
23
|
-
@slots.find { |slot| slot.contains?(slot_number: slot_number) }
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
data/lib/hackle/models/event.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
module Hackle
|
2
|
-
class Event
|
3
|
-
|
4
|
-
# @!attribute [r] key
|
5
|
-
# @return [String]
|
6
|
-
# @!attribute [r] value
|
7
|
-
# @return [Float, nil]
|
8
|
-
# @!attribute [r] properties
|
9
|
-
# @return [Hash]
|
10
|
-
attr_reader :key, :value, :properties
|
11
|
-
|
12
|
-
|
13
|
-
# @param key [String]
|
14
|
-
# @param value [Float, nil]
|
15
|
-
# @param properties [Hash{Symbol => String, Number, boolean}]
|
16
|
-
def initialize(key:, value:, properties:)
|
17
|
-
@key = key
|
18
|
-
@value = value
|
19
|
-
@properties = properties
|
20
|
-
end
|
21
|
-
|
22
|
-
def valid?
|
23
|
-
!key.nil? && key.is_a?(String)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,69 +0,0 @@
|
|
1
|
-
module Hackle
|
2
|
-
class Experiment
|
3
|
-
|
4
|
-
# @!attribute [r] id
|
5
|
-
# @return [Integer]
|
6
|
-
# @!attribute [r] key
|
7
|
-
# @return [Integer]
|
8
|
-
attr_reader :id, :key
|
9
|
-
|
10
|
-
# @param id [Integer]
|
11
|
-
# @param key [Integer]
|
12
|
-
def initialize(id:, key:)
|
13
|
-
@id = id
|
14
|
-
@key = key
|
15
|
-
end
|
16
|
-
|
17
|
-
class Running < Experiment
|
18
|
-
|
19
|
-
# @!attribute [r] bucket
|
20
|
-
# @return [Bucket]
|
21
|
-
attr_reader :bucket
|
22
|
-
|
23
|
-
# @param id [Integer]
|
24
|
-
# @param key [Integer]
|
25
|
-
# @param bucket [Bucket]
|
26
|
-
# @param variations [Hash{String => Variation}]
|
27
|
-
# @param overrides [Hash{String => Integer}]
|
28
|
-
def initialize(id:, key:, bucket:, variations:, overrides:)
|
29
|
-
super(id: id, key: key)
|
30
|
-
@bucket = bucket
|
31
|
-
|
32
|
-
# @type [Hash{String => Variation}]
|
33
|
-
@variations = variations
|
34
|
-
|
35
|
-
# @type [Hash{String => Integer}]
|
36
|
-
@overrides = overrides
|
37
|
-
end
|
38
|
-
|
39
|
-
# @param variation_id [Integer]
|
40
|
-
# @return [Variation, nil]
|
41
|
-
def get_variation(variation_id:)
|
42
|
-
@variations[variation_id]
|
43
|
-
end
|
44
|
-
|
45
|
-
# @param user [User]
|
46
|
-
# @return [Variation, nil]
|
47
|
-
def get_overridden_variation(user:)
|
48
|
-
overridden_variation_id = @overrides[user.id]
|
49
|
-
return nil if overridden_variation_id.nil?
|
50
|
-
get_variation(variation_id: overridden_variation_id)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
class Completed < Experiment
|
55
|
-
|
56
|
-
# @!attribute [r] winner_variation_key
|
57
|
-
# @return [String]
|
58
|
-
attr_reader :winner_variation_key
|
59
|
-
|
60
|
-
# @param id [Integer]
|
61
|
-
# @param key [Integer]
|
62
|
-
# @param winner_variation_key [String]
|
63
|
-
def initialize(id:, key:, winner_variation_key:)
|
64
|
-
super(id: id, key: key)
|
65
|
-
@winner_variation_key = winner_variation_key
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
data/lib/hackle/models/slot.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
module Hackle
|
2
|
-
class Slot
|
3
|
-
# @!attribute variation_id
|
4
|
-
# @return [Integer]
|
5
|
-
attr_reader :variation_id
|
6
|
-
|
7
|
-
# @param start_inclusive [Integer]
|
8
|
-
# @param end_exclusive [Integer]
|
9
|
-
# @param variation_id [Integer]
|
10
|
-
def initialize(start_inclusive:, end_exclusive:, variation_id:)
|
11
|
-
@start_inclusive = start_inclusive
|
12
|
-
@end_exclusive = end_exclusive
|
13
|
-
@variation_id = variation_id
|
14
|
-
end
|
15
|
-
|
16
|
-
# @param slot_number [Integer]
|
17
|
-
# @return [boolean]
|
18
|
-
def contains?(slot_number:)
|
19
|
-
@start_inclusive <= slot_number && slot_number < @end_exclusive
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
data/lib/hackle/models/user.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
module Hackle
|
2
|
-
|
3
|
-
class User
|
4
|
-
|
5
|
-
# @!attribute [r] id
|
6
|
-
# @return [String]
|
7
|
-
# @!attribute [r] properties
|
8
|
-
# @return [Hash]
|
9
|
-
attr_reader :id, :properties
|
10
|
-
|
11
|
-
#
|
12
|
-
# @param id [String]
|
13
|
-
# @param properties [Hash]
|
14
|
-
#
|
15
|
-
def initialize(id:, properties:)
|
16
|
-
@id = id
|
17
|
-
@properties = properties
|
18
|
-
end
|
19
|
-
|
20
|
-
def valid?
|
21
|
-
!id.nil? && id.is_a?(String)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
module Hackle
|
2
|
-
class Variation
|
3
|
-
|
4
|
-
# @!attribute id
|
5
|
-
# @return [Integer]
|
6
|
-
# @!attribute key
|
7
|
-
# @return [String]
|
8
|
-
# @!attribute dropped
|
9
|
-
# @return [boolean]
|
10
|
-
attr_reader :id, :key, :dropped
|
11
|
-
|
12
|
-
# @param id [Integer]
|
13
|
-
# @param key [String]
|
14
|
-
# @param dropped [boolean]
|
15
|
-
def initialize(id:, key:, dropped:)
|
16
|
-
@id = id
|
17
|
-
@key = key
|
18
|
-
@dropped = dropped
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'json'
|
4
|
-
|
5
|
-
module Hackle
|
6
|
-
class HttpWorkspaceFetcher
|
7
|
-
|
8
|
-
def initialize(config:, sdk_info:)
|
9
|
-
@client = HTTP.client(base_uri: config.base_uri)
|
10
|
-
@headers = HTTP.sdk_headers(sdk_info: sdk_info)
|
11
|
-
end
|
12
|
-
|
13
|
-
def fetch
|
14
|
-
request = Net::HTTP::Get.new('/api/v1/workspaces', @headers)
|
15
|
-
response = @client.request(request)
|
16
|
-
|
17
|
-
status_code = response.code.to_i
|
18
|
-
HTTP.check_successful(status_code: status_code)
|
19
|
-
|
20
|
-
response_body = JSON.parse(response.body, symbolize_names: true)
|
21
|
-
Workspace.create(data: response_body)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'concurrent'
|
4
|
-
|
5
|
-
module Hackle
|
6
|
-
class PollingWorkspaceFetcher
|
7
|
-
|
8
|
-
DEFAULT_POLLING_INTERVAL = 10
|
9
|
-
|
10
|
-
def initialize(config:, http_fetcher:)
|
11
|
-
@logger = config.logger
|
12
|
-
@http_fetcher = http_fetcher
|
13
|
-
@current_workspace = Concurrent::AtomicReference.new
|
14
|
-
@task = Concurrent::TimerTask.new(execution_interval: DEFAULT_POLLING_INTERVAL) { poll }
|
15
|
-
@running = false
|
16
|
-
end
|
17
|
-
|
18
|
-
# @return [Workspace, nil]
|
19
|
-
def fetch
|
20
|
-
@current_workspace.get
|
21
|
-
end
|
22
|
-
|
23
|
-
def start!
|
24
|
-
return if @running
|
25
|
-
|
26
|
-
poll
|
27
|
-
@task.execute
|
28
|
-
@running = true
|
29
|
-
end
|
30
|
-
|
31
|
-
def stop!
|
32
|
-
return unless @running
|
33
|
-
|
34
|
-
@logger.info { 'Shutting down Hackle workspace_fetcher' }
|
35
|
-
|
36
|
-
@task.shutdown
|
37
|
-
@running = false
|
38
|
-
end
|
39
|
-
|
40
|
-
def poll
|
41
|
-
workspace = @http_fetcher.fetch
|
42
|
-
@current_workspace.set(workspace)
|
43
|
-
rescue => e
|
44
|
-
@logger.error { "Failed to poll Workspace: #{e.inspect}" }
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
@@ -1,100 +0,0 @@
|
|
1
|
-
module Hackle
|
2
|
-
class Workspace
|
3
|
-
|
4
|
-
# @param experiments [Hash{Integer => Experiment}]
|
5
|
-
# @param event_types [Hash{String => EventType}]
|
6
|
-
def initialize(experiments:, event_types:)
|
7
|
-
|
8
|
-
# @type [Hash{Integer => Experiment}]
|
9
|
-
@experiments = experiments
|
10
|
-
|
11
|
-
# @type [Hash{String => EventType}]
|
12
|
-
@event_types = event_types
|
13
|
-
end
|
14
|
-
|
15
|
-
# @param experiment_key [Integer]
|
16
|
-
#
|
17
|
-
# @return [Experiment, nil]
|
18
|
-
def get_experiment(experiment_key:)
|
19
|
-
@experiments[experiment_key]
|
20
|
-
end
|
21
|
-
|
22
|
-
# @param event_type_key [String]
|
23
|
-
#
|
24
|
-
# @return [EventType]
|
25
|
-
def get_event_type(event_type_key:)
|
26
|
-
event_type = @event_types[event_type_key]
|
27
|
-
|
28
|
-
if event_type.nil?
|
29
|
-
EventType.undefined(key: event_type_key)
|
30
|
-
else
|
31
|
-
event_type
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
class << self
|
36
|
-
def create(data:)
|
37
|
-
buckets = Hash[data[:buckets].map { |b| [b[:id], bucket(b)] }]
|
38
|
-
running_experiments = Hash[data[:experiments].map { |re| [re[:key], running_experiment(re, buckets)] }]
|
39
|
-
completed_experiment = Hash[data[:completedExperiments].map { |ce| [ce[:experimentKey], completed_experiment(ce)] }]
|
40
|
-
event_types = Hash[data[:events].map { |e| [e[:key], event_type(e)] }]
|
41
|
-
experiments = running_experiments.merge(completed_experiment)
|
42
|
-
Workspace.new(
|
43
|
-
experiments: experiments,
|
44
|
-
event_types: event_types
|
45
|
-
)
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
def running_experiment(data, buckets)
|
51
|
-
Experiment::Running.new(
|
52
|
-
id: data[:id],
|
53
|
-
key: data[:key],
|
54
|
-
bucket: buckets[data[:bucketId]],
|
55
|
-
variations: Hash[data[:variations].map { |v| [v[:id], variation(v)] }],
|
56
|
-
overrides: Hash[data[:execution][:userOverrides].map { |u| [u[:userId], u[:variationId]] }]
|
57
|
-
)
|
58
|
-
end
|
59
|
-
|
60
|
-
def completed_experiment(data)
|
61
|
-
Experiment::Completed.new(
|
62
|
-
id: data[:experimentId],
|
63
|
-
key: data[:experimentKey],
|
64
|
-
winner_variation_key: data[:winnerVariationKey]
|
65
|
-
)
|
66
|
-
end
|
67
|
-
|
68
|
-
def variation(data)
|
69
|
-
Variation.new(
|
70
|
-
id: data[:id],
|
71
|
-
key: data[:key],
|
72
|
-
dropped: data[:status] == 'DROPPED'
|
73
|
-
)
|
74
|
-
end
|
75
|
-
|
76
|
-
def bucket(data)
|
77
|
-
Bucket.new(
|
78
|
-
seed: data[:seed],
|
79
|
-
slot_size: data[:slotSize],
|
80
|
-
slots: data[:slots].map { |s| slot(s) }
|
81
|
-
)
|
82
|
-
end
|
83
|
-
|
84
|
-
def slot(data)
|
85
|
-
Slot.new(
|
86
|
-
start_inclusive: data[:startInclusive],
|
87
|
-
end_exclusive: data[:endExclusive],
|
88
|
-
variation_id: data[:variationId]
|
89
|
-
)
|
90
|
-
end
|
91
|
-
|
92
|
-
def event_type(data)
|
93
|
-
EventType.new(
|
94
|
-
id: data[:id],
|
95
|
-
key: data[:key]
|
96
|
-
)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|