Class: Smplkit::Flags::FlagsClient
- Inherits:
-
Object
- Object
- Smplkit::Flags::FlagsClient
- Defined in:
- lib/smplkit/flags/client.rb,
sig/smplkit/flags.rbs
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 CRUD 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) {|FlagsClient| ... } ⇒ 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:) ⇒ BooleanFlag
Declare a boolean flag handle for live evaluation.
-
#close ⇒ void
(also: #_close)
Release resources — only those this client owns.
-
#delete(id) ⇒ void
Delete a flag by id.
-
#flush ⇒ void
POST pending declarations to the flags bulk endpoint.
-
#flush_sync ⇒ void
Synchronous flush — alias of
flushfor the periodic-flush path. -
#get(id) ⇒ Flag
Fetch the editable
Flagresource by id. -
#initialize(api_key = nil, environment: nil, service: nil, base_url: nil, profile: nil, base_domain: nil, scheme: nil, debug: nil, extra_headers: nil, streaming: true, parent: nil, transport: nil, contexts: nil, metrics: nil) ⇒ FlagsClient
constructor
A new instance of FlagsClient.
-
#json_flag(id, default:) ⇒ JsonFlag
Declare a JSON flag handle for live evaluation.
-
#list(page_number: nil, page_size: nil) ⇒ Array<Flag>
List flags for the authenticated account.
-
#new_boolean_flag(id, default:, name: nil, description: nil) ⇒ BooleanFlag
Return a new unsaved boolean
BooleanFlag. -
#new_json_flag(id, default:, name: nil, description: nil, values: nil) ⇒ JsonFlag
Return a new unsaved JSON
JsonFlag. -
#new_number_flag(id, default:, name: nil, description: nil, values: nil) ⇒ NumberFlag
Return a new unsaved numeric
NumberFlag. -
#new_string_flag(id, default:, name: nil, description: nil, values: nil) ⇒ StringFlag
Return a new unsaved string
StringFlag. -
#number_flag(id, default:) ⇒ NumberFlag
Declare a numeric flag handle for live evaluation.
-
#on_change(flag_id = nil) {|FlagChangeEvent| ... } ⇒ Proc
Register a change listener.
-
#pending_count ⇒ Integer
Number of pending flag declarations awaiting flush.
-
#refresh ⇒ void
Re-fetch all flag definitions and clear cache.
-
#register(items, flush: false) ⇒ void
Buffer flag declarations for bulk-discovery upload; optionally flush now.
- #start ⇒ void
-
#stats ⇒ FlagStats
Return evaluation statistics.
-
#string_flag(id, default:) ⇒ StringFlag
Declare a string flag handle for live evaluation.
Constructor Details
#initialize(api_key = nil, environment: nil, service: nil, base_url: nil, profile: nil, base_domain: nil, scheme: nil, debug: nil, extra_headers: nil, streaming: true, parent: nil, transport: nil, contexts: nil, metrics: nil) ⇒ FlagsClient
Returns a new instance of FlagsClient.
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
# File 'lib/smplkit/flags/client.rb', line 226 def initialize(api_key = nil, environment: nil, service: nil, base_url: nil, profile: nil, base_domain: nil, scheme: nil, debug: nil, extra_headers: nil, streaming: true, parent: nil, transport: nil, contexts: nil, metrics: nil) @parent = parent @metrics = metrics @streaming = streaming ? true : false @standalone_api_key = nil if transport.nil? # Standalone: resolve like Smplkit::Client — defaults → ~/.smplkit → # SMPLKIT_* env vars → constructor args — environment and service # included. @flags_http, app_http, @app_base_url, @standalone_api_key, resolved_env, resolved_service = Flags.flags_transport( api_key: api_key, base_url: base_url, profile: profile, base_domain: base_domain, scheme: scheme, environment: environment, service: service, debug: debug, extra_headers: extra_headers ) @environment = parent.nil? ? resolved_env : parent._environment @service = parent.nil? ? resolved_service : parent._service # 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: the parent has already resolved environment/service once — # its values win over both the raw kwargs and re-resolution. @environment = parent.nil? ? environment : parent._environment @service = parent.nil? ? service : parent._service # 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) {|FlagsClient| ... } ⇒ Object
Construct, yield to the block, and close on exit.
590 591 592 593 594 595 596 597 |
# File 'lib/smplkit/flags/client.rb', line 590 def self.open(**kwargs) client = new(**kwargs) begin yield client ensure client.close end end |
Instance Method Details
#_create_flag(flag) ⇒ Object
390 391 392 393 |
# File 'lib/smplkit/flags/client.rb', line 390 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.
644 645 646 |
# File 'lib/smplkit/flags/client.rb', line 644 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.
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 |
# File 'lib/smplkit/flags/client.rb', line 603 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
395 396 397 398 |
# File 'lib/smplkit/flags/client.rb', line 395 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:) ⇒ BooleanFlag
Declare a boolean flag handle for live evaluation. Connects lazily on first use.
472 473 474 475 476 477 478 |
# File 'lib/smplkit/flags/client.rb', line 472 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 ⇒ void Also known as: _close
This method returns an undefined value.
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.
574 575 576 577 578 579 580 581 582 583 |
# File 'lib/smplkit/flags/client.rb', line 574 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) ⇒ void
This method returns an undefined value.
Delete a flag by id.
385 386 387 388 |
# File 'lib/smplkit/flags/client.rb', line 385 def delete(id) ApiSupport::ErrorMapping.call { @api.delete_flag(id) } nil end |
#flush ⇒ void
This method returns an undefined value.
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).
439 440 441 442 443 444 445 446 |
# File 'lib/smplkit/flags/client.rb', line 439 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 ⇒ void
This method returns an undefined value.
Synchronous flush — alias of flush for the periodic-flush path.
451 452 453 |
# File 'lib/smplkit/flags/client.rb', line 451 def flush_sync flush end |
#get(id) ⇒ Flag
Fetch the editable Flag resource by id.
360 361 362 363 |
# File 'lib/smplkit/flags/client.rb', line 360 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:) ⇒ JsonFlag
Declare a JSON flag handle for live evaluation. Connects lazily on first use.
514 515 516 517 518 519 520 |
# File 'lib/smplkit/flags/client.rb', line 514 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) ⇒ Array<Flag>
List flags for the authenticated account.
372 373 374 375 376 377 378 |
# File 'lib/smplkit/flags/client.rb', line 372 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) ⇒ BooleanFlag
Return a new unsaved boolean BooleanFlag. Call save to persist.
289 290 291 292 293 294 295 296 |
# File 'lib/smplkit/flags/client.rb', line 289 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) ⇒ JsonFlag
Return a new unsaved JSON JsonFlag. Call save to persist.
348 349 350 351 352 353 |
# File 'lib/smplkit/flags/client.rb', line 348 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) ⇒ NumberFlag
Return a new unsaved numeric NumberFlag. Call save to persist.
329 330 331 332 333 334 |
# File 'lib/smplkit/flags/client.rb', line 329 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) ⇒ StringFlag
Return a new unsaved string StringFlag. Call save to persist.
310 311 312 313 314 315 |
# File 'lib/smplkit/flags/client.rb', line 310 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:) ⇒ NumberFlag
Declare a numeric flag handle for live evaluation. Connects lazily on first use.
500 501 502 503 504 505 506 |
# File 'lib/smplkit/flags/client.rb', line 500 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) {|FlagChangeEvent| ... } ⇒ Proc
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.
555 556 557 558 559 560 561 562 563 564 565 |
# File 'lib/smplkit/flags/client.rb', line 555 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 ⇒ Integer
Number of pending flag declarations awaiting flush.
458 459 460 |
# File 'lib/smplkit/flags/client.rb', line 458 def pending_count @buffer.pending_count end |
#refresh ⇒ void
This method returns an undefined value.
Re-fetch all flag definitions and clear cache.
Connects lazily on first use — no explicit install step.
531 532 533 534 |
# File 'lib/smplkit/flags/client.rb', line 531 def refresh ensure_connected do_refresh("manual") end |
#register(items, flush: false) ⇒ void
This method returns an undefined value.
Buffer flag declarations for bulk-discovery upload; optionally flush now.
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
# File 'lib/smplkit/flags/client.rb', line 413 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 # Stateless mode (+streaming: false+) never spawns background threads — # the threshold flush runs inline (blocking) instead. if @streaming Thread.new { threshold_flush } else threshold_flush end end |
#start ⇒ void
This method returns an undefined value.
83 |
# File 'sig/smplkit/flags.rbs', line 83
def start: () -> void
|
#stats ⇒ FlagStats
Return evaluation statistics. Connects lazily on first use.
539 540 541 542 |
# File 'lib/smplkit/flags/client.rb', line 539 def stats ensure_connected FlagStats.new(cache_hits: @cache.cache_hits, cache_misses: @cache.cache_misses) end |
#string_flag(id, default:) ⇒ StringFlag
Declare a string flag handle for live evaluation. Connects lazily on first use.
486 487 488 489 490 491 492 |
# File 'lib/smplkit/flags/client.rb', line 486 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 |