Class: Smplkit::Flags::FlagsClient
- Inherits:
-
Object
- Object
- Smplkit::Flags::FlagsClient
- Defined in:
- lib/smplkit/flags/client.rb
Overview
The Smpl Flags client (sync).
One client exposes the full surface, reachable as client.flags (Smplkit::Client) or constructed directly:
flags = Smplkit::FlagsClient.new(environment: "production")
new_flag = flags.new_boolean_flag("beta", default: false)
new_flag.save
beta = flags.boolean_flag("beta", default: false)
beta.get # => ...
The management surface (new_* / get / list / delete and discovery) is pure CRUD. The live surface (boolean_flag / string_flag / number_flag / json_flag / refresh / stats / on_change) connects lazily on first use — the first call flushes discovery, fetches all flag definitions into the local cache, and opens the live-updates WebSocket. No explicit install step is required.
Class Method Summary collapse
-
.open(**kwargs) ⇒ Object
Construct, yield to the block, and close on exit.
Instance Method Summary collapse
- #_create_flag(flag) ⇒ Object
-
#_ensure_connected ⇒ Object
Internal: trigger lazy connect.
-
#_evaluate_handle(flag_id, default, context) ⇒ Object
Core evaluation used by flag handles (the
.getpath). - #_update_flag(flag) ⇒ Object
-
#boolean_flag(id, default:) ⇒ Object
Declare a boolean flag handle for live evaluation.
-
#close ⇒ Object
(also: #_close)
Release resources — only those this client owns.
-
#delete(id) ⇒ Object
Delete a flag by id.
-
#flush ⇒ Object
POST pending declarations to the flags bulk endpoint.
-
#flush_sync ⇒ Object
Synchronous flush — alias of
flushfor the periodic-flush path. -
#get(id) ⇒ Object
Fetch the editable
Flagresource by id. -
#initialize(api_key = nil, environment: nil, base_url: nil, profile: nil, base_domain: nil, scheme: nil, debug: nil, extra_headers: nil, parent: nil, transport: nil, contexts: nil, metrics: nil) ⇒ FlagsClient
constructor
A new instance of FlagsClient.
-
#json_flag(id, default:) ⇒ Object
Declare a JSON flag handle for live evaluation.
-
#list(page_number: nil, page_size: nil) ⇒ Object
List flags for the authenticated account.
-
#new_boolean_flag(id, default:, name: nil, description: nil) ⇒ Object
Return a new unsaved boolean
BooleanFlag. -
#new_json_flag(id, default:, name: nil, description: nil, values: nil) ⇒ Object
Return a new unsaved JSON
JsonFlag. -
#new_number_flag(id, default:, name: nil, description: nil, values: nil) ⇒ Object
Return a new unsaved numeric
NumberFlag. -
#new_string_flag(id, default:, name: nil, description: nil, values: nil) ⇒ Object
Return a new unsaved string
StringFlag. -
#number_flag(id, default:) ⇒ Object
Declare a numeric flag handle for live evaluation.
-
#on_change(flag_id = nil, &block) ⇒ Object
Register a change listener.
-
#pending_count ⇒ Object
Number of pending flag declarations awaiting flush.
-
#refresh ⇒ Object
Re-fetch all flag definitions and clear cache.
-
#register(items, flush: false) ⇒ Object
Buffer flag declarations for bulk-discovery upload; optionally flush now.
-
#stats ⇒ Object
Return evaluation statistics.
-
#string_flag(id, default:) ⇒ Object
Declare a string flag handle for live evaluation.
Constructor Details
#initialize(api_key = nil, environment: nil, base_url: nil, profile: nil, base_domain: nil, scheme: nil, debug: nil, extra_headers: nil, parent: nil, transport: nil, contexts: nil, metrics: nil) ⇒ FlagsClient
Returns a new instance of FlagsClient.
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/smplkit/flags/client.rb', line 166 def initialize(api_key = nil, environment: nil, base_url: nil, profile: nil, base_domain: nil, scheme: nil, debug: nil, extra_headers: nil, parent: nil, transport: nil, contexts: nil, metrics: nil) @parent = parent @metrics = metrics @environment = parent.nil? ? environment : parent._environment @service = parent&._service @standalone_api_key = nil if transport.nil? @flags_http, app_http, @app_base_url, @standalone_api_key = Flags.flags_transport( api_key: api_key, base_url: base_url, profile: profile, base_domain: base_domain, scheme: scheme, debug: debug, extra_headers: extra_headers ) # Standalone: build our own contexts client (and own its app transport). @contexts = Platform::ContextsClient.new(app_http, ContextRegistrationBuffer.new) else @flags_http = transport @app_base_url = nil # Wired: borrow client.platform.contexts as the evaluation-context # registration seam. @contexts = contexts end @api = SmplkitGeneratedClient::Flags::FlagsApi.new(@flags_http) # Discovery buffer is owned by this client (no management delegation). @buffer = FlagRegistrationBuffer.new # Live-surface state. @flag_store = {} @connected = false @ws_subscribed = false @cache = ResolutionCache.new @handles = {} @global_listeners = [] @key_listeners = Hash.new { |h, k| h[k] = [] } @ws_manager = nil @owns_ws = false @lock = Mutex.new end |
Class Method Details
.open(**kwargs) ⇒ Object
Construct, yield to the block, and close on exit.
411 412 413 414 415 416 417 418 |
# File 'lib/smplkit/flags/client.rb', line 411 def self.open(**kwargs) client = new(**kwargs) begin yield client ensure client.close end end |
Instance Method Details
#_create_flag(flag) ⇒ Object
265 266 267 268 |
# File 'lib/smplkit/flags/client.rb', line 265 def _create_flag(flag) response = ApiSupport::ErrorMapping.call { @api.create_flag(flag_body(flag)) } model_from_resource(ApiSupport::ResourceShim.from_model(response.data)) end |
#_ensure_connected ⇒ Object
Internal: trigger lazy connect. Used by Client#wait_until_ready.
465 466 467 |
# File 'lib/smplkit/flags/client.rb', line 465 def _ensure_connected ensure_connected end |
#_evaluate_handle(flag_id, default, context) ⇒ Object
Core evaluation used by flag handles (the .get path).
Connects lazily on first use so flag.get works without an explicit install step.
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 |
# File 'lib/smplkit/flags/client.rb', line 424 def _evaluate_handle(flag_id, default, context) ensure_connected if context # Explicit context: register here. (Implicit set_context registers at # the entry point, so the request-context branch below doesn't need # to.) @contexts&.register(context) eval_dict = Flags.contexts_to_eval_dict(context) else contexts = Smplkit.request_context eval_dict = contexts.empty? ? {} : Flags.contexts_to_eval_dict(contexts) end # Auto-inject service context if set and not already provided. eval_dict["service"] = { "key" => @service } if @service && !eval_dict.key?("service") ctx_hash = Flags.hash_context(eval_dict) cache_key = "#{flag_id}:#{ctx_hash}" hit, cached_value = @cache.get(cache_key) if hit @metrics&.record("flags.cache_hits", unit: "hits") @metrics&.record("flags.evaluations", unit: "evaluations", dimensions: { "flag" => flag_id }) return cached_value end flag_def = @flag_store[flag_id] if flag_def.nil? @cache.put(cache_key, default) return default end value = evaluate_flag(flag_def, @environment, eval_dict) value = default if value.nil? @cache.put(cache_key, value) @metrics&.record("flags.cache_misses", unit: "misses") @metrics&.record("flags.evaluations", unit: "evaluations", dimensions: { "flag" => flag_id }) value end |
#_update_flag(flag) ⇒ Object
270 271 272 273 |
# File 'lib/smplkit/flags/client.rb', line 270 def _update_flag(flag) response = ApiSupport::ErrorMapping.call { @api.update_flag(flag.id, flag_body(flag)) } model_from_resource(ApiSupport::ResourceShim.from_model(response.data)) end |
#boolean_flag(id, default:) ⇒ Object
Declare a boolean flag handle for live evaluation. Connects lazily on first use.
322 323 324 325 326 327 328 |
# File 'lib/smplkit/flags/client.rb', line 322 def boolean_flag(id, default:) ensure_connected handle = BooleanFlag.new(self, id: id, name: id, type: "BOOLEAN", default: default) @handles[id] = handle observe_declaration(id, "BOOLEAN", default) handle end |
#close ⇒ Object Also known as: _close
Release resources — only those this client owns.
Tears down the owned WebSocket (standalone install). A wired client borrows the parent’s transport, WebSocket, and contexts client and closes none of them.
398 399 400 401 402 403 404 405 406 407 |
# File 'lib/smplkit/flags/client.rb', line 398 def close if @owns_ws && @ws_manager @ws_manager.stop @ws_manager = nil @owns_ws = false end # Owned flags/app transports (standalone construction) release their # Faraday connections on GC; there is no explicit shutdown to call. nil end |
#delete(id) ⇒ Object
Delete a flag by id.
260 261 262 263 |
# File 'lib/smplkit/flags/client.rb', line 260 def delete(id) ApiSupport::ErrorMapping.call { @api.delete_flag(id) } nil end |
#flush ⇒ Object
POST pending declarations to the flags bulk endpoint.
Items remain in the buffer until the request succeeds, so a flush against an unhealthy flags service is automatically retried by the next flush call (periodic background flush, install retry, or final flush on close).
298 299 300 301 302 303 304 305 |
# File 'lib/smplkit/flags/client.rb', line 298 def flush batch = @buffer.peek return if batch.empty? body = build_flag_bulk_request(batch) ApiSupport::ErrorMapping.call { @api.bulk_register_flags(body) } @buffer.commit(batch.map { |b| b["id"] }) end |
#flush_sync ⇒ Object
Synchronous flush — alias of flush for the periodic-flush path.
308 309 310 |
# File 'lib/smplkit/flags/client.rb', line 308 def flush_sync flush end |
#get(id) ⇒ Object
Fetch the editable Flag resource by id.
245 246 247 248 |
# File 'lib/smplkit/flags/client.rb', line 245 def get(id) response = ApiSupport::ErrorMapping.call { @api.get_flag(id) } model_from_resource(ApiSupport::ResourceShim.from_model(response.data)) end |
#json_flag(id, default:) ⇒ Object
Declare a JSON flag handle for live evaluation. Connects lazily on first use.
349 350 351 352 353 354 355 |
# File 'lib/smplkit/flags/client.rb', line 349 def json_flag(id, default:) ensure_connected handle = JsonFlag.new(self, id: id, name: id, type: "JSON", default: default) @handles[id] = handle observe_declaration(id, "JSON", default) handle end |
#list(page_number: nil, page_size: nil) ⇒ Object
List flags for the authenticated account.
251 252 253 254 255 256 257 |
# File 'lib/smplkit/flags/client.rb', line 251 def list(page_number: nil, page_size: nil) opts = {} opts[:page_number] = page_number unless page_number.nil? opts[:page_size] = page_size unless page_size.nil? response = ApiSupport::ErrorMapping.call { @api.list_flags(opts) } (response.data || []).map { |r| model_from_resource(ApiSupport::ResourceShim.from_model(r)) } end |
#new_boolean_flag(id, default:, name: nil, description: nil) ⇒ Object
Return a new unsaved boolean BooleanFlag. Call save to persist.
211 212 213 214 215 216 217 218 |
# File 'lib/smplkit/flags/client.rb', line 211 def new_boolean_flag(id, default:, name: nil, description: nil) BooleanFlag.new( self, id: id, name: name || Smplkit::Helpers.key_to_display_name(id), type: "BOOLEAN", default: default, values: [FlagValue.new(name: "True", value: true), FlagValue.new(name: "False", value: false)], description: description ) end |
#new_json_flag(id, default:, name: nil, description: nil, values: nil) ⇒ Object
Return a new unsaved JSON JsonFlag. Call save to persist.
237 238 239 240 241 242 |
# File 'lib/smplkit/flags/client.rb', line 237 def new_json_flag(id, default:, name: nil, description: nil, values: nil) JsonFlag.new( self, id: id, name: name || Smplkit::Helpers.key_to_display_name(id), type: "JSON", default: default, values: values, description: description ) end |
#new_number_flag(id, default:, name: nil, description: nil, values: nil) ⇒ Object
Return a new unsaved numeric NumberFlag. Call save to persist.
229 230 231 232 233 234 |
# File 'lib/smplkit/flags/client.rb', line 229 def new_number_flag(id, default:, name: nil, description: nil, values: nil) NumberFlag.new( self, id: id, name: name || Smplkit::Helpers.key_to_display_name(id), type: "NUMERIC", default: default, values: values, description: description ) end |
#new_string_flag(id, default:, name: nil, description: nil, values: nil) ⇒ Object
Return a new unsaved string StringFlag. Call save to persist.
221 222 223 224 225 226 |
# File 'lib/smplkit/flags/client.rb', line 221 def new_string_flag(id, default:, name: nil, description: nil, values: nil) StringFlag.new( self, id: id, name: name || Smplkit::Helpers.key_to_display_name(id), type: "STRING", default: default, values: values, description: description ) end |
#number_flag(id, default:) ⇒ Object
Declare a numeric flag handle for live evaluation. Connects lazily on first use.
340 341 342 343 344 345 346 |
# File 'lib/smplkit/flags/client.rb', line 340 def number_flag(id, default:) ensure_connected handle = NumberFlag.new(self, id: id, name: id, type: "NUMERIC", default: default) @handles[id] = handle observe_declaration(id, "NUMERIC", default) handle end |
#on_change(flag_id = nil, &block) ⇒ Object
Register a change listener.
client.flags.on_change { |event| ... } # global
client.flags.on_change("checkout-v2") { |e| ... } # flag-scoped
Connects lazily on first use — no explicit install step.
381 382 383 384 385 386 387 388 389 390 391 |
# File 'lib/smplkit/flags/client.rb', line 381 def on_change(flag_id = nil, &block) ensure_connected raise ArgumentError, "on_change requires a block" unless block if flag_id.nil? @global_listeners << block else @key_listeners[flag_id] << block end block end |
#pending_count ⇒ Object
Number of pending flag declarations awaiting flush.
313 314 315 |
# File 'lib/smplkit/flags/client.rb', line 313 def pending_count @buffer.pending_count end |
#refresh ⇒ Object
Re-fetch all flag definitions and clear cache.
Connects lazily on first use — no explicit install step.
364 365 366 367 |
# File 'lib/smplkit/flags/client.rb', line 364 def refresh ensure_connected do_refresh("manual") end |
#register(items, flush: false) ⇒ Object
Buffer flag declarations for bulk-discovery upload; optionally flush now.
280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/smplkit/flags/client.rb', line 280 def register(items, flush: false) batch = items.is_a?(Array) ? items : [items] batch.each { |d| @buffer.add(d) } if flush self.flush return end return unless @buffer.pending_count >= FLAG_BATCH_FLUSH_SIZE Thread.new { threshold_flush } end |
#stats ⇒ Object
Return evaluation statistics. Connects lazily on first use.
370 371 372 373 |
# File 'lib/smplkit/flags/client.rb', line 370 def stats ensure_connected FlagStats.new(cache_hits: @cache.cache_hits, cache_misses: @cache.cache_misses) end |
#string_flag(id, default:) ⇒ Object
Declare a string flag handle for live evaluation. Connects lazily on first use.
331 332 333 334 335 336 337 |
# File 'lib/smplkit/flags/client.rb', line 331 def string_flag(id, default:) ensure_connected handle = StringFlag.new(self, id: id, name: id, type: "STRING", default: default) @handles[id] = handle observe_declaration(id, "STRING", default) handle end |