Class: Featurevisor::Instance

Inherits:
Object
  • Object
show all
Defined in:
lib/featurevisor/instance.rb

Overview

Instance class for managing feature flag evaluations

Constant Summary collapse

EMPTY_DATAFILE =

Empty datafile template

{
  schemaVersion: "2",
  revision: "unknown",
  segments: {},
  features: {}
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Instance

Initialize a new Featurevisor instance

Parameters:

  • options (Hash) (defaults to: {})

    Instance options

Options Hash (options):

  • :datafile (Hash, String)

    Datafile content or JSON string

  • :context (Hash)

    Initial context

  • :log_level (String)

    Log level

  • :logger (Logger)

    Logger instance

  • :sticky (Hash)

    Sticky features

  • :hooks (Array<Hook>)

    Array of hooks



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
# File 'lib/featurevisor/instance.rb', line 26

def initialize(options = {})
  # from options
  @context = options[:context] || {}
  @logger = options[:logger] || Featurevisor.create_logger(level: options[:log_level] || "info")
  @hooks_manager = Featurevisor::Hooks::HooksManager.new(
    hooks: (options[:hooks] || []).map { |hook_data| Featurevisor::Hooks::Hook.new(hook_data) },
    logger: @logger
  )
  @emitter = Featurevisor::Emitter.new
  @sticky = options[:sticky] || {}

  # datafile
  @datafile_reader = Featurevisor::DatafileReader.new(
    datafile: EMPTY_DATAFILE,
    logger: @logger
  )

  if options[:datafile]
    @datafile_reader = Featurevisor::DatafileReader.new(
      datafile: options[:datafile].is_a?(String) ? JSON.parse(options[:datafile], symbolize_names: true) : options[:datafile],
      logger: @logger
    )
  end

  @logger.info("Featurevisor SDK initialized")
end

Instance Attribute Details

#contextObject (readonly)

Returns the value of attribute context.



8
9
10
# File 'lib/featurevisor/instance.rb', line 8

def context
  @context
end

#datafile_readerObject (readonly)

Returns the value of attribute datafile_reader.



8
9
10
# File 'lib/featurevisor/instance.rb', line 8

def datafile_reader
  @datafile_reader
end

#emitterObject (readonly)

Returns the value of attribute emitter.



8
9
10
# File 'lib/featurevisor/instance.rb', line 8

def emitter
  @emitter
end

#hooks_managerObject (readonly)

Returns the value of attribute hooks_manager.



8
9
10
# File 'lib/featurevisor/instance.rb', line 8

def hooks_manager
  @hooks_manager
end

#loggerObject (readonly)

Returns the value of attribute logger.



8
9
10
# File 'lib/featurevisor/instance.rb', line 8

def logger
  @logger
end

#stickyObject (readonly)

Returns the value of attribute sticky.



8
9
10
# File 'lib/featurevisor/instance.rb', line 8

def sticky
  @sticky
end

Instance Method Details

#add_hook(hook) ⇒ Proc?

Add a hook

Parameters:

  • hook (Hook)

    Hook to add

Returns:

  • (Proc, nil)

    Remove function or nil if hook already exists



115
116
117
# File 'lib/featurevisor/instance.rb', line 115

def add_hook(hook)
  @hooks_manager.add(hook)
end

#closeObject

Close the instance



128
129
130
# File 'lib/featurevisor/instance.rb', line 128

def close
  @emitter.clear_all
end

#evaluate_flag(feature_key, context = {}, options = {}) ⇒ Hash

Evaluate a flag

Parameters:

  • feature_key (String)

    Feature key

  • context (Hash) (defaults to: {})

    Context

  • options (Hash) (defaults to: {})

    Override options

Returns:

  • (Hash)

    Evaluation result



184
185
186
187
188
189
190
191
# File 'lib/featurevisor/instance.rb', line 184

def evaluate_flag(feature_key, context = {}, options = {})
  Featurevisor::Evaluate.evaluate_with_hooks(
    get_evaluation_dependencies(context, options).merge(
      type: "flag",
      feature_key: feature_key
    )
  )
end

#evaluate_variable(feature_key, variable_key, context = {}, options = {}) ⇒ Hash

Evaluate a variable

Parameters:

  • feature_key (String)

    Feature key

  • variable_key (String)

    Variable key

  • context (Hash) (defaults to: {})

    Context

  • options (Hash) (defaults to: {})

    Override options

Returns:

  • (Hash)

    Evaluation result



250
251
252
253
254
255
256
257
258
# File 'lib/featurevisor/instance.rb', line 250

def evaluate_variable(feature_key, variable_key, context = {}, options = {})
  Featurevisor::Evaluate.evaluate_with_hooks(
    get_evaluation_dependencies(context, options).merge(
      type: "variable",
      feature_key: feature_key,
      variable_key: variable_key
    )
  )
end

#evaluate_variation(feature_key, context = {}, options = {}) ⇒ Hash

Evaluate a variation

Parameters:

  • feature_key (String)

    Feature key

  • context (Hash) (defaults to: {})

    Context

  • options (Hash) (defaults to: {})

    Override options

Returns:

  • (Hash)

    Evaluation result



213
214
215
216
217
218
219
220
# File 'lib/featurevisor/instance.rb', line 213

def evaluate_variation(feature_key, context = {}, options = {})
  Featurevisor::Evaluate.evaluate_with_hooks(
    get_evaluation_dependencies(context, options).merge(
      type: "variation",
      feature_key: feature_key
    )
  )
end

#get_all_evaluations(context = {}, feature_keys = [], options = {}) ⇒ Hash

Get all evaluations

Parameters:

  • context (Hash) (defaults to: {})

    Context

  • feature_keys (Array<String>) (defaults to: [])

    Feature keys to evaluate

  • options (Hash) (defaults to: {})

    Override options

Returns:

  • (Hash)

    All evaluations



369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/featurevisor/instance.rb', line 369

def get_all_evaluations(context = {}, feature_keys = [], options = {})
  result = {}

          keys = feature_keys.size > 0 ? feature_keys : @datafile_reader.get_feature_keys

  keys.each do |feature_key|
    # Convert symbol keys to strings for evaluation functions
    feature_key_str = feature_key.to_s

    # isEnabled
    evaluated_feature = {
      enabled: is_enabled(feature_key_str, context, options)
    }

    # variation
    if @datafile_reader.has_variations?(feature_key_str)
      variation = get_variation(feature_key_str, context, options)
      evaluated_feature[:variation] = variation if variation
    end

    # variables
    variable_keys = @datafile_reader.get_variable_keys(feature_key_str)
    if variable_keys.size > 0
      evaluated_feature[:variables] = {}

      variable_keys.each do |variable_key|
        evaluated_feature[:variables][variable_key] = get_variable(
          feature_key_str,
          variable_key,
          context,
          options
        )
      end
    end

    result[feature_key] = evaluated_feature
  end

  result
end

#get_context(context = nil) ⇒ Hash

Get context

Parameters:

  • context (Hash, nil) (defaults to: nil)

    Additional context to merge

Returns:

  • (Hash)

    Merged context



156
157
158
159
160
161
162
163
164
165
# File 'lib/featurevisor/instance.rb', line 156

def get_context(context = nil)
  if context
    {
      **@context,
      **context
    }
  else
    @context
  end
end

#get_feature(feature_key) ⇒ Hash?

Get a feature by key

Parameters:

  • feature_key (String)

    Feature key

Returns:

  • (Hash, nil)

    Feature data or nil if not found



108
109
110
# File 'lib/featurevisor/instance.rb', line 108

def get_feature(feature_key)
  @datafile_reader.get_feature(feature_key)
end

#get_revisionString

Get the revision

Returns:

  • (String)

    Revision string



101
102
103
# File 'lib/featurevisor/instance.rb', line 101

def get_revision
  @datafile_reader.get_revision
end

#get_variable(feature_key, variable_key, context = {}, options = {}) ⇒ Object?

Get variable value

Parameters:

  • feature_key (String)

    Feature key

  • variable_key (String)

    Variable key

  • context (Hash) (defaults to: {})

    Context

  • options (Hash) (defaults to: {})

    Override options

Returns:

  • (Object, nil)

    Variable value or nil



266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/featurevisor/instance.rb', line 266

def get_variable(feature_key, variable_key, context = {}, options = {})
  begin
    evaluation = evaluate_variable(feature_key, variable_key, context, options)

    if !evaluation[:variable_value].nil?
      if evaluation[:variable_schema] &&
         evaluation[:variable_schema][:type] == "json" &&
         evaluation[:variable_value].is_a?(String)
        JSON.parse(evaluation[:variable_value], symbolize_names: true)
      else
        evaluation[:variable_value]
      end
    else
      nil
    end
  rescue => e
    @logger.error("getVariable", { feature_key: feature_key, variable_key: variable_key, error: e })
    nil
  end
end

#get_variable_array(feature_key, variable_key, context = {}, options = {}) ⇒ Array?

Get variable as array

Parameters:

  • feature_key (String)

    Feature key

  • variable_key (String)

    Variable key

  • context (Hash) (defaults to: {})

    Context

  • options (Hash) (defaults to: {})

    Override options

Returns:

  • (Array, nil)

    Array value or nil



337
338
339
340
# File 'lib/featurevisor/instance.rb', line 337

def get_variable_array(feature_key, variable_key, context = {}, options = {})
  variable_value = get_variable(feature_key, variable_key, context, options)
  get_value_by_type(variable_value, "array")
end

#get_variable_boolean(feature_key, variable_key, context = {}, options = {}) ⇒ Boolean?

Get variable as boolean

Parameters:

  • feature_key (String)

    Feature key

  • variable_key (String)

    Variable key

  • context (Hash) (defaults to: {})

    Context

  • options (Hash) (defaults to: {})

    Override options

Returns:

  • (Boolean, nil)

    Boolean value or nil



293
294
295
296
# File 'lib/featurevisor/instance.rb', line 293

def get_variable_boolean(feature_key, variable_key, context = {}, options = {})
  variable_value = get_variable(feature_key, variable_key, context, options)
  get_value_by_type(variable_value, "boolean")
end

#get_variable_double(feature_key, variable_key, context = {}, options = {}) ⇒ Float?

Get variable as double

Parameters:

  • feature_key (String)

    Feature key

  • variable_key (String)

    Variable key

  • context (Hash) (defaults to: {})

    Context

  • options (Hash) (defaults to: {})

    Override options

Returns:

  • (Float, nil)

    Float value or nil



326
327
328
329
# File 'lib/featurevisor/instance.rb', line 326

def get_variable_double(feature_key, variable_key, context = {}, options = {})
  variable_value = get_variable(feature_key, variable_key, context, options)
  get_value_by_type(variable_value, "double")
end

#get_variable_integer(feature_key, variable_key, context = {}, options = {}) ⇒ Integer?

Get variable as integer

Parameters:

  • feature_key (String)

    Feature key

  • variable_key (String)

    Variable key

  • context (Hash) (defaults to: {})

    Context

  • options (Hash) (defaults to: {})

    Override options

Returns:

  • (Integer, nil)

    Integer value or nil



315
316
317
318
# File 'lib/featurevisor/instance.rb', line 315

def get_variable_integer(feature_key, variable_key, context = {}, options = {})
  variable_value = get_variable(feature_key, variable_key, context, options)
  get_value_by_type(variable_value, "integer")
end

#get_variable_json(feature_key, variable_key, context = {}, options = {}) ⇒ Object?

Get variable as JSON

Parameters:

  • feature_key (String)

    Feature key

  • variable_key (String)

    Variable key

  • context (Hash) (defaults to: {})

    Context

  • options (Hash) (defaults to: {})

    Override options

Returns:

  • (Object, nil)

    JSON value or nil



359
360
361
362
# File 'lib/featurevisor/instance.rb', line 359

def get_variable_json(feature_key, variable_key, context = {}, options = {})
  variable_value = get_variable(feature_key, variable_key, context, options)
  get_value_by_type(variable_value, "json")
end

#get_variable_object(feature_key, variable_key, context = {}, options = {}) ⇒ Hash?

Get variable as object

Parameters:

  • feature_key (String)

    Feature key

  • variable_key (String)

    Variable key

  • context (Hash) (defaults to: {})

    Context

  • options (Hash) (defaults to: {})

    Override options

Returns:

  • (Hash, nil)

    Object value or nil



348
349
350
351
# File 'lib/featurevisor/instance.rb', line 348

def get_variable_object(feature_key, variable_key, context = {}, options = {})
  variable_value = get_variable(feature_key, variable_key, context, options)
  get_value_by_type(variable_value, "object")
end

#get_variable_string(feature_key, variable_key, context = {}, options = {}) ⇒ String?

Get variable as string

Parameters:

  • feature_key (String)

    Feature key

  • variable_key (String)

    Variable key

  • context (Hash) (defaults to: {})

    Context

  • options (Hash) (defaults to: {})

    Override options

Returns:

  • (String, nil)

    String value or nil



304
305
306
307
# File 'lib/featurevisor/instance.rb', line 304

def get_variable_string(feature_key, variable_key, context = {}, options = {})
  variable_value = get_variable(feature_key, variable_key, context, options)
  get_value_by_type(variable_value, "string")
end

#get_variation(feature_key, context = {}, options = {}) ⇒ String?

Get variation value

Parameters:

  • feature_key (String)

    Feature key

  • context (Hash) (defaults to: {})

    Context

  • options (Hash) (defaults to: {})

    Override options

Returns:

  • (String, nil)

    Variation value or nil



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/featurevisor/instance.rb', line 227

def get_variation(feature_key, context = {}, options = {})
  begin
    evaluation = evaluate_variation(feature_key, context, options)

    if evaluation[:variation_value]
      evaluation[:variation_value]
    elsif evaluation[:variation]
      evaluation[:variation][:value]
    else
      nil
    end
  rescue => e
    @logger.error("getVariation", { feature_key: feature_key, error: e })
    nil
  end
end

#is_enabled(feature_key, context = {}, options = {}) ⇒ Boolean

Check if a feature is enabled

Parameters:

  • feature_key (String)

    Feature key

  • context (Hash) (defaults to: {})

    Context

  • options (Hash) (defaults to: {})

    Override options

Returns:

  • (Boolean)

    True if feature is enabled



198
199
200
201
202
203
204
205
206
# File 'lib/featurevisor/instance.rb', line 198

def is_enabled(feature_key, context = {}, options = {})
  begin
    evaluation = evaluate_flag(feature_key, context, options)
    evaluation[:enabled] == true
  rescue => e
    @logger.error("isEnabled", { feature_key: feature_key, error: e })
    false
  end
end

#on(event_name, callback) ⇒ Proc

Subscribe to an event

Parameters:

  • event_name (String)

    Event name

  • callback (Proc)

    Callback function

Returns:

  • (Proc)

    Unsubscribe function



123
124
125
# File 'lib/featurevisor/instance.rb', line 123

def on(event_name, callback)
  @emitter.on(event_name, callback)
end

#set_context(context, replace = false) ⇒ Object

Set context

Parameters:

  • context (Hash)

    Context to set

  • replace (Boolean) (defaults to: false)

    Whether to replace existing context



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/featurevisor/instance.rb', line 135

def set_context(context, replace = false)
  if replace
    @context = context
  else
    @context = { **@context, **context }
  end

  @emitter.trigger("context_set", {
    context: @context,
    replaced: replace
  })

  @logger.debug(replace ? "context replaced" : "context updated", {
    context: @context,
    replaced: replace
  })
end

#set_datafile(datafile) ⇒ Object

Set the datafile

Parameters:

  • datafile (Hash, String)

    Datafile content or JSON string



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/featurevisor/instance.rb', line 61

def set_datafile(datafile)
  begin
    new_datafile_reader = Featurevisor::DatafileReader.new(
      datafile: datafile.is_a?(String) ? JSON.parse(datafile, symbolize_names: true) : datafile,
      logger: @logger
    )

    details = Featurevisor::Events.get_params_for_datafile_set_event(@datafile_reader, new_datafile_reader)
    @datafile_reader = new_datafile_reader

    @logger.info("datafile set", details)
    @emitter.trigger("datafile_set", details)
  rescue => e
    @logger.error("could not parse datafile", { error: e })
  end
end

#set_log_level(level) ⇒ Object

Set the log level

Parameters:

  • level (String)

    Log level



55
56
57
# File 'lib/featurevisor/instance.rb', line 55

def set_log_level(level)
  @logger.set_level(level)
end

#set_sticky(sticky, replace = false) ⇒ Object

Set sticky features

Parameters:

  • sticky (Hash)

    Sticky features

  • replace (Boolean) (defaults to: false)

    Whether to replace existing sticky features



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

def set_sticky(sticky, replace = false)
  previous_sticky_features = @sticky || {}

  if replace
    @sticky = sticky
  else
    @sticky = {
      **@sticky,
      **sticky
    }
  end

  params = Featurevisor::Events.get_params_for_sticky_set_event(previous_sticky_features, @sticky, replace)

  @logger.info("sticky features set", params)
  @emitter.trigger("sticky_set", params)
end

#spawn(context = {}, options = {}) ⇒ ChildInstance

Spawn a child instance

Parameters:

  • context (Hash) (defaults to: {})

    Child context

  • options (Hash) (defaults to: {})

    Override options

Returns:



171
172
173
174
175
176
177
# File 'lib/featurevisor/instance.rb', line 171

def spawn(context = {}, options = {})
  Featurevisor::ChildInstance.new(
    parent: self,
    context: get_context(context),
    sticky: options[:sticky]
  )
end