Class: Feat::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/feat/client.rb

Overview

Polling HTTP client. Uses stdlib only - zero gem dependencies.

Constant Summary collapse

DEFAULT_POLL_INTERVAL =
30.0
MIN_POLL_INTERVAL =
5.0
MAX_DATAFILE_BYTES =
10 * 1024 * 1024
OPEN_TIMEOUT_SECONDS =
3
READ_TIMEOUT_SECONDS =
10
RETRYABLE_CONNECT_ERRORS =
[
  Net::OpenTimeout,
  Errno::ETIMEDOUT,
  Errno::ECONNREFUSED,
  Errno::EHOSTUNREACH,
  Errno::ENETUNREACH,
].freeze

Instance Method Summary collapse

Constructor Details

#initialize(api_key:, data_plane_url:, poll_interval: DEFAULT_POLL_INTERVAL, http_client: nil) ⇒ Client

Returns a new instance of Client.

Raises:

  • (ArgumentError)


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/feat/client.rb', line 23

def initialize(api_key:, data_plane_url:, poll_interval: DEFAULT_POLL_INTERVAL, http_client: nil)
  raise ArgumentError, "api_key is required" if api_key.nil? || api_key.empty?
  raise ArgumentError, "data_plane_url is required" if data_plane_url.nil? || data_plane_url.empty?

  assert_https_url!(data_plane_url)

  @api_key       = api_key
  @data_plane_url = data_plane_url.chomp("/")
  @poll_interval = [poll_interval.to_f, MIN_POLL_INTERVAL].max
  @http_client   = http_client
  @datafile      = nil
  @etag          = nil
  @mutex         = Mutex.new
  @stop          = false
  @thread        = nil
  @sticky_ip     = nil
end

Instance Method Details

#closeObject



47
48
49
# File 'lib/feat/client.rb', line 47

def close
  @stop = true
end

#evaluate(flag_key, default_value, ctx) ⇒ Object



55
56
57
58
59
60
61
62
63
64
# File 'lib/feat/client.rb', line 55

def evaluate(flag_key, default_value, ctx)
  df = @datafile
  if df.nil?
    return EvaluationResult.new(
      value: default_value, reason: Reason::ERROR,
      error_message: "client not ready: call #start before #evaluate"
    )
  end
  Eval.call(flag_key: flag_key, default_value: default_value, ctx: ctx, datafile: df)
end

#get_boolean_value(flag_key, default, ctx) ⇒ Object



66
67
68
69
# File 'lib/feat/client.rb', line 66

def get_boolean_value(flag_key, default, ctx)
  r = evaluate(flag_key, default, ctx)
  r.value == true || r.value == false ? r.value : default
end

#get_number_value(flag_key, default, ctx) ⇒ Object



76
77
78
79
# File 'lib/feat/client.rb', line 76

def get_number_value(flag_key, default, ctx)
  r = evaluate(flag_key, default, ctx)
  r.value.is_a?(Numeric) && !(r.value == true || r.value == false) ? r.value : default
end

#get_object_value(flag_key, default, ctx) ⇒ Object



81
82
83
84
# File 'lib/feat/client.rb', line 81

def get_object_value(flag_key, default, ctx)
  r = evaluate(flag_key, default, ctx)
  r.value
end

#get_string_value(flag_key, default, ctx) ⇒ Object



71
72
73
74
# File 'lib/feat/client.rb', line 71

def get_string_value(flag_key, default, ctx)
  r = evaluate(flag_key, default, ctx)
  r.value.is_a?(String) ? r.value : default
end

#refreshObject



51
52
53
# File 'lib/feat/client.rb', line 51

def refresh
  fetch_once
end

#startObject

Blocking initial fetch; spawns a background poller thread.



42
43
44
45
# File 'lib/feat/client.rb', line 42

def start
  refresh
  @thread ||= Thread.new { poll_loop }
end