statsig 2.4.3 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 13aa0d6726504ea7bef05a61a4663426e4b1a9fe3e7c8a5c56ade788665d5e2d
4
- data.tar.gz: bd6886c09af0b6403c288c25de15d7c5f40783656dc891518469b7da030f59b2
3
+ metadata.gz: 9d4842b01ba9c40089bbf689984a0c9a59234d83f0d070c2570856335ad012fc
4
+ data.tar.gz: 44afa2a46886c397ecc2975f123d9808f08548fadd7e9703130cd6099033d159
5
5
  SHA512:
6
- metadata.gz: f827849b39d1c4bc2166b8c4b2477644fd24997a2e9b840e9b87db61f051f6a564e04b9e3b1779688baf8d7c128ed3ccf4f5e277a9353ee9cdc44f57afd6b8aa
7
- data.tar.gz: 613c1a21bf021fe0b68920a102e834f03ba2e7c9816175f771526daad548ad496089fbbeae0b4abb7ad179e3fe09daaf376d6d347011bfed97a2a0e164e1780c
6
+ metadata.gz: 13ec911e8fb874d705ace7bc92d857dfa8a71171812b822f2af2826e9f52af01d03af8661bfd42866d7c7560b5a050a9ff2e936084eb61d18a46c67555bf5ea7
7
+ data.tar.gz: 61c0be14ffc93a7bc0db3fe25932a09e8dab52fdb7eaf50b2e15ffef44cba975ddf5706b328ba8f9e0eef6ef0b744856b691efbfae72c247fb58a0d04465a86e
data/lib/evaluator.rb CHANGED
@@ -20,6 +20,8 @@ module Statsig
20
20
 
21
21
  attr_accessor :config_overrides
22
22
 
23
+ attr_accessor :experiment_overrides
24
+
23
25
  attr_accessor :options
24
26
 
25
27
  attr_accessor :persistent_storage_utils
@@ -31,6 +33,7 @@ module Statsig
31
33
  @spec_store = store
32
34
  @gate_overrides = {}
33
35
  @config_overrides = {}
36
+ @experiment_overrides = {}
34
37
  @options = options
35
38
  @persistent_storage_utils = persistent_storage_utils
36
39
  end
@@ -58,19 +61,32 @@ module Statsig
58
61
 
59
62
  def lookup_config_override(config_name)
60
63
  config_name_sym = config_name.to_sym
64
+ if @experiment_overrides.key?(config_name_sym)
65
+ override = @experiment_overrides[config_name_sym]
66
+ return ConfigResult.new(
67
+ name: config_name,
68
+ json_value: override[:value],
69
+ group_name: override[:group_name],
70
+ rule_id: override[:rule_id],
71
+ evaluation_details: EvaluationDetails.local_override(
72
+ @spec_store.last_config_sync_time,
73
+ @spec_store.initial_config_sync_time
74
+ )
75
+ )
76
+ end
61
77
  if @config_overrides.key?(config_name_sym)
78
+ override = @config_overrides[config_name_sym]
62
79
  return ConfigResult.new(
63
80
  name: config_name,
64
- json_value: @config_overrides[config_name_sym],
81
+ json_value: override,
65
82
  rule_id: Const::OVERRIDE,
66
- id_type: @spec_store.has_config?(config_name) ? @spec_store.get_config(config_name)[:idType] : Const::EMPTY_STR,
67
83
  evaluation_details: EvaluationDetails.local_override(
68
84
  @spec_store.last_config_sync_time,
69
85
  @spec_store.initial_config_sync_time
70
86
  )
71
87
  )
72
88
  end
73
- return nil
89
+ nil
74
90
  end
75
91
 
76
92
  def check_gate(user, gate_name, end_result, ignore_local_overrides: false, is_nested: false)
@@ -108,6 +124,8 @@ module Statsig
108
124
  end_result.id_type = local_override.id_type
109
125
  end_result.rule_id = local_override.rule_id
110
126
  end_result.json_value = local_override.json_value
127
+ end_result.group_name = local_override.group_name
128
+ end_result.is_experiment_group = local_override.is_experiment_group
111
129
  unless end_result.disable_evaluation_details
112
130
  end_result.evaluation_details = local_override.evaluation_details
113
131
  end
@@ -447,6 +465,37 @@ module Statsig
447
465
  @config_overrides.clear
448
466
  end
449
467
 
468
+ def override_experiment_by_group_name(experiment_name, group_name)
469
+ return unless @spec_store.has_config?(experiment_name)
470
+
471
+ config = @spec_store.get_config(experiment_name)
472
+ return unless config[:entity] == Const::TYPE_EXPERIMENT
473
+
474
+ config[:rules].each do |rule|
475
+ if rule[:groupName] == group_name
476
+ @experiment_overrides[experiment_name.to_sym] = {
477
+ value: rule[:returnValue],
478
+ group_name: rule[:groupName],
479
+ rule_id: rule[:id],
480
+ evaluation_details: EvaluationDetails.local_override(@config_sync_time, @init_time)
481
+ }
482
+ return
483
+ end
484
+ end
485
+
486
+ # If no matching rule is found, create a default override with empty value
487
+ @experiment_overrides[experiment_name.to_sym] = {
488
+ value: {},
489
+ group_name: group_name,
490
+ rule_id: "#{experiment_name}:override",
491
+ evaluation_details: EvaluationDetails.local_override(@config_sync_time, @init_time)
492
+ }
493
+ end
494
+
495
+ def clear_experiment_overrides
496
+ @experiment_overrides.clear
497
+ end
498
+
450
499
  def eval_spec(config_name, user, config, end_result, is_nested: false)
451
500
  config[:rules].each do |rule|
452
501
  end_result.sampling_rate = rule[:samplingRate]
data/lib/memo.rb CHANGED
@@ -3,7 +3,11 @@ module Statsig
3
3
 
4
4
  @global_memo = {}
5
5
 
6
- def self.for(hash, method, key)
6
+ def self.for(hash, method, key, disable_evaluation_memoization: false)
7
+ if disable_evaluation_memoization
8
+ return yield
9
+ end
10
+
7
11
  if key != nil
8
12
  method_hash = hash[method]
9
13
  unless method_hash
data/lib/statsig.rb CHANGED
@@ -328,6 +328,17 @@ module Statsig
328
328
  @shared_instance&.override_config(config_name, config_value)
329
329
  end
330
330
 
331
+
332
+ ##
333
+ # Overrides an experiment to return the value for a specific group name.
334
+ #
335
+ # @param experiment_name The name of the experiment to be overridden
336
+ # @param group_name The name of the group whose value should be returned
337
+ def self.override_experiment_by_group_name(experiment_name, group_name)
338
+ ensure_initialized
339
+ @shared_instance&.override_experiment_by_group_name(experiment_name, group_name)
340
+ end
341
+
331
342
  def self.remove_config_override(config_name)
332
343
  ensure_initialized
333
344
  @shared_instance&.remove_config_override(config_name)
@@ -338,6 +349,11 @@ module Statsig
338
349
  @shared_instance&.clear_config_overrides
339
350
  end
340
351
 
352
+ def self.clear_experiment_overrides
353
+ ensure_initialized
354
+ @shared_instance&.clear_experiment_overrides
355
+ end
356
+
341
357
  ##
342
358
  # @param [HashTable] debug information log with exposure events
343
359
  def self.set_debug_info(debug_info)
@@ -370,11 +386,15 @@ module Statsig
370
386
  def self.get_statsig_metadata
371
387
  {
372
388
  'sdkType' => 'ruby-server',
373
- 'sdkVersion' => '2.4.3',
389
+ 'sdkVersion' => '2.5.0',
374
390
  'languageVersion' => RUBY_VERSION
375
391
  }
376
392
  end
377
393
 
394
+ def self.get_options
395
+ @driver&.instance_variable_get(:@options)
396
+ end
397
+
378
398
  private
379
399
 
380
400
  def self.ensure_initialized
@@ -65,7 +65,7 @@ class StatsigDriver
65
65
 
66
66
  user = verify_inputs(user, gate_name, 'gate_name')
67
67
 
68
- Statsig::Memo.for(user.get_memo, :get_gate_impl, gate_name) do
68
+ Statsig::Memo.for(user.get_memo, :get_gate_impl, gate_name, disable_evaluation_memoization: @options.disable_evaluation_memoization) do
69
69
  res = Statsig::ConfigResult.new(
70
70
  name: gate_name,
71
71
  disable_exposures: disable_log_exposure,
@@ -171,7 +171,7 @@ class StatsigDriver
171
171
  @err_boundary.capture(caller: __method__, recover: -> { Layer.new(layer_name) }) do
172
172
  run_with_diagnostics(caller: :get_layer) do
173
173
  user = verify_inputs(user, layer_name, "layer_name")
174
- Statsig::Memo.for(user.get_memo, :get_layer, layer_name) do
174
+ Statsig::Memo.for(user.get_memo, :get_layer, layer_name, disable_evaluation_memoization: @options.disable_evaluation_memoization) do
175
175
  exposures_disabled = options&.disable_log_exposure == true
176
176
  res = Statsig::ConfigResult.new(
177
177
  name: layer_name,
@@ -304,6 +304,12 @@ class StatsigDriver
304
304
  end
305
305
  end
306
306
 
307
+ def clear_experiment_overrides
308
+ @err_boundary.capture(caller: __method__) do
309
+ @evaluator.clear_experiment_overrides
310
+ end
311
+ end
312
+
307
313
  def set_debug_info(debug_info)
308
314
  @err_boundary.capture(caller: __method__) do
309
315
  @logger.set_debug_info(debug_info)
@@ -337,6 +343,10 @@ class StatsigDriver
337
343
  end
338
344
  end
339
345
 
346
+ def override_experiment_by_group_name(experiment_name, group_name)
347
+ @evaluator.override_experiment_by_group_name(experiment_name, group_name)
348
+ end
349
+
340
350
  private
341
351
 
342
352
  def run_with_diagnostics(caller:)
@@ -361,7 +371,7 @@ class StatsigDriver
361
371
 
362
372
  def verify_inputs(user, config_name, variable_name)
363
373
  validate_user(user)
364
- user = Statsig::Memo.for(user.get_memo(), :verify_inputs, 0) do
374
+ user = Statsig::Memo.for(user.get_memo(), :verify_inputs, 0, disable_evaluation_memoization: @options.disable_evaluation_memoization) do
365
375
  user = normalize_user(user)
366
376
  check_shutdown
367
377
  maybe_restart_background_threads
@@ -376,7 +386,7 @@ class StatsigDriver
376
386
  end
377
387
 
378
388
  def get_config_impl(user, config_name, disable_log_exposure, user_persisted_values: nil, disable_evaluation_details: false, ignore_local_overrides: false)
379
- Statsig::Memo.for(user.get_memo, :get_config_impl, config_name) do
389
+ Statsig::Memo.for(user.get_memo, :get_config_impl, config_name, disable_evaluation_memoization: @options.disable_evaluation_memoization) do
380
390
  res = Statsig::ConfigResult.new(
381
391
  name: config_name,
382
392
  disable_exposures: disable_log_exposure,
data/lib/statsig_event.rb CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  class StatsigEvent
3
2
  attr_accessor :value, :metadata, :statsig_metadata, :secondary_exposures
4
3
  attr_reader :user
@@ -15,7 +14,7 @@ class StatsigEvent
15
14
 
16
15
  def user=(value)
17
16
  if value.is_a?(StatsigUser)
18
- @user = Statsig::Memo.for(value.get_memo(), :serialize, 0) do
17
+ @user = Statsig::Memo.for(value.get_memo(), :serialize, 0, disable_evaluation_memoization: Statsig.get_options&.disable_evaluation_memoization) do
19
18
  value.serialize(true)
20
19
  end
21
20
  end
@@ -88,6 +88,10 @@ class StatsigOptions
88
88
  # Implements Statsig::Interfaces::IUserPersistentStorage.
89
89
  attr_accessor :user_persistent_storage
90
90
 
91
+ # Disable memoization of evaluation results. When true, each evaluation will be performed fresh.
92
+ # default: false
93
+ attr_accessor :disable_evaluation_memoization
94
+
91
95
  def initialize(
92
96
  environment = nil,
93
97
  download_config_specs_url: nil,
@@ -110,7 +114,8 @@ class StatsigOptions
110
114
  network_timeout: nil,
111
115
  post_logs_retry_limit: 3,
112
116
  post_logs_retry_backoff: nil,
113
- user_persistent_storage: nil
117
+ user_persistent_storage: nil,
118
+ disable_evaluation_memoization: false
114
119
  )
115
120
  @environment = environment.is_a?(Hash) ? environment : nil
116
121
 
@@ -140,6 +145,6 @@ class StatsigOptions
140
145
  @post_logs_retry_limit = post_logs_retry_limit
141
146
  @post_logs_retry_backoff = post_logs_retry_backoff
142
147
  @user_persistent_storage = user_persistent_storage
143
-
148
+ @disable_evaluation_memoization = disable_evaluation_memoization
144
149
  end
145
150
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statsig
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.3
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Statsig, Inc
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-05-22 00:00:00.000000000 Z
11
+ date: 2025-05-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler