Class: Smplkit::Client
- Inherits:
-
Object
- Object
- Smplkit::Client
- 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 each one in precedence order, lowest to highest: built-in defaults, then the ~/.smplkit configuration file, then SMPLKIT_* environment variables, then the explicit constructor arguments (a value supplied at a higher level overrides the lower ones).
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 =
Periodic flush of all sub-client registration buffers (contexts, flags, loggers). Threshold flushes still fire immediately when buffers fill up; this timer is the liveness guarantee for the tail.
60.0
Instance Attribute Summary collapse
-
#account ⇒ Object
readonly
Returns the value of attribute account.
-
#audit ⇒ Object
readonly
Returns the value of attribute audit.
-
#config ⇒ Object
readonly
Returns the value of attribute config.
-
#flags ⇒ Object
readonly
Returns the value of attribute flags.
-
#jobs ⇒ Object
readonly
Returns the value of attribute jobs.
-
#logging ⇒ Object
readonly
Returns the value of attribute logging.
-
#platform ⇒ Object
readonly
Returns the value of attribute platform.
Class Method Summary collapse
-
.open(**kwargs) {|client| ... } ⇒ Object
Construct a client, yield it to the block, and close it on exit.
Instance Method Summary collapse
- #_api_key ⇒ Object
- #_app_base_url ⇒ Object
-
#_ensure_started ⇒ Object
Start the deferred background machinery exactly once.
- #_ensure_ws ⇒ Object
- #_environment ⇒ Object
- #_extra_headers ⇒ Object
- #_metrics ⇒ Object
-
#_service ⇒ Object
Internal accessors used by sub-clients ——————————–.
-
#close ⇒ void
Release all resources held by this client.
-
#initialize(api_key: nil, environment: nil, service: nil, profile: nil, base_domain: nil, scheme: nil, debug: nil, telemetry: nil, extra_headers: nil) ⇒ Client
constructor
A new instance of Client.
-
#set_context(contexts) { ... } ⇒ Object, Smplkit::ContextScope
Stash
contextsas the current request’s evaluation context. -
#wait_until_ready(timeout: 10.0) ⇒ void
Optionally pre-warm the SDK and block until the live socket is up.
Constructor Details
#initialize(api_key: nil, environment: nil, service: nil, profile: nil, base_domain: nil, scheme: nil, debug: nil, telemetry: nil, extra_headers: nil) ⇒ Client
Returns a new instance of Client.
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 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/smplkit/client.rb', line 62 def initialize(api_key: nil, environment: nil, service: nil, profile: nil, base_domain: nil, scheme: nil, debug: nil, telemetry: nil, extra_headers: 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 @extra_headers = extra_headers 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}" ) # Build the per-service HTTP transports + the context-registration buffer. # Side-effect-free: each transport connects lazily on first call. # client.platform owns the buffer; client.config/flags/logging/jobs borrow # their transports from here. @transports = Transport.build_service_transports(Transport.to_transport_config(cfg, extra_headers)) app_url = @transports.app_url audit_url = ConfigResolution.service_url(cfg.scheme, "audit", cfg.base_domain) # Alias the shared HTTP transports — single connection pool per service. @http_client = @transports.config_http @app_http = @transports.app_http @app_base_url = app_url # Metrics reporter @metrics = if cfg.telemetry MetricsReporter.new(http_client: @app_http, environment: cfg.environment, service: cfg.service) end @ws_manager = nil # Platform's cross-cutting CRUD on one client; wired into this parent so # it borrows the shared app transport, and owns the context-registration # buffer. Built BEFORE flags so the contexts seam below is available. @platform = Platform::PlatformClient.new(app_transport: @app_http) # Account-level settings on one client; built from the app url + api key # (the settings sub-client uses Faraday directly). @account = Account::AccountClient.new(api_key: cfg.api_key, base_url: app_url, extra_headers: extra_headers) # Config's full surface on one client; wired into this parent so it # borrows the shared config transport and WebSocket. @config = Config::ConfigClient.new(parent: self, transport: @transports.config_http, metrics: @metrics) # Flags' full surface on one client; wired into this parent so it borrows # the shared flags transport and WebSocket. ``contexts`` is the injection # seam for evaluation-context registration, wired to # ``client.platform.contexts``. @flags = Flags::FlagsClient.new( parent: self, transport: @transports.flags_http, contexts: @platform.contexts, metrics: @metrics ) # Logging's full surface on one client; wired into this parent so it # borrows the shared logging transport and WebSocket. The two management # sub-clients live at client.logging.loggers / client.logging.log_groups. @logging = Logging::LoggingClient.new(parent: self, transport: @transports.logging_http, metrics: @metrics) # Audit's full surface on one client; this runtime instance carries the # configured environment as ``X-Smplkit-Environment`` and owns its own # transport (closed in ``close``). @audit = Audit::AuditClient.new( api_key: cfg.api_key, base_url: audit_url, environment: cfg.environment, extra_headers: extra_headers ) # Jobs has no runtime/management split — reuse the shared jobs transport # (single connection pool) so ``client.jobs`` is one-stop. @jobs = Jobs::JobsClient.new(auth_client: @transports.jobs_http) # Construction is side-effect-free: no background threads, no phone-home. # The periodic registration-buffer flush and the service-context # registration are deferred until the first config/flags/logging operation # or set_context via ``_ensure_started`` — so an audit-only or jobs-only # customer pays zero threads and zero network at construction. @closed = false @started = false @start_lock = Mutex.new @flush_timer = nil @init_thread = nil end |
Instance Attribute Details
#account ⇒ Object (readonly)
Returns the value of attribute account.
36 37 38 |
# File 'lib/smplkit/client.rb', line 36 def account @account end |
#audit ⇒ Object (readonly)
Returns the value of attribute audit.
36 37 38 |
# File 'lib/smplkit/client.rb', line 36 def audit @audit end |
#config ⇒ Object (readonly)
Returns the value of attribute config.
36 37 38 |
# File 'lib/smplkit/client.rb', line 36 def config @config end |
#flags ⇒ Object (readonly)
Returns the value of attribute flags.
36 37 38 |
# File 'lib/smplkit/client.rb', line 36 def flags @flags end |
#jobs ⇒ Object (readonly)
Returns the value of attribute jobs.
36 37 38 |
# File 'lib/smplkit/client.rb', line 36 def jobs @jobs end |
#logging ⇒ Object (readonly)
Returns the value of attribute logging.
36 37 38 |
# File 'lib/smplkit/client.rb', line 36 def logging @logging end |
#platform ⇒ Object (readonly)
Returns the value of attribute platform.
36 37 38 |
# File 'lib/smplkit/client.rb', line 36 def platform @platform end |
Class Method Details
.open(**kwargs) {|client| ... } ⇒ Object
Construct a client, yield it to the block, and close it on exit.
43 44 45 46 47 48 49 50 |
# File 'lib/smplkit/client.rb', line 43 def self.open(**kwargs) client = new(**kwargs) begin yield client ensure client.close end end |
Instance Method Details
#_api_key ⇒ Object
248 |
# File 'lib/smplkit/client.rb', line 248 def _api_key = @api_key |
#_app_base_url ⇒ Object
249 |
# File 'lib/smplkit/client.rb', line 249 def _app_base_url = @app_base_url |
#_ensure_started ⇒ Object
Start the deferred background machinery exactly once.
Idempotent and thread-safe (lock + flag); a no-op after close. Triggered by the first config/flags/logging operation, set_context, wait_until_ready, or WebSocket open — never at construction.
258 259 260 261 262 263 264 265 266 |
# File 'lib/smplkit/client.rb', line 258 def _ensure_started @start_lock.synchronize do return if @started || @closed @started = true end schedule_periodic_flush @init_thread = Thread.new { register_service_context } end |
#_ensure_ws ⇒ Object
268 269 270 271 272 273 274 275 |
# File 'lib/smplkit/client.rb', line 268 def _ensure_ws _ensure_started if @ws_manager.nil? @ws_manager = SharedWebSocket.new(app_base_url: @app_base_url, api_key: @api_key, metrics: @metrics) @ws_manager.start end @ws_manager end |
#_environment ⇒ Object
247 |
# File 'lib/smplkit/client.rb', line 247 def _environment = @environment |
#_extra_headers ⇒ Object
251 |
# File 'lib/smplkit/client.rb', line 251 def _extra_headers = @extra_headers |
#_metrics ⇒ Object
250 |
# File 'lib/smplkit/client.rb', line 250 def _metrics = @metrics |
#_service ⇒ Object
Internal accessors used by sub-clients ——————————–
246 |
# File 'lib/smplkit/client.rb', line 246 def _service = @service |
#close ⇒ void
This method returns an undefined value.
Release all resources held by this client.
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/smplkit/client.rb', line 226 def close Smplkit.debug("lifecycle", "Client.close called") @closed = true @flush_timer&.shutdown @flush_timer = nil final_flush @metrics&.close @logging._close @flags._close @audit._close @ws_manager&.stop @ws_manager = nil # Close the shared per-service HTTP transports (app/config/flags/logging/ # jobs). client.platform/account borrow the app transport and close # nothing; client.audit owns and closed its own transport above. @transports.close end |
#set_context(contexts) { ... } ⇒ Object, Smplkit::ContextScope
Stash contexts as the current request’s evaluation context.
Typical use is from middleware — set the context once at request entry and every flag.get (and other context-sensitive evaluations) inside that request automatically picks it up.
Each unique (type, key) is also registered with the platform (deduplicated via an LRU; sent in the background).
Two usage shapes:
# Fire-and-forget (typical middleware)
client.set_context([Smplkit::Context.new("user", "u-123")])
# Scoped block (e.g. impersonation or one-off override)
client.set_context([Smplkit::Context.new("user", "impersonated")]) do
# ...
end
# original context restored here
211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/smplkit/client.rb', line 211 def set_context(contexts, &block) _ensure_started @platform.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) ⇒ void
This method returns an undefined value.
Optionally pre-warm the SDK and block until the live socket is up.
Eagerly connects config and flags — flushing discovery, pre-fetching all flags and configs into the local cache, opening the live-updates WebSocket — and waits for the handshake to complete. After this returns, flag.get / client.config.subscribe hit cache (no first-request connect tax) and any on_change listeners receive every server event from this point forward.
Optional: config and flags connect lazily on first live use, so this is purely a pre-warm / WebSocket-ready barrier. Logging integration is not connected here — call client.logging.install separately if you want it (it installs adapters and hooks into your application’s logger, which should be opt-in).
169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/smplkit/client.rb', line 169 def wait_until_ready(timeout: 10.0) @flags._ensure_connected @config._ensure_connected 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 |