Class: Hackle::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/hackle/client.rb

Overview

The entry point of Hackle SDKs.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(core:, user_resolver:) ⇒ Client

Returns a new instance of Client.

Parameters:



28
29
30
31
32
33
34
# File 'lib/hackle/client.rb', line 28

def initialize(core:, user_resolver:)
  # @type [Hackle::Core]
  @core = core

  # @type [Hackle::HackleUserResolver]
  @user_resolver = user_resolver
end

Class Method Details

.create(sdk_key:, config: Config.builder.build) ⇒ Hackle::Client

Instantiates a Hackle client.

Parameters:

  • sdk_key (String)
  • config (Hackle::Config) (defaults to: Config.builder.build)

Returns:



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/hackle/client.rb', line 45

def self.create(sdk_key:, config: Config.builder.build)
  client = @instance[sdk_key]
  unless client.nil?
    client.__resume
    return client
  end

  Log.init(config.logger)

  sdk = Sdk.new(name: 'ruby-sdk', version: Hackle::VERSION, key: sdk_key)

  http_workspace_fetcher = HttpWorkspaceFetcher.new(
    http_client: HttpClient.create(base_url: config.sdk_url, sdk: sdk),
    sdk: sdk
  )
  workspace_fetcher = PollingWorkspaceFetcher.new(
    http_workspace_fetcher: http_workspace_fetcher,
    scheduler: Executors.scheduler,
    polling_interval_seconds: 10.0
  )

  event_dispatcher = UserEventDispatcher.create(
    http_client: HttpClient.create(base_url: config.event_url, sdk: sdk),
    executor: Executors.thread_pool(pool_size: 2, queue_capacity: 64)
  )

  event_processor = DefaultUserEventProcessor.new(
    queue: SizedQueue.new(10_000),
    event_dispatcher: event_dispatcher,
    event_dispatch_size: 100,
    flush_scheduler: Executors.scheduler,
    flush_interval_seconds: 10.0,
    shutdown_timeout_seconds: 10.0
  )

  workspace_fetcher.start
  event_processor.start

  core = Core.create(
    workspace_fetcher: workspace_fetcher,
    event_processor: event_processor
  )

  new_client = Client.new(
    core: core,
    user_resolver: HackleUserResolver.new
  )
  @instance[sdk_key] = new_client
  new_client
end

Instance Method Details

#__resumeObject



222
223
224
# File 'lib/hackle/client.rb', line 222

def __resume
  @core.resume
end

#closeObject

Shutdown the background task and release the resources used for the background task. This should only be called when the application shutdown.



218
219
220
# File 'lib/hackle/client.rb', line 218

def close
  @core.close
end

#feature_flag_detail(feature_key, user) ⇒ Hackle::FeatureFlagDecision

Decide whether the feature is turned on to the user, and returns an object that describes the way the value was decided.

Parameters:

  • feature_key (Integer)

    the unique key of the feature.

  • user (Hackle::User)

    the user requesting the feature.

Returns:



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/hackle/client.rb', line 157

def feature_flag_detail(feature_key, user)
  unless feature_key.is_a?(Integer)
    Log.get.warn { "Invalid feature key: #{feature_key} (expected: integer)" }
    return FeatureFlagDecision.new(false, DecisionReason::INVALID_INPUT, ParameterConfig.empty)
  end

  hackle_user = @user_resolver.resolve_or_nil(user)
  if hackle_user.nil?
    Log.get.warn { "Invalid hackle user: #{user}" }
    return FeatureFlagDecision.new(false, DecisionReason::INVALID_INPUT, ParameterConfig.empty)
  end

  @core.feature_flag(feature_key, hackle_user)
rescue => e
  Log.get.error { "Unexpected error while deciding feature flag[#{feature_key}]: #{e.inspect}]" }
  FeatureFlagDecision.new(false, DecisionReason::EXCEPTION, ParameterConfig.empty)
end

#is_feature_on(feature_key, user) ⇒ TrueClass, FalseClass

Decide whether the feature is turned on to the user.

Parameters:

  • feature_key (Integer)

    the unique key of the feature.

  • user (Hackle::User)

    the user requesting the feature.

Returns:

  • (TrueClass)

    of the feature is on

  • (FalseClass)

    of the feature is off



144
145
146
# File 'lib/hackle/client.rb', line 144

def is_feature_on(feature_key, user)
  feature_flag_detail(feature_key, user).is_on
end

#remote_config(user) ⇒ Hackle::RemoteConfig

Returns a instance of Hackle::RemoteConfig.

Parameters:

  • user (Hackle::User)

    the user requesting the remote config.

Returns:



182
183
184
# File 'lib/hackle/client.rb', line 182

def remote_config(user)
  RemoteConfig.new(user: user, user_resolver: @user_resolver, core: @core)
end

#track(event, user) ⇒ Object

Records the event that occurred by the user.

Parameters:



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/hackle/client.rb', line 192

def track(event, user)
  unless event.is_a?(Event)
    Log.get.warn { "Invalid event: #{event} (expected: Hackle::Event)" }
    return
  end

  unless event.valid?
    Log.get.error { "Invalid event: #{event.error_or_nil}" }
    return
  end

  hackle_user = @user_resolver.resolve_or_nil(user)
  if hackle_user.nil?
    Log.get.warn { "Invalid hackle user: #{user}" }
    return FeatureFlagDecision.new(false, DecisionReason::INVALID_INPUT, ParameterConfig.empty)
  end

  @core.track(event, hackle_user)
rescue => e
  Log.get.error { "Unexpected error while tracking event: #{e.inspect}]" }
end

#variation(experiment_key, user) ⇒ String

Decide the variation to expose to the user for experiment.

Parameters:

Returns:

  • (String)

    the decided variation for the user



104
105
106
# File 'lib/hackle/client.rb', line 104

def variation(experiment_key, user)
  variation_detail(experiment_key, user).variation
end

#variation_detail(experiment_key, user) ⇒ Hackle::ExperimentDecision

Decide the variation to expose to the user for experiment, and returns an object that describes the way the variation was decided.

Parameters:

  • experiment_key (Integer)

    the unique key of the experiment. MUST NOT be nil.

  • user (Hackle::User)

    the user to participate in the experiment. MUST NOT be nil.

Returns:



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/hackle/client.rb', line 117

def variation_detail(experiment_key, user)
  unless experiment_key.is_a?(Integer)
    Log.get.warn { "Invalid experiment key: #{experiment_key} (expected: integer)" }
    return ExperimentDecision.new('A', DecisionReason::INVALID_INPUT, ParameterConfig.empty)
  end

  hackle_user = @user_resolver.resolve_or_nil(user)
  if hackle_user.nil?
    Log.get.warn { "Invalid hackle user: #{user}" }
    return ExperimentDecision.new('A', DecisionReason::INVALID_INPUT, ParameterConfig.empty)
  end

  @core.experiment(experiment_key, hackle_user, 'A')
rescue => e
  Log.get.error { "Unexpected error while deciding variation of experiment[#{experiment_key}]: #{e.inspect}]" }
  ExperimentDecision.new('A', DecisionReason::EXCEPTION, ParameterConfig.empty)
end