Class: Smplkit::Client

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

Overview

Synchronous entry point for the smplkit SDK.

client = Smplkit::Client.new(environment: "production", service: "my-svc")
checkout_v2 = client.flags.boolean_flag("checkout-v2", default: false)
if checkout_v2.get
  # ...
end
client.close

Block form:

Smplkit::Client.open(environment: "production", service: "my-svc") do |client|
  # ...
end

All parameters are optional. When omitted, the SDK resolves them from environment variables (SMPLKIT_*) or the ~/.smplkit configuration file. See ADR-021 for the full resolution algorithm.

Smplkit::Client is thread-safe by construction. Background work runs on internal SDK-owned threads; public methods block the calling thread and return values directly.

Constant Summary collapse

PERIODIC_FLUSH_INTERVAL =
60.0

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(api_key: nil, environment: nil, service: nil, profile: nil, base_domain: nil, scheme: nil, debug: nil, telemetry: nil) ⇒ Client

Returns a new instance of Client.



43
44
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/smplkit/client.rb', line 43

def initialize(api_key: nil, environment: nil, service: nil, profile: nil,
               base_domain: nil, scheme: nil, debug: nil, telemetry: nil)
  cfg = ConfigResolution.resolve_config(
    profile: profile, api_key: api_key, base_domain: base_domain, scheme: scheme,
    environment: environment, service: service, debug: debug, telemetry: telemetry
  )
  Smplkit.enable_debug if cfg.debug

  @api_key = cfg.api_key
  @environment = cfg.environment
  @service = cfg.service
  @base_domain = cfg.base_domain
  @scheme = cfg.scheme

  masked_key = cfg.api_key.length > 10 ? "#{cfg.api_key[0, 10]}..." : cfg.api_key
  Smplkit.debug(
    "lifecycle",
    "Client init: api_key=#{masked_key} env=#{cfg.environment.inspect} service=#{cfg.service.inspect} " \
    "base_domain=#{cfg.base_domain.inspect} scheme=#{cfg.scheme.inspect} " \
    "debug=#{cfg.debug} telemetry=#{cfg.telemetry}"
  )

  mgmt_cfg = ConfigResolution::ResolvedManagementConfig.new(
    api_key: cfg.api_key, base_domain: cfg.base_domain, scheme: cfg.scheme, debug: cfg.debug
  )
  @manage = ManagementClient.from_resolved(mgmt_cfg)

  app_url = ConfigResolution.service_url(cfg.scheme, "app", cfg.base_domain)
  flags_url = ConfigResolution.service_url(cfg.scheme, "flags", cfg.base_domain)
  logging_url = ConfigResolution.service_url(cfg.scheme, "logging", cfg.base_domain)
  @app_base_url = app_url

  @metrics = if cfg.telemetry
               MetricsReporter.new(http_client: @manage._app_http,
                                   environment: cfg.environment,
                                   service: cfg.service)
             end

  @ws_manager = nil
  @config = Config::ConfigClient.new(self, manage: @manage, metrics: @metrics)
  @flags = Flags::FlagsClient.new(self, manage: @manage, metrics: @metrics,
                                        flags_base_url: flags_url, app_base_url: app_url)
  @logging = Logging::LoggingClient.new(self, manage: @manage, metrics: @metrics,
                                              logging_base_url: logging_url, app_base_url: app_url)

  @closed = false
  schedule_periodic_flush

  @init_thread = Thread.new(@manage, @environment, @service, @app_base_url) do |mgmt, env, svc, app_url|
    register_service_context(mgmt, env, svc, app_url)
  end
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



31
32
33
# File 'lib/smplkit/client.rb', line 31

def config
  @config
end

#flagsObject (readonly)

Returns the value of attribute flags.



31
32
33
# File 'lib/smplkit/client.rb', line 31

def flags
  @flags
end

#loggingObject (readonly)

Returns the value of attribute logging.



31
32
33
# File 'lib/smplkit/client.rb', line 31

def logging
  @logging
end

#manageObject (readonly)

Returns the value of attribute manage.



31
32
33
# File 'lib/smplkit/client.rb', line 31

def manage
  @manage
end

Class Method Details

.open(**kwargs) ⇒ Object

Construct, yield to the block, and close on exit.



34
35
36
37
38
39
40
41
# File 'lib/smplkit/client.rb', line 34

def self.open(**kwargs)
  client = new(**kwargs)
  begin
    yield client
  ensure
    client.close
  end
end

Instance Method Details

#_api_keyObject



165
# File 'lib/smplkit/client.rb', line 165

def _api_key = @api_key

#_app_base_urlObject



166
# File 'lib/smplkit/client.rb', line 166

def _app_base_url = @app_base_url

#_config_transportObject



178
# File 'lib/smplkit/client.rb', line 178

def _config_transport = @manage.config

#_ensure_wsObject



169
170
171
172
173
174
175
# File 'lib/smplkit/client.rb', line 169

def _ensure_ws
  @_ensure_ws ||= begin
    ws = SharedWebSocket.new(app_base_url: @app_base_url, api_key: @api_key, metrics: @metrics)
    ws.start
    ws
  end
end

#_environmentObject



164
# File 'lib/smplkit/client.rb', line 164

def _environment = @environment

#_flags_transportObject



177
# File 'lib/smplkit/client.rb', line 177

def _flags_transport = @manage.flags

#_metricsObject



167
# File 'lib/smplkit/client.rb', line 167

def _metrics = @metrics

#_serviceObject

Internal accessors used by sub-clients ——————————–



163
# File 'lib/smplkit/client.rb', line 163

def _service = @service

#closeObject



147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/smplkit/client.rb', line 147

def close
  Smplkit.debug("lifecycle", "Client.close called")
  @closed = true
  @flush_timer&.shutdown
  final_flush
  @metrics&.close
  @logging._close
  @flags._close
  @config._close
  @ws_manager&.stop
  @ws_manager = nil
  @manage.close
end

#set_context(contexts, &block) ⇒ Object

Stash contexts as the current request’s evaluation context.

Two usage shapes:

# Fire-and-forget (typical middleware)
client.set_context([Smplkit::Context.new("user", "u-123")])

# Scoped block (impersonation or one-off override)
client.set_context([Smplkit::Context.new("user", "impersonated")]) do
  # ...
end
# original context restored here

Each unique (type, key) is also queued for bulk registration on the management API.



136
137
138
139
140
141
142
143
144
145
# File 'lib/smplkit/client.rb', line 136

def set_context(contexts, &block)
  @manage.contexts.register(contexts) if contexts && !contexts.empty?

  scope = Smplkit.set_request_context(contexts || [])
  if block
    scope.call(&block)
  else
    scope
  end
end

#wait_until_ready(timeout: 10.0) ⇒ Object

Eagerly initialize the SDK and block until it is fully ready.

Pre-fetches all flags and configs into the local cache, opens the live-updates WebSocket, and waits for the handshake to complete. After this returns, flag.get / client.config.get hit cache (no first-request connect tax) and any on_change listeners receive every server event from this point forward.

Logging integration is not installed here — call client.logging.install separately if you want it.



106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/smplkit/client.rb', line 106

def wait_until_ready(timeout: 10.0)
  @flags.start
  @config.start
  ws = _ensure_ws
  deadline = monotonic_now + timeout
  while ws.connection_status != "connected"
    if monotonic_now >= deadline
      raise TimeoutError, "Live-updates websocket did not connect within #{timeout}s " \
                          "(status: #{ws.connection_status.inspect})"
    end

    sleep(0.05)
  end
end