Class: Featurevisor::Instance
- Inherits:
-
Object
- Object
- Featurevisor::Instance
- 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: {}, variables: {} }.freeze
Instance Method Summary collapse
-
#add_module(mod) ⇒ Proc?
Add a module.
-
#close ⇒ Object
Close the instance.
-
#evaluate_flag(feature_key, context = {}, options = {}) ⇒ Hash
Evaluate a flag.
-
#evaluate_variable(feature_or_variable_key, variable_key_or_context = nil, context_or_options = {}, options = {}) ⇒ Hash
Evaluate a variable.
-
#evaluate_variation(feature_key, context = {}, options = {}) ⇒ Hash
Evaluate a variation.
-
#get_context(context = nil) ⇒ Hash
Get context.
-
#get_feature(feature_key) ⇒ Hash?
Get a feature by key.
-
#get_feature_evaluations(context = {}, feature_keys = [], options = {}) ⇒ Hash
Get all evaluations.
- #get_feature_keys ⇒ Object
-
#get_revision ⇒ String
Get the revision.
- #get_schema_version ⇒ Object
- #get_segment(segment_key) ⇒ Object
-
#get_variable(feature_or_variable_key, variable_key_or_context = nil, context_or_options = {}, options = {}) ⇒ Object?
Get variable value.
-
#get_variable_array(*args) ⇒ Array?
Get variable as array.
-
#get_variable_boolean(*args) ⇒ Boolean?
Get variable as boolean.
-
#get_variable_double(*args) ⇒ Float?
Get variable as double.
- #get_variable_evaluations(context = {}, variable_keys = [], options = {}) ⇒ Object
-
#get_variable_integer(*args) ⇒ Integer?
Get variable as integer.
-
#get_variable_json(*args) ⇒ Object?
Get variable as JSON.
- #get_variable_keys(feature_key = nil) ⇒ Object
-
#get_variable_object(*args) ⇒ Hash?
Get variable as object.
-
#get_variable_string(*args) ⇒ String?
Get variable as string.
-
#get_variation(feature_key, context = {}, options = {}) ⇒ String?
Get variation value.
- #has_variations?(feature_key) ⇒ Boolean
-
#initialize(options = {}) ⇒ Instance
constructor
Initialize a new Featurevisor instance.
-
#is_enabled(feature_key, context = {}, options = {}) ⇒ Boolean
Check if a feature is enabled.
-
#on(event_name, callback) ⇒ Proc
Subscribe to an event.
- #remove_module(name) ⇒ Object
-
#set_context(context, replace = false) ⇒ Object
Set context.
-
#set_datafile(datafile, replace = false) ⇒ Object
Set the datafile.
-
#set_log_level(level) ⇒ Object
Set the log level.
-
#set_sticky_features(sticky, replace = false) ⇒ Object
Set sticky features.
- #set_sticky_variables(sticky, replace = false) ⇒ Object
-
#spawn(context = {}, options = {}) ⇒ ChildInstance
Spawn a child instance.
Constructor Details
#initialize(options = {}) ⇒ Instance
Initialize a new Featurevisor instance
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 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/featurevisor/instance.rb', line 26 def initialize( = {}) # from options @context = [:context] || {} @diagnostics = DiagnosticReporter.new( level: [:log_level] || "info", handler: method(:handle_evaluation_diagnostic) ) @on_diagnostic = [:on_diagnostic] || [:onDiagnostic] @emitter = Featurevisor::Emitter.new @sticky_features = [:sticky_features] || [:stickyFeatures] || {} @sticky_variables = [:sticky_variables] || [:stickyVariables] || {} @closed = false @module_diagnostic_subscriptions = [] # datafile @datafile = InstanceEvaluationDataProvider.new( datafile: EMPTY_DATAFILE, diagnostics: @diagnostics ) @modules_manager = Featurevisor::Modules::ModulesManager.new( modules: [:modules] || [], report_diagnostic: method(:report_diagnostic), module_api_factory: method(:create_module_api), clear_module_diagnostic_subscriptions: method(:clear_module_diagnostic_subscriptions) ) if [:datafile] set_datafile([:datafile], true) end report_diagnostic( level: "info", code: "sdk_initialized", message: "SDK initialized" ) end |
Instance Method Details
#add_module(mod) ⇒ Proc?
Add a module
213 214 215 |
# File 'lib/featurevisor/instance.rb', line 213 def add_module(mod) @modules_manager.add(mod) end |
#close ⇒ Object
Close the instance
232 233 234 235 236 237 238 239 |
# File 'lib/featurevisor/instance.rb', line 232 def close return if @closed @closed = true @modules_manager.close_all @module_diagnostic_subscriptions = [] @emitter.clear_all end |
#evaluate_flag(feature_key, context = {}, options = {}) ⇒ Hash
Evaluate a flag
299 300 301 302 303 304 305 306 |
# File 'lib/featurevisor/instance.rb', line 299 def evaluate_flag(feature_key, context = {}, = {}) Featurevisor::Evaluate.evaluate_with_modules( get_evaluation_dependencies(context, ).merge( type: "flag", feature_key: feature_key ) ) end |
#evaluate_variable(feature_or_variable_key, variable_key_or_context = nil, context_or_options = {}, options = {}) ⇒ Hash
Evaluate a variable
365 366 367 368 369 370 371 372 373 374 375 376 377 378 |
# File 'lib/featurevisor/instance.rb', line 365 def evaluate_variable(feature_or_variable_key, variable_key_or_context = nil, = {}, = {}) if variable_key_or_context.nil? || variable_key_or_context.is_a?(Hash) context = variable_key_or_context || {} return evaluate_variable_without_feature(feature_or_variable_key, context, ) end Featurevisor::Evaluate.evaluate_with_modules( get_evaluation_dependencies(, ).merge( type: "variable", feature_key: feature_or_variable_key, variable_key: variable_key_or_context ) ) end |
#evaluate_variation(feature_key, context = {}, options = {}) ⇒ Hash
Evaluate a variation
328 329 330 331 332 333 334 335 |
# File 'lib/featurevisor/instance.rb', line 328 def evaluate_variation(feature_key, context = {}, = {}) Featurevisor::Evaluate.evaluate_with_modules( get_evaluation_dependencies(context, ).merge( type: "variation", feature_key: feature_key ) ) end |
#get_context(context = nil) ⇒ Hash
Get context
270 271 272 273 274 275 276 277 278 279 |
# File 'lib/featurevisor/instance.rb', line 270 def get_context(context = nil) if context { **@context, **context } else @context end end |
#get_feature(feature_key) ⇒ Hash?
Get a feature by key
206 207 208 |
# File 'lib/featurevisor/instance.rb', line 206 def get_feature(feature_key) @datafile.get_feature(feature_key) end |
#get_feature_evaluations(context = {}, feature_keys = [], options = {}) ⇒ Hash
Get all evaluations
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 |
# File 'lib/featurevisor/instance.rb', line 489 def get_feature_evaluations(context = {}, feature_keys = [], = {}) result = {} keys = feature_keys.size > 0 ? feature_keys : @datafile.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, ) } # variation if @datafile.has_variations?(feature_key_str) variation = get_variation(feature_key_str, context, ) evaluated_feature[:variation] = variation unless variation.nil? end # variables variable_keys = @datafile.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, ) end end result[feature_key] = evaluated_feature end result end |
#get_feature_keys ⇒ Object
191 192 193 |
# File 'lib/featurevisor/instance.rb', line 191 def get_feature_keys @datafile.get_feature_keys end |
#get_revision ⇒ String
Get the revision
179 180 181 |
# File 'lib/featurevisor/instance.rb', line 179 def get_revision @datafile.get_revision end |
#get_schema_version ⇒ Object
183 184 185 |
# File 'lib/featurevisor/instance.rb', line 183 def get_schema_version @datafile.get_schema_version end |
#get_segment(segment_key) ⇒ Object
187 188 189 |
# File 'lib/featurevisor/instance.rb', line 187 def get_segment(segment_key) @datafile.get_segment(segment_key) end |
#get_variable(feature_or_variable_key, variable_key_or_context = nil, context_or_options = {}, options = {}) ⇒ Object?
Get variable value
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
# File 'lib/featurevisor/instance.rb', line 386 def get_variable(feature_or_variable_key, variable_key_or_context = nil, = {}, = {}) begin evaluation = evaluate_variable(feature_or_variable_key, variable_key_or_context, , ) if evaluation.key?(:variable_value) variable_type = evaluation.dig(:variable_schema, :type) || evaluation.dig(:variable, :type) if variable_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 report_diagnostic(level: "error", code: "evaluation_error", message: "getVariable failed", originalError: e, details: { variableKey: variable_key_or_context || feature_or_variable_key }) nil end end |
#get_variable_array(*args) ⇒ Array?
Get variable as array
457 458 459 460 |
# File 'lib/featurevisor/instance.rb', line 457 def get_variable_array(*args) variable_value = get_variable(*args) get_value_by_type(variable_value, "array") end |
#get_variable_boolean(*args) ⇒ Boolean?
Get variable as boolean
413 414 415 416 |
# File 'lib/featurevisor/instance.rb', line 413 def get_variable_boolean(*args) variable_value = get_variable(*args) get_value_by_type(variable_value, "boolean") end |
#get_variable_double(*args) ⇒ Float?
Get variable as double
446 447 448 449 |
# File 'lib/featurevisor/instance.rb', line 446 def get_variable_double(*args) variable_value = get_variable(*args) get_value_by_type(variable_value, "double") end |
#get_variable_evaluations(context = {}, variable_keys = [], options = {}) ⇒ Object
530 531 532 533 |
# File 'lib/featurevisor/instance.rb', line 530 def get_variable_evaluations(context = {}, variable_keys = [], = {}) keys = variable_keys.empty? ? @datafile.get_variable_keys : variable_keys keys.to_h { |key| [key, get_variable(key.to_s, context, )] } end |
#get_variable_integer(*args) ⇒ Integer?
Get variable as integer
435 436 437 438 |
# File 'lib/featurevisor/instance.rb', line 435 def get_variable_integer(*args) variable_value = get_variable(*args) get_value_by_type(variable_value, "integer") end |
#get_variable_json(*args) ⇒ Object?
Get variable as JSON
479 480 481 482 |
# File 'lib/featurevisor/instance.rb', line 479 def get_variable_json(*args) variable_value = get_variable(*args) get_value_by_type(variable_value, "json") end |
#get_variable_keys(feature_key = nil) ⇒ Object
195 196 197 |
# File 'lib/featurevisor/instance.rb', line 195 def get_variable_keys(feature_key = nil) @datafile.get_variable_keys(feature_key) end |
#get_variable_object(*args) ⇒ Hash?
Get variable as object
468 469 470 471 |
# File 'lib/featurevisor/instance.rb', line 468 def get_variable_object(*args) variable_value = get_variable(*args) get_value_by_type(variable_value, "object") end |
#get_variable_string(*args) ⇒ String?
Get variable as string
424 425 426 427 |
# File 'lib/featurevisor/instance.rb', line 424 def get_variable_string(*args) variable_value = get_variable(*args) get_value_by_type(variable_value, "string") end |
#get_variation(feature_key, context = {}, options = {}) ⇒ String?
Get variation value
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 |
# File 'lib/featurevisor/instance.rb', line 342 def get_variation(feature_key, context = {}, = {}) begin evaluation = evaluate_variation(feature_key, context, ) if evaluation.key?(:variation_value) evaluation[:variation_value] elsif evaluation[:variation] evaluation[:variation][:value] else nil end rescue => e report_diagnostic(level: "error", code: "evaluation_error", message: "getVariation failed", originalError: e, details: { featureKey: feature_key }) nil end end |
#has_variations?(feature_key) ⇒ Boolean
199 200 201 |
# File 'lib/featurevisor/instance.rb', line 199 def has_variations?(feature_key) @datafile.has_variations?(feature_key) end |
#is_enabled(feature_key, context = {}, options = {}) ⇒ Boolean
Check if a feature is enabled
313 314 315 316 317 318 319 320 321 |
# File 'lib/featurevisor/instance.rb', line 313 def is_enabled(feature_key, context = {}, = {}) begin evaluation = evaluate_flag(feature_key, context, ) evaluation[:enabled] == true rescue => e report_diagnostic(level: "error", code: "evaluation_error", message: "isEnabled failed", originalError: e, details: { featureKey: feature_key }) false end end |
#on(event_name, callback) ⇒ Proc
Subscribe to an event
225 226 227 228 229 |
# File 'lib/featurevisor/instance.rb', line 225 def on(event_name, callback) return -> {} if @closed @emitter.on(event_name, callback) end |
#remove_module(name) ⇒ Object
217 218 219 |
# File 'lib/featurevisor/instance.rb', line 217 def remove_module(name) @modules_manager.remove(name) end |
#set_context(context, replace = false) ⇒ Object
Set context
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
# File 'lib/featurevisor/instance.rb', line 244 def set_context(context, replace = false) if replace @context = context else @context = { **@context, **context } end @emitter.trigger("context_set", { context: @context, replaced: replace }) report_diagnostic( level: "debug", code: "context_set", message: replace ? "Context replaced" : "Context updated", details: { context: @context, replaced: replace } ) end |
#set_datafile(datafile, replace = false) ⇒ Object
Set the datafile
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/featurevisor/instance.rb', line 100 def set_datafile(datafile, replace = false) return if @closed begin parsed_datafile = if datafile.is_a?(String) JSON.parse(datafile, symbolize_names: true) elsif datafile.is_a?(Hash) JSON.parse(JSON.generate(datafile), symbolize_names: true) else datafile end unless parsed_datafile.is_a?(Hash) && parsed_datafile[:schemaVersion].is_a?(String) && parsed_datafile[:revision].is_a?(String) && parsed_datafile[:segments].is_a?(Hash) && parsed_datafile[:features].is_a?(Hash) && (!parsed_datafile.key?(:variables) || parsed_datafile[:variables].is_a?(Hash)) raise ArgumentError, "Invalid datafile" end next_datafile = replace ? parsed_datafile : merge_datafiles(@datafile.get_datafile, parsed_datafile) new_datafile = InstanceEvaluationDataProvider.new( datafile: next_datafile, diagnostics: @diagnostics ) details = Featurevisor::Events.get_params_for_datafile_set_event(@datafile, new_datafile, replace) @datafile = new_datafile report_diagnostic( level: "info", code: "datafile_set", message: "Datafile set", details: details ) @emitter.trigger("datafile_set", details) rescue => e report_diagnostic( level: "error", code: "invalid_datafile", message: "Could not parse datafile", original_error: e ) end end |
#set_log_level(level) ⇒ Object
Set the log level
66 67 68 |
# File 'lib/featurevisor/instance.rb', line 66 def set_log_level(level) @diagnostics.set_level(level) end |
#set_sticky_features(sticky, replace = false) ⇒ Object
Set sticky features
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/featurevisor/instance.rb', line 146 def set_sticky_features(sticky, replace = false) previous_sticky_features = @sticky_features || {} if replace @sticky_features = sticky else @sticky_features = { **@sticky_features, **sticky } end params = Featurevisor::Events.get_params_for_sticky_features_set_event(previous_sticky_features, @sticky_features, replace) report_diagnostic( level: "info", code: "sticky_features_set", message: "Sticky features set", details: params ) @emitter.trigger("sticky_features_set", params) end |
#set_sticky_variables(sticky, replace = false) ⇒ Object
169 170 171 172 173 174 175 |
# File 'lib/featurevisor/instance.rb', line 169 def set_sticky_variables(sticky, replace = false) previous = @sticky_variables || {} @sticky_variables = replace ? sticky : { **@sticky_variables, **sticky } params = Featurevisor::Events.get_params_for_sticky_variables_set_event(previous, @sticky_variables, replace) report_diagnostic(level: "info", code: "sticky_variables_set", message: "Sticky variables set", details: params) @emitter.trigger("sticky_variables_set", params) end |
#spawn(context = {}, options = {}) ⇒ ChildInstance
Spawn a child instance
285 286 287 288 289 290 291 292 |
# File 'lib/featurevisor/instance.rb', line 285 def spawn(context = {}, = {}) Featurevisor::ChildInstance.new( parent: self, context: get_context(context), sticky_features: [:sticky_features] || [:stickyFeatures], sticky_variables: [:sticky_variables] || [:stickyVariables] ) end |