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 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, 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:) ⇒ 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.
-
#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, 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.
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/smplkit/flags/client.rb', line 189 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) {|FlagsClient| ... } ⇒ Object
Construct, yield to the block, and close on exit.
537 538 539 540 541 542 543 544 |
# File 'lib/smplkit/flags/client.rb', line 537 def self.open(**kwargs) client = new(**kwargs) begin yield client ensure client.close end end |
Instance Method Details
#_create_flag(flag) ⇒ Object
343 344 345 346 |
# File 'lib/smplkit/flags/client.rb', line 343 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.
591 592 593 |
# File 'lib/smplkit/flags/client.rb', line 591 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.
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 |
# File 'lib/smplkit/flags/client.rb', line 550 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
348 349 350 351 |
# File 'lib/smplkit/flags/client.rb', line 348 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.
419 420 421 422 423 424 425 |
# File 'lib/smplkit/flags/client.rb', line 419 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.
521 522 523 524 525 526 527 528 529 530 |
# File 'lib/smplkit/flags/client.rb', line 521 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.
338 339 340 341 |
# File 'lib/smplkit/flags/client.rb', line 338 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).
386 387 388 389 390 391 392 393 |
# File 'lib/smplkit/flags/client.rb', line 386 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.
398 399 400 |
# File 'lib/smplkit/flags/client.rb', line 398 def flush_sync flush end |
#get(id) ⇒ Flag
Fetch the editable Flag resource by id.
313 314 315 316 |
# File 'lib/smplkit/flags/client.rb', line 313 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.
461 462 463 464 465 466 467 |
# File 'lib/smplkit/flags/client.rb', line 461 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.
325 326 327 328 329 330 331 |
# File 'lib/smplkit/flags/client.rb', line 325 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.
242 243 244 245 246 247 248 249 |
# File 'lib/smplkit/flags/client.rb', line 242 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.
301 302 303 304 305 306 |
# File 'lib/smplkit/flags/client.rb', line 301 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.
282 283 284 285 286 287 |
# File 'lib/smplkit/flags/client.rb', line 282 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.
263 264 265 266 267 268 |
# File 'lib/smplkit/flags/client.rb', line 263 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.
447 448 449 450 451 452 453 |
# File 'lib/smplkit/flags/client.rb', line 447 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.
502 503 504 505 506 507 508 509 510 511 512 |
# File 'lib/smplkit/flags/client.rb', line 502 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.
405 406 407 |
# File 'lib/smplkit/flags/client.rb', line 405 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.
478 479 480 481 |
# File 'lib/smplkit/flags/client.rb', line 478 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.
366 367 368 369 370 371 372 373 374 375 376 |
# File 'lib/smplkit/flags/client.rb', line 366 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 ⇒ FlagStats
Return evaluation statistics. Connects lazily on first use.
486 487 488 489 |
# File 'lib/smplkit/flags/client.rb', line 486 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.
433 434 435 436 437 438 439 |
# File 'lib/smplkit/flags/client.rb', line 433 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 |