hackle-ruby-sdk 0.1.0 → 1.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
  SHA256:
3
- metadata.gz: f7ece6c3cc0981ab69f50731472d68c3f4903ccf30a5ffd5fb069210e076cc55
4
- data.tar.gz: f9f19bb60c2a4d34afe50203cc4637d92939e9c1a94e8ff86f1334e38d60a515
3
+ metadata.gz: 2eb9518d11c4980c9a6abf036b8400fad5de381db6c60d2fc98b68e37cda8851
4
+ data.tar.gz: faf6536122c01e9a55f95eef4e6497313a7e09e2ff16ea1123ca6c7fdc56470a
5
5
  SHA512:
6
- metadata.gz: e63f1afec1b82a27376f69db48721be968d72b7f959df243155d952159e615aa629dc1e98c473d72155769b7fca567c712e37c11efbbf3ccdee48726e1d76108
7
- data.tar.gz: f89a85d084ebb33b2c788674e4fb4869bae3c5d9952126faa75374a4d07b7db382af3a93b7f3c52acbf64525bdc3e0bd652d11ab64db0637f98f8b0a593541fb
6
+ metadata.gz: cbf193163d0b5926f052e76b7b01e0ca472297ef5da32b2317e90a1f35450cefa6496a72bcd0e9d6c7faf83947b6a9379e07be7111f5fa0a5030b891013c6c1c
7
+ data.tar.gz: e9d8ed3a4439684fb0b3308edb0a2e48c36ba041fa8f081e33ddad237ee8a5b546f0c98a1ec05b5c4123f76fe9289bc2ea3e84d9b02b068a3833472eec77691e
@@ -12,7 +12,7 @@ module Hackle
12
12
  # @see Client#initialize
13
13
  #
14
14
  # @param sdk_key [String] The SDK key of your Hackle environment
15
- # @param options An optional client configuration
15
+ # @param options Optional parameters of configuration options
16
16
  #
17
17
  # @return [Client] The Hackle client instance.
18
18
  #
@@ -36,4 +36,41 @@ module Hackle
36
36
  decider: Decider.new
37
37
  )
38
38
  end
39
+
40
+ #
41
+ # Instantiate a user to be used for the hackle sdk.
42
+ #
43
+ # The only required parameter is `id`, which must uniquely identify each user.
44
+ #
45
+ # @example
46
+ # Hackle.user(id: 'ae2182e0')
47
+ # Hackle.user(id: 'ae2182e0', app_version: '1.0.1', paying_customer: false)
48
+ #
49
+ # @param id [String] The identifier of the user. (e.g. device_id, account_id etc.)
50
+ # @param properties Additional properties of the user. (e.g. app_version, membership_grade, etc.)
51
+ #
52
+ # @return [User] The configured user object.
53
+ #
54
+ def self.user(id:, **properties)
55
+ User.new(id: id, properties: properties)
56
+ end
57
+
58
+ #
59
+ # Instantiate an event to be used for the hackle sdk.
60
+ #
61
+ # The only required parameter is `key`, which must uniquely identify each event.
62
+ #
63
+ # @example
64
+ # Hackle.event(key: 'purchase')
65
+ # Hackle.event(key: 'purchase', value: 42000.0, app_version: '1.0.1', payment_method: 'CARD' )
66
+ #
67
+ # @param key [String] The unique key of the events.
68
+ # @param value [Float] Optional numeric value of the events (e.g. purchase_amount, quantity, etc.)
69
+ # @param properties Additional properties of the events (e.g. app_version, os_type, etc.)
70
+ #
71
+ # @return [Event] The configured event object.
72
+ #
73
+ def self.event(key:, value: nil, **properties)
74
+ Event.new(key: key, value: value, properties: properties)
75
+ end
39
76
  end
@@ -3,16 +3,18 @@
3
3
  require 'hackle/decision/bucketer'
4
4
  require 'hackle/decision/decider'
5
5
 
6
- require 'hackle/events/event'
6
+ require 'hackle/events/user_event'
7
7
  require 'hackle/events/event_dispatcher'
8
8
  require 'hackle/events/event_processor'
9
9
 
10
10
  require 'hackle/http/http'
11
11
 
12
12
  require 'hackle/models/bucket'
13
+ require 'hackle/models/event'
13
14
  require 'hackle/models/event_type'
14
15
  require 'hackle/models/experiment'
15
16
  require 'hackle/models/slot'
17
+ require 'hackle/models/user'
16
18
  require 'hackle/models/variation'
17
19
 
18
20
  require 'hackle/workspaces/http_workspace_fetcher'
@@ -36,8 +38,14 @@ module Hackle
36
38
  #
37
39
  def initialize(config:, workspace_fetcher:, event_processor:, decider:)
38
40
  @logger = config.logger
41
+
42
+ # @type [PollingWorkspaceFetcher]
39
43
  @workspace_fetcher = workspace_fetcher
44
+
45
+ # @type [EventProcessor]
40
46
  @event_processor = event_processor
47
+
48
+ # @type [Decider]
41
49
  @decider = decider
42
50
  end
43
51
 
@@ -50,16 +58,16 @@ module Hackle
50
58
  # - The user is not allocated to the experiment
51
59
  # - The decided variation has been dropped
52
60
  #
53
- # @param experiment_key [Integer] The unique key of the experiment.
54
- # @param user_id [String] The identifier of your customer. (e.g. user_email, account_id, decide_id, etc.)
61
+ # @param experiment_key [Integer] The unique key of the experiment. MUST NOT be nil.
62
+ # @param user [User] the user to participate in the experiment. MUST NOT be nil.
55
63
  # @param default_variation [String] The default variation of the experiment.
56
64
  #
57
65
  # @return [String] The decided variation for the user, or default variation
58
66
  #
59
- def variation(experiment_key:, user_id:, default_variation: 'A')
67
+ def variation(experiment_key:, user:, default_variation: 'A')
60
68
 
61
- return default_variation if experiment_key.nil?
62
- return default_variation if user_id.nil?
69
+ return default_variation if experiment_key.nil? || !experiment_key.is_a?(Integer)
70
+ return default_variation if user.nil? || !user.is_a?(User) || !user.valid?
63
71
 
64
72
  workspace = @workspace_fetcher.fetch
65
73
  return default_variation if workspace.nil?
@@ -67,40 +75,45 @@ module Hackle
67
75
  experiment = workspace.get_experiment(experiment_key: experiment_key)
68
76
  return default_variation if experiment.nil?
69
77
 
70
- decision = @decider.decide(experiment: experiment, user_id: user_id)
78
+ decision = @decider.decide(experiment: experiment, user: user)
71
79
  case decision
72
80
  when Decision::NotAllocated
73
81
  default_variation
74
82
  when Decision::ForcedAllocated
75
83
  decision.variation_key
76
84
  when Decision::NaturalAllocated
77
- exposure_event = Event::Exposure.new(user_id: user_id, experiment: experiment, variation: decision.variation)
85
+ exposure_event = UserEvent::Exposure.new(user: user, experiment: experiment, variation: decision.variation)
78
86
  @event_processor.process(event: exposure_event)
79
87
  decision.variation.key
80
88
  else
81
89
  default_variation
82
90
  end
91
+
92
+ rescue => e
93
+ @logger.error { "Unexpected error while deciding variation for experiment[#{experiment_key}]. Returning default variation[#{default_variation}]: #{e.inspect}" }
94
+ default_variation
83
95
  end
84
96
 
85
97
  #
86
- # Records the events performed by the user.
98
+ # Records the event that occurred by the user.
87
99
  #
88
- # @param event_key [String] The unique key of the events.
89
- # @param user_id [String] The identifier of user that performed the vent.
90
- # @param value [Float] Additional numeric value of the events (e.g. purchase_amount, api_latency, etc.)
100
+ # @param event [Event] the event that occurred.
101
+ # @param user [User] the user that occurred the event.
91
102
  #
92
- def track(event_key:, user_id:, value: nil)
103
+ def track(event:, user:)
93
104
 
94
- return if event_key.nil?
95
- return if user_id.nil?
105
+ return if event.nil? || !event.is_a?(Event) || !event.valid?
106
+ return if user.nil? || !user.is_a?(User) || !user.valid?
96
107
 
97
108
  workspace = @workspace_fetcher.fetch
98
109
  return if workspace.nil?
99
110
 
100
- event_type = workspace.get_event_type(event_type_key: event_key)
101
-
102
- track_event = Event::Track.new(user_id: user_id, event_type: event_type, value: value)
111
+ event_type = workspace.get_event_type(event_type_key: event.key)
112
+ track_event = UserEvent::Track.new(user: user, event_type: event_type, event: event)
103
113
  @event_processor.process(event: track_event)
114
+
115
+ rescue => e
116
+ @logger.error { "Unexpected error while tracking event: #{e.inspect}" }
104
117
  end
105
118
 
106
119
  #
@@ -4,20 +4,34 @@ require 'murmurhash3'
4
4
 
5
5
  module Hackle
6
6
  class Bucketer
7
- def bucketing(bucket:, user_id:)
7
+
8
+ # @param bucket [Bucket]
9
+ # @param user [User]
10
+ #
11
+ # @return [Slot, nil]
12
+ def bucketing(bucket:, user:)
8
13
  slot_number = calculate_slot_number(
9
14
  seed: bucket.seed,
10
15
  slot_size: bucket.slot_size,
11
- user_id: user_id
16
+ user_id: user.id
12
17
  )
13
18
  bucket.get_slot(slot_number: slot_number)
14
19
  end
15
20
 
21
+ # @param seed [Integer]
22
+ # @param slot_size [Integer]
23
+ # @param user_id [String]
24
+ #
25
+ # @return [Integer]
16
26
  def calculate_slot_number(seed:, slot_size:, user_id:)
17
27
  hash_value = hash(data: user_id, seed: seed)
18
28
  hash_value.abs % slot_size
19
29
  end
20
30
 
31
+ # @param data [String]
32
+ # @param seed [Integer]
33
+ #
34
+ # @return [Integer]
21
35
  def hash(data:, seed:)
22
36
  unsigned_value = MurmurHash3::V32.str_hash(data, seed)
23
37
  if (unsigned_value & 0x80000000).zero?
@@ -2,20 +2,25 @@
2
2
 
3
3
  module Hackle
4
4
  class Decision
5
+
5
6
  class NotAllocated < Decision
6
7
  end
7
8
 
8
9
  class ForcedAllocated < Decision
10
+ # @return [String]
9
11
  attr_reader :variation_key
10
12
 
13
+ # @param variation_key [String]
11
14
  def initialize(variation_key:)
12
15
  @variation_key = variation_key
13
16
  end
14
17
  end
15
18
 
16
19
  class NaturalAllocated < Decision
20
+ # @return [Variation]
17
21
  attr_reader :variation
18
22
 
23
+ # @param variation [Variation]
19
24
  def initialize(variation:)
20
25
  @variation = variation
21
26
  end
@@ -27,21 +32,31 @@ module Hackle
27
32
  @bucketer = Bucketer.new
28
33
  end
29
34
 
30
- def decide(experiment:, user_id:)
35
+ # @param experiment [Experiment]
36
+ # @param user [User]
37
+ #
38
+ # @return [Decision]
39
+ def decide(experiment:, user:)
31
40
  case experiment
32
41
  when Experiment::Completed
33
42
  Decision::ForcedAllocated.new(variation_key: experiment.winner_variation_key)
34
43
  when Experiment::Running
35
- decide_running(running_experiment: experiment, user_id: user_id)
44
+ decide_running(running_experiment: experiment, user: user)
45
+ else
46
+ NotAllocated.new
36
47
  end
37
48
  end
38
49
 
39
- def decide_running(running_experiment:, user_id:)
50
+ # @param running_experiment [Experiment::Running]
51
+ # @param user [User]
52
+ #
53
+ # @return [Decision]
54
+ def decide_running(running_experiment:, user:)
40
55
 
41
- overridden_variation = running_experiment.get_overridden_variation(user_id: user_id)
56
+ overridden_variation = running_experiment.get_overridden_variation(user: user)
42
57
  return Decision::ForcedAllocated.new(variation_key: overridden_variation.key) unless overridden_variation.nil?
43
58
 
44
- allocated_slot = @bucketer.bucketing(bucket: running_experiment.bucket, user_id: user_id)
59
+ allocated_slot = @bucketer.bucketing(bucket: running_experiment.bucket, user: user)
45
60
  return Decision::NotAllocated.new if allocated_slot.nil?
46
61
 
47
62
  allocated_variation = running_experiment.get_variation(variation_id: allocated_slot.variation_id)
@@ -53,9 +53,9 @@ module Hackle
53
53
  track_events = []
54
54
  events.each do |event|
55
55
  case event
56
- when Event::Exposure
56
+ when UserEvent::Exposure
57
57
  exposure_events << create_exposure_event(event)
58
- when Event::Track
58
+ when UserEvent::Track
59
59
  track_events << create_track_event(event)
60
60
  end
61
61
  end
@@ -65,24 +65,31 @@ module Hackle
65
65
  }
66
66
  end
67
67
 
68
- def create_exposure_event(event)
68
+ #
69
+ # @param exposure [UserEvent::Exposure]
70
+ #
71
+ def create_exposure_event(exposure)
69
72
  {
70
- timestamp: event.timestamp,
71
- userId: event.user_id,
72
- experimentId: event.experiment.id,
73
- experimentKey: event.experiment.key,
74
- variationId: event.variation.id,
75
- variationKey: event.variation.key
73
+ timestamp: exposure.timestamp,
74
+ userId: exposure.user.id,
75
+ experimentId: exposure.experiment.id,
76
+ experimentKey: exposure.experiment.key,
77
+ variationId: exposure.variation.id,
78
+ variationKey: exposure.variation.key
76
79
  }
77
80
  end
78
81
 
79
- def create_track_event(event)
82
+ #
83
+ # @param track [UserEvent::Track]
84
+ #
85
+ def create_track_event(track)
80
86
  {
81
- timestamp: event.timestamp,
82
- userId: event.user_id,
83
- eventTypeId: event.event_type.id,
84
- eventTypeKey: event.event_type.key,
85
- value: event.value
87
+ timestamp: track.timestamp,
88
+ userId: track.user.id,
89
+ eventTypeId: track.event_type.id,
90
+ eventTypeKey: track.event_type.key,
91
+ value: track.event.value,
92
+ properties: track.event.properties
86
93
  }
87
94
  end
88
95
  end
@@ -6,6 +6,8 @@ module Hackle
6
6
 
7
7
  DEFAULT_FLUSH_INTERVAL = 10
8
8
 
9
+ # @param config [Config]
10
+ # @param event_dispatcher [EventDispatcher]
9
11
  def initialize(config:, event_dispatcher:)
10
12
  @logger = config.logger
11
13
  @event_dispatcher = event_dispatcher
@@ -26,6 +28,8 @@ module Hackle
26
28
  def stop!
27
29
  return unless @running
28
30
 
31
+ @logger.info { 'Shutting down Hackle event_processor' }
32
+
29
33
  @message_processor.produce(message: Message::Shutdown.new, non_block: false)
30
34
  @consume_task.join(10)
31
35
  @flush_task.shutdown
@@ -34,6 +38,7 @@ module Hackle
34
38
  @running = false
35
39
  end
36
40
 
41
+ # @param event [UserEvent]
37
42
  def process(event:)
38
43
  @message_processor.produce(message: Message::Event.new(event))
39
44
  end
@@ -44,8 +49,11 @@ module Hackle
44
49
 
45
50
  class Message
46
51
  class Event < Message
52
+
53
+ # @return [UserEvent]
47
54
  attr_reader :event
48
55
 
56
+ # @param event [UserEvent]
49
57
  def initialize(event)
50
58
  @event = event
51
59
  end
@@ -71,6 +79,8 @@ module Hackle
71
79
  @consumed_events = []
72
80
  end
73
81
 
82
+ # @param message [Message]
83
+ # @param non_block [boolean]
74
84
  def produce(message:, non_block: true)
75
85
  @message_queue.push(message, non_block)
76
86
  rescue ThreadError
@@ -99,6 +109,7 @@ module Hackle
99
109
 
100
110
  private
101
111
 
112
+ # @param event [UserEvent]
102
113
  def consume_event(event:)
103
114
  @consumed_events << event
104
115
  dispatch_events if @consumed_events.length >= DEFAULT_MAX_EVENT_DISPATCH_SIZE
@@ -0,0 +1,61 @@
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
@@ -1,13 +1,24 @@
1
1
  module Hackle
2
+
2
3
  class Bucket
4
+
5
+ # @!attribute [r] seed
6
+ # @return [Integer]
7
+ # @!attribute [r] slot_size
8
+ # @return [Integer]
3
9
  attr_reader :seed, :slot_size
4
10
 
11
+ # @param seed [Integer]
12
+ # @param slot_size [Integer]
13
+ # @param slots [Array]
5
14
  def initialize(seed:, slot_size:, slots:)
6
15
  @seed = seed
7
16
  @slot_size = slot_size
8
17
  @slots = slots
9
18
  end
10
19
 
20
+ # @param slot_number [Integer]
21
+ # @return [Slot, nil]
11
22
  def get_slot(slot_number:)
12
23
  @slots.find { |slot| slot.contains?(slot_number: slot_number) }
13
24
  end
@@ -0,0 +1,26 @@
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,12 +1,20 @@
1
1
  module Hackle
2
2
  class EventType
3
+
4
+ # @!attribute [r] id
5
+ # @return [Integer]
6
+ # @!attribute [r] key
7
+ # @return [String]
3
8
  attr_reader :id, :key
4
9
 
10
+ # @param id [Integer]
11
+ # @param key [String]
5
12
  def initialize(id:, key:)
6
13
  @id = id
7
14
  @key = key
8
15
  end
9
16
 
17
+ # @param key [String]
10
18
  def self.undefined(key:)
11
19
  EventType.new(id: 0, key: key)
12
20
  end
@@ -1,34 +1,67 @@
1
1
  module Hackle
2
2
  class Experiment
3
+
4
+ # @!attribute [r] id
5
+ # @return [Integer]
6
+ # @!attribute [r] key
7
+ # @return [Integer]
3
8
  attr_reader :id, :key
4
9
 
10
+ # @param id [Integer]
11
+ # @param key [Integer]
12
+ def initialize(id:, key:)
13
+ @id = id
14
+ @key = key
15
+ end
16
+
5
17
  class Running < Experiment
18
+
19
+ # @!attribute [r] bucket
20
+ # @return [Bucket]
6
21
  attr_reader :bucket
7
22
 
8
- def initialize(id:, key:, bucket:, variations:, user_overrides:)
9
- @id = id
10
- @key = key
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)
11
30
  @bucket = bucket
31
+
32
+ # @type [Hash{String => Variation}]
12
33
  @variations = variations
13
- @user_overrides = user_overrides
34
+
35
+ # @type [Hash{String => Integer}]
36
+ @overrides = overrides
14
37
  end
15
38
 
39
+ # @param variation_id [Integer]
40
+ # @return [Variation, nil]
16
41
  def get_variation(variation_id:)
17
42
  @variations[variation_id]
18
43
  end
19
44
 
20
- def get_overridden_variation(user_id:)
21
- variation_id = @user_overrides[user_id]
22
- get_variation(variation_id: variation_id)
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)
23
51
  end
24
52
  end
25
53
 
26
54
  class Completed < Experiment
55
+
56
+ # @!attribute [r] winner_variation_key
57
+ # @return [String]
27
58
  attr_reader :winner_variation_key
28
59
 
60
+ # @param id [Integer]
61
+ # @param key [Integer]
62
+ # @param winner_variation_key [String]
29
63
  def initialize(id:, key:, winner_variation_key:)
30
- @id = id
31
- @key = key
64
+ super(id: id, key: key)
32
65
  @winner_variation_key = winner_variation_key
33
66
  end
34
67
  end
@@ -1,13 +1,20 @@
1
1
  module Hackle
2
2
  class Slot
3
+ # @!attribute variation_id
4
+ # @return [Integer]
3
5
  attr_reader :variation_id
4
6
 
7
+ # @param start_inclusive [Integer]
8
+ # @param end_exclusive [Integer]
9
+ # @param variation_id [Integer]
5
10
  def initialize(start_inclusive:, end_exclusive:, variation_id:)
6
11
  @start_inclusive = start_inclusive
7
12
  @end_exclusive = end_exclusive
8
13
  @variation_id = variation_id
9
14
  end
10
15
 
16
+ # @param slot_number [Integer]
17
+ # @return [boolean]
11
18
  def contains?(slot_number:)
12
19
  @start_inclusive <= slot_number && slot_number < @end_exclusive
13
20
  end
@@ -0,0 +1,24 @@
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,7 +1,17 @@
1
1
  module Hackle
2
2
  class Variation
3
+
4
+ # @!attribute id
5
+ # @return [Integer]
6
+ # @!attribute key
7
+ # @return [String]
8
+ # @!attribute dropped
9
+ # @return [boolean]
3
10
  attr_reader :id, :key, :dropped
4
11
 
12
+ # @param id [Integer]
13
+ # @param key [String]
14
+ # @param dropped [boolean]
5
15
  def initialize(id:, key:, dropped:)
6
16
  @id = id
7
17
  @key = key
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hackle
4
- VERSION = '0.1.0'
4
+ VERSION = '1.0.0'
5
5
  SDK_NAME = 'ruby-sdk'
6
6
 
7
7
  class SdkInfo
@@ -15,6 +15,7 @@ module Hackle
15
15
  @running = false
16
16
  end
17
17
 
18
+ # @return [Workspace, nil]
18
19
  def fetch
19
20
  @current_workspace.get
20
21
  end
@@ -30,6 +31,8 @@ module Hackle
30
31
  def stop!
31
32
  return unless @running
32
33
 
34
+ @logger.info { 'Shutting down Hackle workspace_fetcher' }
35
+
33
36
  @task.shutdown
34
37
  @running = false
35
38
  end
@@ -1,14 +1,27 @@
1
1
  module Hackle
2
2
  class Workspace
3
+
4
+ # @param experiments [Hash{Integer => Experiment}]
5
+ # @param event_types [Hash{String => EventType}]
3
6
  def initialize(experiments:, event_types:)
7
+
8
+ # @type [Hash{Integer => Experiment}]
4
9
  @experiments = experiments
10
+
11
+ # @type [Hash{String => EventType}]
5
12
  @event_types = event_types
6
13
  end
7
14
 
15
+ # @param experiment_key [Integer]
16
+ #
17
+ # @return [Experiment, nil]
8
18
  def get_experiment(experiment_key:)
9
19
  @experiments[experiment_key]
10
20
  end
11
21
 
22
+ # @param event_type_key [String]
23
+ #
24
+ # @return [EventType]
12
25
  def get_event_type(event_type_key:)
13
26
  event_type = @event_types[event_type_key]
14
27
 
@@ -40,7 +53,7 @@ module Hackle
40
53
  key: data[:key],
41
54
  bucket: buckets[data[:bucketId]],
42
55
  variations: Hash[data[:variations].map { |v| [v[:id], variation(v)] }],
43
- user_overrides: Hash[data[:execution][:userOverrides].map { |u| [u[:userId], u[:variationId]] }]
56
+ overrides: Hash[data[:execution][:userOverrides].map { |u| [u[:userId], u[:variationId]] }]
44
57
  )
45
58
  end
46
59
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hackle-ruby-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hackle
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-03 00:00:00.000000000 Z
11
+ date: 2020-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -142,14 +142,16 @@ files:
142
142
  - lib/hackle/config.rb
143
143
  - lib/hackle/decision/bucketer.rb
144
144
  - lib/hackle/decision/decider.rb
145
- - lib/hackle/events/event.rb
146
145
  - lib/hackle/events/event_dispatcher.rb
147
146
  - lib/hackle/events/event_processor.rb
147
+ - lib/hackle/events/user_event.rb
148
148
  - lib/hackle/http/http.rb
149
149
  - lib/hackle/models/bucket.rb
150
+ - lib/hackle/models/event.rb
150
151
  - lib/hackle/models/event_type.rb
151
152
  - lib/hackle/models/experiment.rb
152
153
  - lib/hackle/models/slot.rb
154
+ - lib/hackle/models/user.rb
153
155
  - lib/hackle/models/variation.rb
154
156
  - lib/hackle/version.rb
155
157
  - lib/hackle/workspaces/http_workspace_fetcher.rb
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Hackle
4
- class Event
5
- attr_reader :timestamp, :user_id
6
-
7
- class Exposure < Event
8
- attr_reader :experiment, :variation
9
-
10
- def initialize(user_id:, experiment:, variation:)
11
- @timestamp = Event.generate_timestamp
12
- @user_id = user_id
13
- @experiment = experiment
14
- @variation = variation
15
- end
16
- end
17
-
18
- class Track < Event
19
- attr_reader :event_type, :value
20
-
21
- def initialize(user_id:, event_type:, value: nil)
22
- @timestamp = Event.generate_timestamp
23
- @user_id = user_id
24
- @event_type = event_type
25
- @value = value
26
- end
27
- end
28
-
29
- def self.generate_timestamp
30
- (Time.now.to_f * 1000).to_i
31
- end
32
- end
33
- end