Class: Quonfig::Client
- Inherits:
-
Object
- Object
- Quonfig::Client
- Defined in:
- lib/quonfig/client.rb
Overview
Public Quonfig SDK client.
Wires the JSON stack: Quonfig::ConfigStore + Quonfig::Evaluator + Quonfig::Resolver. Three modes are supported:
-
datadir:(offline) – load a workspace from the local filesystem. -
store:(test harness) – caller-supplied ConfigStore, no I/O. -
network mode (default) – HTTP fetch from
api_urlspopulates the ConfigStore, then (if enabled) an SSE subscription keeps it live.
Network mode is the happy path for production SDK usage. The protobuf stack was retired in qfg-dk6.32; HTTP + SSE were wired back through Client in qfg-s7h.
Constant Summary collapse
- LOG =
Quonfig::InternalLogger.new(self)
Instance Attribute Summary collapse
-
#config_loader ⇒ Object
readonly
Returns the value of attribute config_loader.
-
#evaluator ⇒ Object
readonly
Returns the value of attribute evaluator.
-
#instance_hash ⇒ Object
readonly
Returns the value of attribute instance_hash.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
-
#resolver ⇒ Object
readonly
Returns the value of attribute resolver.
-
#store ⇒ Object
readonly
Returns the value of attribute store.
-
#telemetry_reporter ⇒ Object
readonly
Returns the value of attribute telemetry_reporter.
Instance Method Summary collapse
- #defined?(key) ⇒ Boolean
- #enabled?(feature_name, jit_context = NO_DEFAULT_PROVIDED) ⇒ Boolean
- #fork ⇒ Object
-
#get(key, default = NO_DEFAULT_PROVIDED, jit_context = NO_DEFAULT_PROVIDED) ⇒ Object
—- Lookup ——————————————————–.
- #get_bool(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
- #get_duration(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
- #get_float(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
- #get_int(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
- #get_json(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
- #get_string(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
- #get_string_list(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
-
#in_context(properties) ⇒ Object
—- Context binding ———————————————-.
-
#initialize(options = nil, store: nil, **option_kwargs) ⇒ Client
constructor
A new instance of Client.
- #inspect ⇒ Object
- #keys ⇒ Object
-
#logger_key ⇒ Object
The configured
logger_keyfrom Options — the Quonfig config key the higher-levelshould_log?helper evaluates per-logger. - #on_update(&block) ⇒ Object
-
#semantic_logger_filter(config_key:) ⇒ Object
—- Filters & helpers ——————————————–.
-
#should_log?(logger_path:, desired_level:, contexts: {}) ⇒ Boolean
Higher-level log-level check — a convenience on top of the primitive
get. -
#stdlib_formatter(logger_name: nil) ⇒ Proc
Build a formatter Proc for Ruby’s built-in
::Logger. - #stop ⇒ Object
- #with_context(properties, &block) ⇒ Object
Constructor Details
#initialize(options = nil, store: nil, **option_kwargs) ⇒ Client
Returns a new instance of Client.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/quonfig/client.rb', line 26 def initialize( = nil, store: nil, **option_kwargs) @options = if .is_a?(Quonfig::Options) elsif .is_a?(Hash) Quonfig::Options.new(.merge(option_kwargs)) else Quonfig::Options.new(option_kwargs) end @global_context = normalize_context(@options.global_context) @instance_hash = SecureRandom.uuid @store = store || Quonfig::ConfigStore.new @evaluator = Quonfig::Evaluator.new(@store, env_id: @options.environment) @resolver = Quonfig::Resolver.new(@store, @evaluator) @semantic_logger_filters = {} @sse_client = nil @poll_thread = nil @stopped = false @telemetry_reporter = nil # If the caller injected a store, we're in test/bootstrap mode; skip I/O. return if store if @options.datadir load_datadir_into_store else initialize_network_mode end initialize_telemetry end |
Instance Attribute Details
#config_loader ⇒ Object (readonly)
Returns the value of attribute config_loader.
23 24 25 |
# File 'lib/quonfig/client.rb', line 23 def config_loader @config_loader end |
#evaluator ⇒ Object (readonly)
Returns the value of attribute evaluator.
23 24 25 |
# File 'lib/quonfig/client.rb', line 23 def evaluator @evaluator end |
#instance_hash ⇒ Object (readonly)
Returns the value of attribute instance_hash.
23 24 25 |
# File 'lib/quonfig/client.rb', line 23 def instance_hash @instance_hash end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
23 24 25 |
# File 'lib/quonfig/client.rb', line 23 def @options end |
#resolver ⇒ Object (readonly)
Returns the value of attribute resolver.
23 24 25 |
# File 'lib/quonfig/client.rb', line 23 def resolver @resolver end |
#store ⇒ Object (readonly)
Returns the value of attribute store.
23 24 25 |
# File 'lib/quonfig/client.rb', line 23 def store @store end |
#telemetry_reporter ⇒ Object (readonly)
Returns the value of attribute telemetry_reporter.
23 24 25 |
# File 'lib/quonfig/client.rb', line 23 def telemetry_reporter @telemetry_reporter end |
Instance Method Details
#defined?(key) ⇒ Boolean
111 112 113 |
# File 'lib/quonfig/client.rb', line 111 def defined?(key) !@store.get(key).nil? end |
#enabled?(feature_name, jit_context = NO_DEFAULT_PROVIDED) ⇒ Boolean
106 107 108 109 |
# File 'lib/quonfig/client.rb', line 106 def enabled?(feature_name, jit_context = NO_DEFAULT_PROVIDED) value = get(feature_name, false, jit_context) value == true || value == 'true' end |
#fork ⇒ Object
248 249 250 |
# File 'lib/quonfig/client.rb', line 248 def fork self.class.new(@options.for_fork) end |
#get(key, default = NO_DEFAULT_PROVIDED, jit_context = NO_DEFAULT_PROVIDED) ⇒ Object
—- Lookup ——————————————————–
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/quonfig/client.rb', line 60 def get(key, default = NO_DEFAULT_PROVIDED, jit_context = NO_DEFAULT_PROVIDED) ctx = build_context(jit_context) record_context_for_telemetry(ctx) result = begin @resolver.get(key, ctx) rescue Quonfig::Errors::MissingDefaultError # The Resolver raises (matching Quonfig.get_or_raise semantics). # The Client's get applies the caller-provided default *or* the # configured on_no_default policy via handle_missing. nil end return handle_missing(key, default) if result.nil? record_evaluation_for_telemetry(result) result.unwrapped_value end |
#get_bool(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
90 91 92 |
# File 'lib/quonfig/client.rb', line 90 def get_bool(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) typed_get(key, :bool, default: default, context: context) end |
#get_duration(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
98 99 100 |
# File 'lib/quonfig/client.rb', line 98 def get_duration(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) typed_get(key, :duration, default: default, context: context) end |
#get_float(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
86 87 88 |
# File 'lib/quonfig/client.rb', line 86 def get_float(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) typed_get(key, Float, default: default, context: context) end |
#get_int(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
82 83 84 |
# File 'lib/quonfig/client.rb', line 82 def get_int(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) typed_get(key, Integer, default: default, context: context) end |
#get_json(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
102 103 104 |
# File 'lib/quonfig/client.rb', line 102 def get_json(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) typed_get(key, :json, default: default, context: context) end |
#get_string(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
78 79 80 |
# File 'lib/quonfig/client.rb', line 78 def get_string(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) typed_get(key, String, default: default, context: context) end |
#get_string_list(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
94 95 96 |
# File 'lib/quonfig/client.rb', line 94 def get_string_list(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) typed_get(key, :string_list, default: default, context: context) end |
#in_context(properties) ⇒ Object
—- Context binding ———————————————-
121 122 123 124 |
# File 'lib/quonfig/client.rb', line 121 def in_context(properties) bound = Quonfig::BoundClient.new(self, properties) block_given? ? yield(bound) : bound end |
#inspect ⇒ Object
252 253 254 |
# File 'lib/quonfig/client.rb', line 252 def inspect "#<Quonfig::Client:#{object_id} environment=#{@options.environment.inspect}>" end |
#keys ⇒ Object
115 116 117 |
# File 'lib/quonfig/client.rb', line 115 def keys @store.keys end |
#logger_key ⇒ Object
The configured logger_key from Options — the Quonfig config key the higher-level should_log? helper evaluates per-logger. nil if the client was not configured for dynamic log levels.
167 168 169 |
# File 'lib/quonfig/client.rb', line 167 def logger_key @options.logger_key end |
#on_update(&block) ⇒ Object
223 224 225 |
# File 'lib/quonfig/client.rb', line 223 def on_update(&block) @on_update = block end |
#semantic_logger_filter(config_key:) ⇒ Object
—- Filters & helpers ——————————————–
136 137 138 139 |
# File 'lib/quonfig/client.rb', line 136 def semantic_logger_filter(config_key:) @semantic_logger_filters[config_key] ||= Quonfig::SemanticLoggerFilter.new(self, config_key: config_key) end |
#should_log?(logger_path:, desired_level:, contexts: {}) ⇒ Boolean
Higher-level log-level check — a convenience on top of the primitive get. Evaluates the client’s logger_key config and returns whether a message at desired_level should be emitted for logger_path.
The SDK injects logger_path under the quonfig-sdk-logging named context with property key so a single log-level config can drive per-logger overrides via the normal rule engine (e.g. PROP_STARTS_WITH_ONE_OF “MyApp::Services::”).
logger_path is passed through verbatim — the SDK does not normalize it. Callers may pass any identifier shape their host language prefers (dotted, colon, slash, etc.) and author matching rules in the config against that exact shape.
Parallels sdk-node’s shouldLog({loggerPath}) and sdk-go’s ShouldLogPath.
Raises Quonfig::Error if logger_key was not set on the client —use semantic_logger_filter(config_key:) directly if you want to evaluate a specific key without declaring it at init time.
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/quonfig/client.rb', line 198 def should_log?(logger_path:, desired_level:, contexts: {}) unless logger_key raise Quonfig::Error, 'logger_key must be set at init to use should_log?(logger_path:, ...). ' \ 'Pass `logger_key:` to Quonfig::Options.new, or call ' \ 'semantic_logger_filter(config_key:) / get(config_key) directly.' end logger_context = { Quonfig::SemanticLoggerFilter::LOGGER_CONTEXT_NAME => { Quonfig::SemanticLoggerFilter::LOGGER_CONTEXT_KEY_PROP => logger_path } } merged = merge_contexts(normalize_context(contexts), logger_context) configured = get(logger_key, nil, merged) return true if configured.nil? desired_severity = Quonfig::SemanticLoggerFilter::LEVELS[normalize_log_level(desired_level)] || Quonfig::SemanticLoggerFilter::LEVELS[:debug] min_severity = Quonfig::SemanticLoggerFilter::LEVELS[normalize_log_level(configured)] || Quonfig::SemanticLoggerFilter::LEVELS[:debug] desired_severity >= min_severity end |
#stdlib_formatter(logger_name: nil) ⇒ Proc
Build a formatter Proc for Ruby’s built-in ::Logger. The returned proc honors dynamic log levels from the client’s logger_key config: for each log call, it evaluates should_log? and either formats the record or returns an empty string (suppressing output).
Matches ReforgeHQ’s stdlib_formatter API name (snake_case).
Usage:
logger = ::Logger.new($stdout)
logger.formatter = client.stdlib_formatter # uses progname
logger.formatter = client.stdlib_formatter(logger_name: 'MyApp') # fixed name
Raises Quonfig::Error if logger_key was not set at init — parallels should_log?‘s behavior.
160 161 162 |
# File 'lib/quonfig/client.rb', line 160 def stdlib_formatter(logger_name: nil) Quonfig::StdlibFormatter.build(self, logger_name: logger_name) end |
#stop ⇒ Object
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/quonfig/client.rb', line 227 def stop @stopped = true begin @sse_client&.close rescue StandardError => e LOG.debug "Error closing SSE client: #{e.}" end @sse_client = nil thread = @poll_thread @poll_thread = nil thread&.kill begin @telemetry_reporter&.stop rescue StandardError => e LOG.debug "Error stopping telemetry reporter: #{e.}" end @telemetry_reporter = nil end |
#with_context(properties, &block) ⇒ Object
126 127 128 129 130 131 132 |
# File 'lib/quonfig/client.rb', line 126 def with_context(properties, &block) if block_given? in_context(properties, &block) else Quonfig::BoundClient.new(self, properties) end end |