Class: CruFlags::Client
- Inherits:
-
Object
- Object
- CruFlags::Client
- Defined in:
- lib/cru_flags/client.rb
Overview
The polling flag client (design doc §3–§8). One instance per process in normal use, via the CruFlags module singleton.
Constant Summary collapse
- VALID_SCHEMES =
%w[http https].freeze
- MODES =
%w[background on-demand].freeze
Class Method Summary collapse
-
.url_from_env ⇒ Object
The one place CRU_FLAGS_URL is read and normalized (design doc §3:
url: nilmeans "read CRU_FLAGS_URL on first use").
Instance Method Summary collapse
- #close ⇒ Object
- #enabled?(name) ⇒ Boolean
-
#flags_for_adapter ⇒ Object
The Flipper adapter's read primitive: the frozen Flags hash (or {}), through the same read path as enabled? (lazy start / on-demand refresh).
- #inert? ⇒ Boolean
-
#initialize(url: nil, poll_seconds: 30.0, fetch_timeout: 2.0, on_error: nil, refresh_mode: nil) ⇒ Client
constructor
A new instance of Client.
-
#ready(timeout: nil) ⇒ Object
Blocks until the first fetch attempt completes (success or failure) and returns whether it did within timeout.
- #refresh(force: false) ⇒ Object
- #snapshot ⇒ Object
Constructor Details
#initialize(url: nil, poll_seconds: 30.0, fetch_timeout: 2.0, on_error: nil, refresh_mode: nil) ⇒ Client
Returns a new instance of Client.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/cru_flags/client.rb', line 24 def initialize(url: nil, poll_seconds: 30.0, fetch_timeout: 2.0, on_error: nil, refresh_mode: nil) @poll_seconds = positive(poll_seconds, 30.0) @fetch_timeout = positive(fetch_timeout, 2.0) @on_error = on_error || default_on_error @refresh_mode = resolve_mode(refresh_mode) @url = url || self.class.url_from_env @document = nil @etag = nil @healthy = true @attempted = false @last_attempt_at = nil @last_attempt_ok = false @closed = false @write_mutex = Mutex.new @fetch_mutex = Mutex.new @ready_mutex = Mutex.new @ready_cv = ConditionVariable.new @wake = Queue.new @thread = nil @pid = nil validate_url! end |
Class Method Details
.url_from_env ⇒ Object
The one place CRU_FLAGS_URL is read and normalized (design doc §3:
url: nil means "read CRU_FLAGS_URL on first use"). It lives on the
Client, not on the module singleton, so a directly-constructed Client
honors the documented contract instead of being silently inert; the
module singleton gets the same normalization by construction.
19 20 21 22 |
# File 'lib/cru_flags/client.rb', line 19 def self.url_from_env value = ENV[ENV_VAR].to_s.strip value.empty? ? nil : value end |
Instance Method Details
#close ⇒ Object
117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/cru_flags/client.rb', line 117 def close @closed = true @wake.push(:stop) @ready_mutex.synchronize { @ready_cv.broadcast } begin @thread&.join(0.1) rescue nil # close called from the poller thread itself must not raise ThreadError end nil end |
#enabled?(name) ⇒ Boolean
50 51 52 53 54 55 56 |
# File 'lib/cru_flags/client.rb', line 50 def enabled?(name) read_path_touch doc = @document doc&.dig("Flags", name.to_s, "Enabled") == true rescue false end |
#flags_for_adapter ⇒ Object
The Flipper adapter's read primitive: the frozen Flags hash (or {}), through the same read path as enabled? (lazy start / on-demand refresh).
69 70 71 72 73 74 |
# File 'lib/cru_flags/client.rb', line 69 def flags_for_adapter read_path_touch @document&.fetch("Flags", nil) || {} rescue {} end |
#inert? ⇒ Boolean
48 |
# File 'lib/cru_flags/client.rb', line 48 def inert? = @url.nil? |
#ready(timeout: nil) ⇒ Object
Blocks until the first fetch attempt completes (success or failure) and returns whether it did within timeout. Immediately false for inert clients (design doc §3.3, §7).
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/cru_flags/client.rb', line 79 def ready(timeout: nil) return false if inert? || (@closed && !@attempted) ensure_started return on_demand_ready if on_demand? @ready_mutex.synchronize do deadline = timeout && Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout until @attempted ensure_started # self-heals a poller that died mid-wait, rather than waiting on it forever remaining = deadline && deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC) return false if remaining && remaining <= 0 @ready_cv.wait(@ready_mutex, remaining || 1.0) return false if @closed && !@attempted end true end rescue false end |
#refresh(force: false) ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/cru_flags/client.rb', line 98 def refresh(force: false) return false if inert? || @closed waited_from = Process.clock_gettime(Process::CLOCK_MONOTONIC) begin @fetch_mutex.synchronize do if force attempt_fetch unless @last_attempt_at && @last_attempt_at >= waited_from elsif stale? attempt_fetch end end ensure signal_ready end @attempted && @last_attempt_ok rescue false end |
#snapshot ⇒ Object
58 59 60 61 62 63 64 |
# File 'lib/cru_flags/client.rb', line 58 def snapshot read_path_touch doc = @document doc ? JSON.parse(JSON.generate(doc)) : {} rescue {} end |