statsig 2.9.3 → 2.9.4

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: 5371899fd2582b8e213db4bfc0c0c9252e2a7e16ff1303c94fa7e1cecc43cfda
4
- data.tar.gz: 786baba109ff66e4c38ffc4c71995fdf5fde54910ac0d7712cdfb9b85ac584f4
3
+ metadata.gz: 96c137e20d5bc087e6b3108ad346f3882a8f981c2fc18e95270af8511230f932
4
+ data.tar.gz: daeaec468e9fa579f7a5ef80085bf19a49175d25985ae7d2128ed1c767e073bb
5
5
  SHA512:
6
- metadata.gz: e4dc2ad08e71c883d41ecfa012f9e592c6ece9e18e8e14b9f0df9c5ef0732140dddf7cd0f6b88e27516efdbfa89e0d7309e2763fb634b73da0de082eb78b84f3
7
- data.tar.gz: 9fc9b841b4d3f6020b9ea71a87203155a5eef84641ac1f7ff52a1e8913ef1735e3f49ef3730575d67050b1c00b7e61d0953a00b4cfc001af8e5b31277499f6d1
6
+ metadata.gz: 76055e14d07eab81f94c6ad559e3378da16860a8c60fc3dc6a1118fa7621c61f53d7b4921e0a70856e1195aa954b23fb188812fe9ba16f4c6db3059c3ad59f1c
7
+ data.tar.gz: ad835e17a3ee00318cbf3ee7ccbcf94b7ef09dd05a08163b6ff1308d79040593175fb3d08b44d716c015edeae55d5949f1ad3515c74eed13ae16c5c709b84069
@@ -51,7 +51,8 @@ module Statsig
51
51
  name: config_name,
52
52
  disable_evaluation_details: true,
53
53
  disable_exposures: !include_exposures,
54
- include_local_overrides: include_local_overrides
54
+ include_local_overrides: include_local_overrides,
55
+ disable_nested_experiment_evaluation: true
55
56
  )
56
57
  evaluator.eval_spec(config_name_str, user, config_spec, eval_result)
57
58
  else
data/lib/config_result.rb CHANGED
@@ -22,6 +22,7 @@ module Statsig
22
22
  attr_accessor :sampling_rate
23
23
  attr_accessor :has_seen_analytical_gates
24
24
  attr_accessor :override_config_name
25
+ attr_accessor :disable_nested_experiment_evaluation
25
26
 
26
27
  def initialize(
27
28
  name:,
@@ -43,7 +44,8 @@ module Statsig
43
44
  forward_all_exposures: false,
44
45
  sampling_rate: nil,
45
46
  has_seen_analytical_gates: false,
46
- override_config_name: nil
47
+ override_config_name: nil,
48
+ disable_nested_experiment_evaluation: false
47
49
  )
48
50
  @name = name
49
51
  @gate_value = gate_value
@@ -66,6 +68,7 @@ module Statsig
66
68
  @sampling_rate = sampling_rate
67
69
  @has_seen_analytical_gates = has_seen_analytical_gates
68
70
  @override_config_name = override_config_name
71
+ @disable_nested_experiment_evaluation = disable_nested_experiment_evaluation
69
72
  end
70
73
 
71
74
  def self.from_user_persisted_values(config_name, user_persisted_values)
data/lib/constants.rb CHANGED
@@ -7,6 +7,7 @@ module Statsig
7
7
  SUPPORTED_CONDITION_TYPES = Set.new(%i[
8
8
  public fail_gate pass_gate ip_based ua_based user_field
9
9
  environment_field current_time user_bucket unit_id
10
+ experiment_group
10
11
  ]).freeze
11
12
 
12
13
  SUPPORTED_OPERATORS = Set.new(%i[
@@ -87,6 +88,7 @@ module Statsig
87
88
  CND_MULTI_PASS_GATE = 'multi_pass_gate'.freeze
88
89
  CND_MULTI_FAIL_GATE = 'multi_fail_gate'.freeze
89
90
  CND_CURRENT_TIME = 'current_time'.freeze
91
+ CND_EXPERIMENT_GROUP = 'experiment_group'.freeze
90
92
  CND_ENVIRONMENT_FIELD = 'environment_field'.freeze
91
93
  CND_USER_BUCKET = 'user_bucket'.freeze
92
94
  CND_UNIT_ID = 'unit_id'.freeze
data/lib/evaluator.rb CHANGED
@@ -14,6 +14,12 @@ require_relative 'user_persistent_storage_utils'
14
14
  module Statsig
15
15
  class Evaluator
16
16
 
17
+ EXPOSURE_LOGGING_CONDITION_TYPES = [
18
+ Const::CND_PASS_GATE,
19
+ Const::CND_FAIL_GATE,
20
+ Const::CND_EXPERIMENT_GROUP
21
+ ].freeze
22
+
17
23
  attr_accessor :spec_store
18
24
 
19
25
  attr_accessor :gate_overrides
@@ -26,7 +32,7 @@ module Statsig
26
32
 
27
33
  attr_accessor :persistent_storage_utils
28
34
 
29
- def initialize(store, options, persistent_storage_utils)
35
+ def initialize(store, options, persistent_storage_utils, logger = nil)
30
36
  UAParser.initialize_async
31
37
  CountryLookup.initialize_async
32
38
 
@@ -36,6 +42,7 @@ module Statsig
36
42
  @experiment_overrides = {}
37
43
  @options = options
38
44
  @persistent_storage_utils = persistent_storage_utils
45
+ @logger = logger
39
46
  end
40
47
 
41
48
  def maybe_restart_background_threads
@@ -689,8 +696,10 @@ module Statsig
689
696
  eval_condition(user, condition, end_result)
690
697
  end
691
698
 
699
+ # Conditions that log an exposure as a side effect are never memoized, or the
700
+ # repeat evaluations would silently drop their exposures.
692
701
  if !@options.disable_evaluation_memoization &&
693
- condition && condition[:type] != Const::CND_PASS_GATE && condition[:type] != Const::CND_FAIL_GATE
702
+ condition && !EXPOSURE_LOGGING_CONDITION_TYPES.include?(condition[:type])
694
703
  eval_rule_memo[condition_hash] = result
695
704
  end
696
705
 
@@ -742,6 +751,8 @@ module Statsig
742
751
  return type == Const::CND_PASS_GATE ? result : !result
743
752
  when Const::CND_MULTI_PASS_GATE, Const::CND_MULTI_FAIL_GATE
744
753
  return eval_nested_gates(target, type, user, end_result)
754
+ when Const::CND_EXPERIMENT_GROUP
755
+ value = eval_experiment_group(field, user, end_result)
745
756
  when Const::CND_IP_BASED
746
757
  value = get_value_from_user(user, field) || get_value_from_ip(user, field)
747
758
  when Const::CND_UA_BASED
@@ -921,6 +932,29 @@ module Statsig
921
932
  gate_value
922
933
  end
923
934
 
935
+ # The condition's field carries the name of a parent experiment. The user's group
936
+ # in that experiment becomes the value the operator compares against. Mirrors
937
+ # statsig-rust's evaluate_experiment_group, which evaluates the parent through the
938
+ # public get_experiment API: a full evaluation that logs its own experiment
939
+ # exposure and contributes nothing to the outer result's secondary exposures.
940
+ def eval_experiment_group(experiment_name, user, end_result)
941
+ return nil if end_result.disable_nested_experiment_evaluation
942
+ return nil unless experiment_name.is_a?(String) && !experiment_name.empty?
943
+
944
+ nested_result = ConfigResult.new(
945
+ name: experiment_name,
946
+ disable_exposures: end_result.disable_exposures,
947
+ disable_evaluation_details: end_result.disable_evaluation_details
948
+ )
949
+ get_config(user, experiment_name, nested_result)
950
+
951
+ unless end_result.disable_exposures
952
+ @logger&.log_config_exposure(user, nested_result)
953
+ end
954
+
955
+ nested_result.group_name
956
+ end
957
+
924
958
  def eval_nested_gates(gate_names, condition_type, user, end_result)
925
959
  has_passing_gate = false
926
960
  is_multi_pass_gate_type = condition_type == Const::CND_MULTI_PASS_GATE
data/lib/statsig.rb CHANGED
@@ -410,7 +410,7 @@ module Statsig
410
410
  def self.get_statsig_metadata
411
411
  {
412
412
  'sdkType' => 'ruby-server',
413
- 'sdkVersion' => '2.9.3',
413
+ 'sdkVersion' => '2.9.4',
414
414
  'languageVersion' => RUBY_VERSION
415
415
  }
416
416
  end
@@ -40,7 +40,7 @@ class StatsigDriver
40
40
  @logger = Statsig::StatsigLogger.new(@net, @options, @err_boundary, @sdk_configs)
41
41
  @persistent_storage_utils = Statsig::UserPersistentStorageUtils.new(@options)
42
42
  @store = Statsig::SpecStore.new(@net, @options, error_callback, @diagnostics, @err_boundary, @logger, secret_key, @sdk_configs)
43
- @evaluator = Statsig::Evaluator.new(@store, @options, @persistent_storage_utils)
43
+ @evaluator = Statsig::Evaluator.new(@store, @options, @persistent_storage_utils, @logger)
44
44
  tracker.end(success: true)
45
45
 
46
46
  @logger.log_diagnostics_event(@diagnostics, 'initialize')
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.9.3
4
+ version: 2.9.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Statsig, Inc
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-08 00:00:00.000000000 Z
11
+ date: 2026-09-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler