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

rubocop:disable Metrics/AbcSize



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
95
96
97
98
# File 'lib/smplkit/client.rb', line 45

def initialize(api_key: nil, environment: nil, service: nil, profile: nil, # rubocop:disable Metrics/AbcSize
               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)
  audit_url = ConfigResolution.service_url(cfg.scheme, "audit", 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)
  @audit = Audit::AuditClient.new(api_key: cfg.api_key, base_url: audit_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

#auditObject (readonly)

Returns the value of attribute audit.



33
34
35
# File 'lib/smplkit/client.rb', line 33

def audit
  @audit
end

#configObject (readonly)

Returns the value of attribute config.



33
34
35
# File 'lib/smplkit/client.rb', line 33

def config
  @config
end

#flagsObject (readonly)

Returns the value of attribute flags.



33
34
35
# File 'lib/smplkit/client.rb', line 33

def flags
  @flags
end

#loggingObject (readonly)

Returns the value of attribute logging.



33
34
35
# File 'lib/smplkit/client.rb', line 33

def logging
  @logging
end

#manageObject (readonly)

Returns the value of attribute manage.



33
34
35
# File 'lib/smplkit/client.rb', line 33

def manage
  @manage
end

Class Method Details

.open(**kwargs) ⇒ Object

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



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

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

Instance Method Details

#_api_keyObject



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

def _api_key = @api_key

#_app_base_urlObject



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

def _app_base_url = @app_base_url

#_config_transportObject



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

def _config_transport = @manage.config

#_ensure_wsObject



174
175
176
177
178
179
180
# File 'lib/smplkit/client.rb', line 174

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



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

def _environment = @environment

#_flags_transportObject



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

def _flags_transport = @manage.flags

#_metricsObject



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

def _metrics = @metrics

#_serviceObject

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



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

def _service = @service

#closeObject



151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/smplkit/client.rb', line 151

def close
  Smplkit.debug("lifecycle", "Client.close called")
  @closed = true
  @flush_timer&.shutdown
  final_flush
  @metrics&.close
  @logging._close
  @flags._close
  @config._close
  @audit._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.



140
141
142
143
144
145
146
147
148
149
# File 'lib/smplkit/client.rb', line 140

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.



110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/smplkit/client.rb', line 110

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