hackle-ruby-sdk 0.0.1 → 1.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 +4 -4
 - data/README.md +1 -3
 - data/hackle-ruby-sdk.gemspec +1 -1
 - data/lib/hackle-ruby-sdk.rb +2 -21
 - data/lib/hackle.rb +76 -0
 - data/lib/hackle/client.rb +127 -0
 - data/lib/{hackle-ruby-sdk → hackle}/config.rb +1 -1
 - data/lib/hackle/decision/bucketer.rb +44 -0
 - data/lib/hackle/decision/decider.rb +69 -0
 - data/lib/{hackle-ruby-sdk → hackle}/events/event_dispatcher.rb +31 -24
 - data/lib/{hackle-ruby-sdk → hackle}/events/event_processor.rb +22 -11
 - data/lib/hackle/events/user_event.rb +61 -0
 - data/lib/{hackle-ruby-sdk → hackle}/http/http.rb +7 -9
 - data/lib/hackle/models/bucket.rb +26 -0
 - data/lib/hackle/models/event.rb +26 -0
 - data/lib/hackle/models/event_type.rb +22 -0
 - data/lib/hackle/models/experiment.rb +69 -0
 - data/lib/hackle/models/slot.rb +22 -0
 - data/lib/hackle/models/user.rb +24 -0
 - data/lib/hackle/models/variation.rb +21 -0
 - data/lib/{hackle-ruby-sdk → hackle}/version.rb +2 -2
 - data/lib/hackle/workspaces/http_workspace_fetcher.rb +24 -0
 - data/lib/{hackle-ruby-sdk → hackle}/workspaces/polling_workspace_fetcher.rb +4 -1
 - data/lib/hackle/workspaces/workspace.rb +100 -0
 - metadata +22 -19
 - data/lib/hackle-ruby-sdk/client.rb +0 -108
 - data/lib/hackle-ruby-sdk/decision/bucketer.rb +0 -26
 - data/lib/hackle-ruby-sdk/decision/decider.rb +0 -54
 - data/lib/hackle-ruby-sdk/events/event.rb +0 -33
 - data/lib/hackle-ruby-sdk/models/bucket.rb +0 -15
 - data/lib/hackle-ruby-sdk/models/event_type.rb +0 -10
 - data/lib/hackle-ruby-sdk/models/experiment.rb +0 -39
 - data/lib/hackle-ruby-sdk/models/slot.rb +0 -15
 - data/lib/hackle-ruby-sdk/models/variation.rb +0 -11
 - data/lib/hackle-ruby-sdk/workspaces/http_workspace_fetcher.rb +0 -24
 - data/lib/hackle-ruby-sdk/workspaces/workspace.rb +0 -78
 
| 
         @@ -6,10 +6,12 @@ module Hackle 
     | 
|
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
                DEFAULT_FLUSH_INTERVAL = 10
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
                 
     | 
| 
      
 9 
     | 
    
         
            +
                # @param config [Config]
         
     | 
| 
      
 10 
     | 
    
         
            +
                # @param event_dispatcher [EventDispatcher]
         
     | 
| 
      
 11 
     | 
    
         
            +
                def initialize(config:, event_dispatcher:)
         
     | 
| 
       10 
12 
     | 
    
         
             
                  @logger = config.logger
         
     | 
| 
       11 
13 
     | 
    
         
             
                  @event_dispatcher = event_dispatcher
         
     | 
| 
       12 
     | 
    
         
            -
                  @message_processor = MessageProcessor.new( 
     | 
| 
      
 14 
     | 
    
         
            +
                  @message_processor = MessageProcessor.new(config: config, event_dispatcher: event_dispatcher)
         
     | 
| 
       13 
15 
     | 
    
         
             
                  @flush_task = Concurrent::TimerTask.new(execution_interval: DEFAULT_FLUSH_INTERVAL) { flush }
         
     | 
| 
       14 
16 
     | 
    
         
             
                  @consume_task = nil
         
     | 
| 
       15 
17 
     | 
    
         
             
                  @running = false
         
     | 
| 
         @@ -26,7 +28,9 @@ module Hackle 
     | 
|
| 
       26 
28 
     | 
    
         
             
                def stop!
         
     | 
| 
       27 
29 
     | 
    
         
             
                  return unless @running
         
     | 
| 
       28 
30 
     | 
    
         | 
| 
       29 
     | 
    
         
            -
                  @ 
     | 
| 
      
 31 
     | 
    
         
            +
                  @logger.info { 'Shutting down Hackle event_processor' }
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  @message_processor.produce(message: Message::Shutdown.new, non_block: false)
         
     | 
| 
       30 
34 
     | 
    
         
             
                  @consume_task.join(10)
         
     | 
| 
       31 
35 
     | 
    
         
             
                  @flush_task.shutdown
         
     | 
| 
       32 
36 
     | 
    
         
             
                  @event_dispatcher.shutdown
         
     | 
| 
         @@ -34,18 +38,22 @@ module Hackle 
     | 
|
| 
       34 
38 
     | 
    
         
             
                  @running = false
         
     | 
| 
       35 
39 
     | 
    
         
             
                end
         
     | 
| 
       36 
40 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
                 
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
      
 41 
     | 
    
         
            +
                # @param event [UserEvent]
         
     | 
| 
      
 42 
     | 
    
         
            +
                def process(event:)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  @message_processor.produce(message: Message::Event.new(event))
         
     | 
| 
       39 
44 
     | 
    
         
             
                end
         
     | 
| 
       40 
45 
     | 
    
         | 
| 
       41 
46 
     | 
    
         
             
                def flush
         
     | 
| 
       42 
     | 
    
         
            -
                  @message_processor.produce(Message::Flush.new)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  @message_processor.produce(message: Message::Flush.new)
         
     | 
| 
       43 
48 
     | 
    
         
             
                end
         
     | 
| 
       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
         
     | 
| 
         @@ -63,7 +71,7 @@ module Hackle 
     | 
|
| 
       63 
71 
     | 
    
         
             
                  DEFAULT_MESSAGE_QUEUE_CAPACITY = 1000
         
     | 
| 
       64 
72 
     | 
    
         
             
                  DEFAULT_MAX_EVENT_DISPATCH_SIZE = 500
         
     | 
| 
       65 
73 
     | 
    
         | 
| 
       66 
     | 
    
         
            -
                  def initialize(event_dispatcher 
     | 
| 
      
 74 
     | 
    
         
            +
                  def initialize(config:, event_dispatcher:)
         
     | 
| 
       67 
75 
     | 
    
         
             
                    @logger = config.logger
         
     | 
| 
       68 
76 
     | 
    
         
             
                    @event_dispatcher = event_dispatcher
         
     | 
| 
       69 
77 
     | 
    
         
             
                    @message_queue = SizedQueue.new(DEFAULT_MESSAGE_QUEUE_CAPACITY)
         
     | 
| 
         @@ -71,7 +79,9 @@ module Hackle 
     | 
|
| 
       71 
79 
     | 
    
         
             
                    @consumed_events = []
         
     | 
| 
       72 
80 
     | 
    
         
             
                  end
         
     | 
| 
       73 
81 
     | 
    
         | 
| 
       74 
     | 
    
         
            -
                   
     | 
| 
      
 82 
     | 
    
         
            +
                  # @param message [Message]
         
     | 
| 
      
 83 
     | 
    
         
            +
                  # @param non_block [boolean]
         
     | 
| 
      
 84 
     | 
    
         
            +
                  def produce(message:, non_block: true)
         
     | 
| 
       75 
85 
     | 
    
         
             
                    @message_queue.push(message, non_block)
         
     | 
| 
       76 
86 
     | 
    
         
             
                  rescue ThreadError
         
     | 
| 
       77 
87 
     | 
    
         
             
                    if @random.rand(1..100) == 1 # log only 1% of the time
         
     | 
| 
         @@ -84,7 +94,7 @@ module Hackle 
     | 
|
| 
       84 
94 
     | 
    
         
             
                      message = @message_queue.pop
         
     | 
| 
       85 
95 
     | 
    
         
             
                      case message
         
     | 
| 
       86 
96 
     | 
    
         
             
                      when Message::Event
         
     | 
| 
       87 
     | 
    
         
            -
                        consume_event(message.event)
         
     | 
| 
      
 97 
     | 
    
         
            +
                        consume_event(event: message.event)
         
     | 
| 
       88 
98 
     | 
    
         
             
                      when Message::Flush
         
     | 
| 
       89 
99 
     | 
    
         
             
                        dispatch_events
         
     | 
| 
       90 
100 
     | 
    
         
             
                      when Message::Shutdown
         
     | 
| 
         @@ -99,7 +109,8 @@ module Hackle 
     | 
|
| 
       99 
109 
     | 
    
         | 
| 
       100 
110 
     | 
    
         
             
                  private
         
     | 
| 
       101 
111 
     | 
    
         | 
| 
       102 
     | 
    
         
            -
                   
     | 
| 
      
 112 
     | 
    
         
            +
                  # @param event [UserEvent]
         
     | 
| 
      
 113 
     | 
    
         
            +
                  def consume_event(event:)
         
     | 
| 
       103 
114 
     | 
    
         
             
                    @consumed_events << event
         
     | 
| 
       104 
115 
     | 
    
         
             
                    dispatch_events if @consumed_events.length >= DEFAULT_MAX_EVENT_DISPATCH_SIZE
         
     | 
| 
       105 
116 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -107,7 +118,7 @@ module Hackle 
     | 
|
| 
       107 
118 
     | 
    
         
             
                  def dispatch_events
         
     | 
| 
       108 
119 
     | 
    
         
             
                    return if @consumed_events.empty?
         
     | 
| 
       109 
120 
     | 
    
         | 
| 
       110 
     | 
    
         
            -
                    @event_dispatcher.dispatch(@consumed_events)
         
     | 
| 
      
 121 
     | 
    
         
            +
                    @event_dispatcher.dispatch(events: @consumed_events)
         
     | 
| 
       111 
122 
     | 
    
         
             
                    @consumed_events = []
         
     | 
| 
       112 
123 
     | 
    
         
             
                  end
         
     | 
| 
       113 
124 
     | 
    
         
             
                end
         
     | 
| 
         @@ -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
         
     | 
| 
         @@ -4,14 +4,10 @@ require 'net/http' 
     | 
|
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            module Hackle
         
     | 
| 
       6 
6 
     | 
    
         
             
              class UnexpectedResponseError < StandardError
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
                def initialize(status_code)
         
     | 
| 
       9 
     | 
    
         
            -
                  super("HTTP status code #{status_code}")
         
     | 
| 
       10 
     | 
    
         
            -
                end
         
     | 
| 
       11 
7 
     | 
    
         
             
              end
         
     | 
| 
       12 
8 
     | 
    
         | 
| 
       13 
9 
     | 
    
         
             
              class HTTP
         
     | 
| 
       14 
     | 
    
         
            -
                def self.client(base_uri)
         
     | 
| 
      
 10 
     | 
    
         
            +
                def self.client(base_uri:)
         
     | 
| 
       15 
11 
     | 
    
         
             
                  uri = URI.parse(base_uri)
         
     | 
| 
       16 
12 
     | 
    
         
             
                  client = Net::HTTP.new(uri.host, uri.port)
         
     | 
| 
       17 
13 
     | 
    
         
             
                  client.use_ssl = uri.scheme == 'https'
         
     | 
| 
         @@ -20,7 +16,7 @@ module Hackle 
     | 
|
| 
       20 
16 
     | 
    
         
             
                  client
         
     | 
| 
       21 
17 
     | 
    
         
             
                end
         
     | 
| 
       22 
18 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
                def self.sdk_headers(sdk_info)
         
     | 
| 
      
 19 
     | 
    
         
            +
                def self.sdk_headers(sdk_info:)
         
     | 
| 
       24 
20 
     | 
    
         
             
                  {
         
     | 
| 
       25 
21 
     | 
    
         
             
                    'X-HACKLE-SDK-KEY' => sdk_info.key,
         
     | 
| 
       26 
22 
     | 
    
         
             
                    'X-HACKLE-SDK-NAME' => sdk_info.name,
         
     | 
| 
         @@ -28,12 +24,14 @@ module Hackle 
     | 
|
| 
       28 
24 
     | 
    
         
             
                  }
         
     | 
| 
       29 
25 
     | 
    
         
             
                end
         
     | 
| 
       30 
26 
     | 
    
         | 
| 
       31 
     | 
    
         
            -
                def self.successful?(status_code)
         
     | 
| 
      
 27 
     | 
    
         
            +
                def self.successful?(status_code:)
         
     | 
| 
       32 
28 
     | 
    
         
             
                  status_code >= 200 && status_code < 300
         
     | 
| 
       33 
29 
     | 
    
         
             
                end
         
     | 
| 
       34 
30 
     | 
    
         | 
| 
       35 
     | 
    
         
            -
                def self.check_successful(status_code)
         
     | 
| 
       36 
     | 
    
         
            -
                   
     | 
| 
      
 31 
     | 
    
         
            +
                def self.check_successful(status_code:)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  unless successful?(status_code: status_code)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    raise UnexpectedResponseError, "HTTP status code #{status_code}"
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
       37 
35 
     | 
    
         
             
                end
         
     | 
| 
       38 
36 
     | 
    
         
             
              end
         
     | 
| 
       39 
37 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,26 @@ 
     | 
|
| 
      
 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
         
     | 
| 
         @@ -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
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Hackle
         
     | 
| 
      
 2 
     | 
    
         
            +
              class EventType
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                # @!attribute [r] id
         
     | 
| 
      
 5 
     | 
    
         
            +
                #  @return [Integer]
         
     | 
| 
      
 6 
     | 
    
         
            +
                # @!attribute [r] key
         
     | 
| 
      
 7 
     | 
    
         
            +
                #  @return [String]
         
     | 
| 
      
 8 
     | 
    
         
            +
                attr_reader :id, :key
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                # @param id [Integer]
         
     | 
| 
      
 11 
     | 
    
         
            +
                # @param key [String]
         
     | 
| 
      
 12 
     | 
    
         
            +
                def initialize(id:, key:)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @id = id
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @key = key
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                # @param key [String]
         
     | 
| 
      
 18 
     | 
    
         
            +
                def self.undefined(key:)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  EventType.new(id: 0, key: key)
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,69 @@ 
     | 
|
| 
      
 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
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 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
         
     | 
| 
         @@ -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
         
     | 
| 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 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,12 +1,12 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module Hackle
         
     | 
| 
       4 
     | 
    
         
            -
              VERSION = '0.0 
     | 
| 
      
 4 
     | 
    
         
            +
              VERSION = '1.0.0'
         
     | 
| 
       5 
5 
     | 
    
         
             
              SDK_NAME = 'ruby-sdk'
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
              class SdkInfo
         
     | 
| 
       8 
8 
     | 
    
         
             
                attr_reader :key, :name, :version
         
     | 
| 
       9 
     | 
    
         
            -
                def initialize(key)
         
     | 
| 
      
 9 
     | 
    
         
            +
                def initialize(key:)
         
     | 
| 
       10 
10 
     | 
    
         
             
                  @key = key
         
     | 
| 
       11 
11 
     | 
    
         
             
                  @name = SDK_NAME
         
     | 
| 
       12 
12 
     | 
    
         
             
                  @version = VERSION
         
     | 
| 
         @@ -0,0 +1,24 @@ 
     | 
|
| 
      
 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
         
     |