Class: FeatBit::Client

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = Options.new) ⇒ Client

Returns a new instance of Client.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/featbit/client.rb', line 9

def initialize(options = Options.new)
  @options = options.is_a?(Options) ? options : Options.new(offline: true)
  @data_store = @options.data_store || InMemoryDataStore.new
  initial_status = @options.offline ? Status::OFFLINE : Status::STARTING
  @status_provider = StatusProvider.new(initial_status, logger: @options.logger)
  @listeners = {}
  @listener_id = 0
  @listener_mutex = Mutex.new
  @lifecycle_mutex = Mutex.new
  @closed = false
  @close_result = nil

  load_bootstrap(@options.bootstrap) if @options.bootstrap
  @event_processor = build_event_processor
  @evaluator = Evaluator.new(
    flag_getter: ->(key) { @data_store.flag(key) },
    segment_getter: ->(id) { @data_store.segment(id) },
    logger: @options.logger
  )
  start_synchronizer unless @options.offline
  @status_provider.update(Status::READY) if @options.offline && @data_store.initialized?
  @status_provider.wait_until_ready(@options.start_wait) if !@options.offline && @options.start_wait.positive?
rescue StandardError => e
  safe_log(:error, "FeatBit client initialization failed: #{e.message}")
  @status_provider ||= StatusProvider.new(Status::FAILED)
  @status_provider.update(Status::FAILED, message: e.message)
  @event_processor ||= NullEventProcessor.new
end

Instance Attribute Details

#data_storeObject (readonly)

Returns the value of attribute data_store.



7
8
9
# File 'lib/featbit/client.rb', line 7

def data_store
  @data_store
end

#event_processorObject (readonly)

Returns the value of attribute event_processor.



7
8
9
# File 'lib/featbit/client.rb', line 7

def event_processor
  @event_processor
end

#optionsObject (readonly)

Returns the value of attribute options.



7
8
9
# File 'lib/featbit/client.rb', line 7

def options
  @options
end

#status_providerObject (readonly)

Returns the value of attribute status_provider.



7
8
9
# File 'lib/featbit/client.rb', line 7

def status_provider
  @status_provider
end

Instance Method Details

#add_flag_change_listener(callable = nil, &block) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/featbit/client.rb', line 105

def add_flag_change_listener(callable = nil, &block)
  listener = callable || block
  return nil unless listener.respond_to?(:call)

  @listener_mutex.synchronize do
    @listener_id += 1
    @listeners[@listener_id] = listener
    @listener_id
  end
rescue StandardError
  nil
end

#bool_variation(flag_key, user, default_value = false) ⇒ Object



63
64
65
# File 'lib/featbit/client.rb', line 63

def bool_variation(flag_key, user, default_value = false)
  typed_variation(flag_key, user, default_value, [TrueClass, FalseClass])
end

#closeObject Also known as: stop



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/featbit/client.rb', line 124

def close
  @lifecycle_mutex.synchronize do
    return @close_result unless @close_result.nil?
    return true if @closed

    @closed = true
  end

  results = [safe_close(@synchronizer), safe_close(@event_processor)]
  results << safe_status_update(Status::CLOSED)
  @lifecycle_mutex.synchronize { @close_result = results.all? }
rescue StandardError => e
  safe_log(:warn, "FeatBit client close failed: #{e.message}")
  false
end

#flushObject



99
100
101
102
103
# File 'lib/featbit/client.rb', line 99

def flush
  @event_processor.flush
rescue StandardError
  false
end

#initialized?Boolean

Returns:

  • (Boolean)


38
39
40
41
42
# File 'lib/featbit/client.rb', line 38

def initialized?
  @data_store.initialized?
rescue StandardError
  false
end

#json_variation(flag_key, user, default_value = {}) ⇒ Object



75
76
77
# File 'lib/featbit/client.rb', line 75

def json_variation(flag_key, user, default_value = {})
  typed_variation(flag_key, user, default_value, [Hash, Array])
end

#number_variation(flag_key, user, default_value = 0) ⇒ Object



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

def number_variation(flag_key, user, default_value = 0)
  typed_variation(flag_key, user, default_value, [Numeric])
end

#remove_flag_change_listener(id) ⇒ Object



118
119
120
121
122
# File 'lib/featbit/client.rb', line 118

def remove_flag_change_listener(id)
  @listener_mutex.synchronize { !@listeners.delete(id).nil? }
rescue StandardError
  false
end

#string_variation(flag_key, user, default_value = "") ⇒ Object



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

def string_variation(flag_key, user, default_value = "")
  typed_variation(flag_key, user, default_value, [String])
end

#track(user, event_name, numeric_value = 1.0) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/featbit/client.rb', line 79

def track(user, event_name, numeric_value = 1.0)
  normalized_user = normalize_user(user)
  return false unless normalized_user.valid? && !event_name.to_s.empty?

  @event_processor.enqueue(
    user: normalized_user.to_h,
    metrics: [{
      eventName: event_name.to_s,
      numericValue: numeric_value.to_f,
      route: "index/metric",
      type: "CustomEvent",
      appType: "ruby-server-side",
      timestamp: timestamp_ms
    }]
  )
rescue StandardError => e
  safe_log(:warn, "FeatBit track failed: #{e.message}")
  false
end

#variation(flag_key, user, default_value) ⇒ Object



44
45
46
47
48
49
# File 'lib/featbit/client.rb', line 44

def variation(flag_key, user, default_value)
  variation_detail(flag_key, user, default_value).value
rescue StandardError => e
  safe_log(:error, "FeatBit variation failed: #{e.message}")
  default_value
end

#variation_detail(flag_key, user, default_value) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
# File 'lib/featbit/client.rb', line 51

def variation_detail(flag_key, user, default_value)
  return safe_detail(flag_key, default_value, :client_not_ready) if @closed || !evaluable?

  normalized_user = normalize_user(user)
  detail = @evaluator.evaluate(flag_key, normalized_user, default_value)
  enqueue_evaluation(detail, normalized_user) if detail.success?
  detail
rescue StandardError => e
  safe_log(:error, "FeatBit variation detail failed: #{e.message}")
  safe_detail(flag_key, default_value, :error, e.message)
end