Class: ToggleFleet::Client
- Inherits:
-
Object
- Object
- ToggleFleet::Client
- Defined in:
- lib/togglefleet.rb
Overview
A self-contained client. Use ToggleFleet.* for the process-wide singleton, or build your own (e.g. to talk to two environments at once): ToggleFleet::Client.new(config).
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Returns the value of attribute config.
Instance Method Summary collapse
-
#all(actor: nil, groups: nil) ⇒ Object
Snapshot every known flag for an actor — handy for bootstrapping a JS client.
-
#enabled?(flag, actor: nil, groups: nil) ⇒ Boolean
The whole point: evaluate locally, no network call here.
-
#initialize(config) ⇒ Client
constructor
A new instance of Client.
-
#register_group(name, &block) ⇒ Object
Register a group predicate.
-
#start ⇒ Object
Pull the config once and start the background refresh thread.
-
#stop ⇒ Object
Stop the background refresh thread.
-
#sync ⇒ Object
Force a refresh now (returns true if the config changed).
Constructor Details
#initialize(config) ⇒ Client
Returns a new instance of Client.
48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/togglefleet.rb', line 48 def initialize(config) @config = config @groups = {} # name => predicate proc @flags = {} # flag key => state hash @etag = nil @loaded = false @mutex = Mutex.new @poller = nil @poller_pid = nil # pid that owns @poller; threads do not survive fork @last_attempt = nil # monotonic time of the last fetch attempt (success or failure) end |
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
46 47 48 |
# File 'lib/togglefleet.rb', line 46 def config @config end |
Instance Method Details
#all(actor: nil, groups: nil) ⇒ Object
Snapshot every known flag for an actor — handy for bootstrapping a JS client.
129 130 131 132 133 |
# File 'lib/togglefleet.rb', line 129 def all(actor: nil, groups: nil) ensure_loaded keys = @mutex.synchronize { @flags.keys } keys.each_with_object({}) { |k, h| h[k] = enabled?(k, actor: actor, groups: groups) } end |
#enabled?(flag, actor: nil, groups: nil) ⇒ Boolean
The whole point: evaluate locally, no network call here.
116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/togglefleet.rb', line 116 def enabled?(flag, actor: nil, groups: nil) restart_poller_if_forked ensure_loaded state = @mutex.synchronize { @flags[flag.to_s] } result = state ? evaluate(state, actor, groups) : @config.default notify_evaluation(flag.to_s, actor, result) result rescue StandardError => e log("enabled?(#{flag}) error: #{e.class}: #{e.}") @config.default end |
#register_group(name, &block) ⇒ Object
Register a group predicate. Group membership is decided in YOUR code, so a flag enabled for :admins turns on for any actor where the block returns true.
62 63 64 65 66 |
# File 'lib/togglefleet.rb', line 62 def register_group(name, &block) raise ArgumentError, "register_group needs a block" unless block @mutex.synchronize { @groups[name.to_s] = block } self end |
#start ⇒ Object
Pull the config once and start the background refresh thread. Idempotent.
69 70 71 72 73 |
# File 'lib/togglefleet.rb', line 69 def start attempt_sync start_poller self end |
#stop ⇒ Object
Stop the background refresh thread. Safe to call more than once.
76 77 78 79 80 81 82 83 84 85 |
# File 'lib/togglefleet.rb', line 76 def stop thread = @mutex.synchronize do current = @poller @poller = nil @poller_pid = nil current end thread&.kill self end |
#sync ⇒ Object
Force a refresh now (returns true if the config changed).
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/togglefleet.rb', line 136 def sync uri = URI.join(@config.url + "/", "v1/config") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = uri.scheme == "https" http.open_timeout = @config.open_timeout http.read_timeout = @config.read_timeout req = Net::HTTP::Get.new(uri) req["Authorization"] = "Bearer #{@config.sdk_key}" req["If-None-Match"] = @etag if @etag req["User-Agent"] = "togglefleet-ruby/#{VERSION}" res = http.request(req) case res when Net::HTTPNotModified false when Net::HTTPSuccess flags = JSON.parse(res.body).fetch("flags", {}) @mutex.synchronize { @flags = flags; @etag = res["ETag"]; @loaded = true } true when Net::HTTPUnauthorized raise Error, "invalid SDK key (401) — check config.sdk_key" else raise Error, "config fetch failed: HTTP #{res.code}" end end |