statsig 2.5.4 → 2.6.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: 18141eb6b0169f1e1f2a1cb9fac6a249b49c422bab8ced2c7dc09de0a023c2ce
4
- data.tar.gz: 7328411125b434f4e90c8e9900f2f6a9cd5fb811a9089ed677e3cf248f23bb73
3
+ metadata.gz: 2e43e289ee71055ef26a191a9daf4d54eb4f6a5dbacb8eb6a9160d1f66c07470
4
+ data.tar.gz: 7c457bd018afe66c60c7ba2d1091b6b0c3054487d50cae69c02ebf640d367acf
5
5
  SHA512:
6
- metadata.gz: a022ec077aa7ae5e661add0ba003cca65b74521de6f2da467322913a267a47376c2e6a52474b97bab634be0a00e7a0a0cbc9d385a8b8718f3478a46d28fc4045
7
- data.tar.gz: b33efac15e9d651b47140fdeb299188fd0df2a23865014de309f13178e33f11363da0bf766c08b7b8ec2f941984d800929e1ed69b7543286a89e7f7aeecc5bbd
6
+ metadata.gz: 80cf6fe68efe3a458f2e9c6637ea6433d7f6ed93110da01fb0c771ea5cd409623b0f721ecc651810e9146d3796b19c23931396407aae64357795dab6424e7d1d
7
+ data.tar.gz: 496ead0234ed956e4d2077cfea9cd95d755a0f7e036c32c7c9504c727f153a511771465c4a0774b94e4243805c6002056fe96f8434257401770622ff7ae5fe4a
data/lib/config_result.rb CHANGED
@@ -21,6 +21,7 @@ module Statsig
21
21
  attr_accessor :forward_all_exposures
22
22
  attr_accessor :sampling_rate
23
23
  attr_accessor :has_seen_analytical_gates
24
+ attr_accessor :override_config_name
24
25
 
25
26
  def initialize(
26
27
  name:,
@@ -41,7 +42,8 @@ module Statsig
41
42
  include_local_overrides: true,
42
43
  forward_all_exposures: false,
43
44
  sampling_rate: nil,
44
- has_seen_analytical_gates: false
45
+ has_seen_analytical_gates: false,
46
+ override_config_name: nil
45
47
  )
46
48
  @name = name
47
49
  @gate_value = gate_value
@@ -63,6 +65,7 @@ module Statsig
63
65
  @forward_all_exposures = forward_all_exposures
64
66
  @sampling_rate = sampling_rate
65
67
  @has_seen_analytical_gates = has_seen_analytical_gates
68
+ @override_config_name = override_config_name
66
69
  end
67
70
 
68
71
  def self.from_user_persisted_values(config_name, user_persisted_values)
@@ -82,7 +85,8 @@ module Statsig
82
85
  init_time: @init_time,
83
86
  group_name: @group_name,
84
87
  id_type: @id_type,
85
- target_app_ids: @target_app_ids
88
+ target_app_ids: @target_app_ids,
89
+ override_config_name: @override_config_name
86
90
  }
87
91
  end
88
92
  end
data/lib/evaluator.rb CHANGED
@@ -89,6 +89,50 @@ module Statsig
89
89
  nil
90
90
  end
91
91
 
92
+ def try_apply_config_mapping(gate_name, user, end_result, type, salt)
93
+ gate_name_sym = gate_name.to_sym
94
+ return false unless @spec_store.overrides.key?(gate_name_sym)
95
+
96
+ mapping_list = @spec_store.overrides[gate_name_sym]
97
+ mapping_list.each do |mapping|
98
+ rules = mapping[:rules]
99
+ rules.each do |rule|
100
+ start_time = rule[:start_time]
101
+ if !start_time.nil? && start_time > (Time.now.to_i * 1000)
102
+ next
103
+ end
104
+
105
+ rule_name = rule[:rule_name].to_sym
106
+ next unless @spec_store.override_rules.key?(rule_name)
107
+
108
+ override_rule = @spec_store.override_rules[rule_name]
109
+ eval_rule(user, override_rule, end_result)
110
+ unless end_result.gate_value
111
+ next
112
+ end
113
+
114
+ pass = eval_pass_percent(user, override_rule, salt)
115
+ unless pass
116
+ next
117
+ end
118
+
119
+ new_config_name = mapping[:new_config_name]
120
+ end_result.override_config_name = new_config_name
121
+ if type == Const::TYPE_FEATURE_GATE
122
+ check_gate(user, new_config_name, end_result)
123
+ end
124
+ if [Const::TYPE_EXPERIMENT, Const::TYPE_DYNAMIC_CONFIG, Const::TYPE_AUTOTUNE].include?(type)
125
+ get_config(user, new_config_name, end_result)
126
+ end
127
+ if type == Const::TYPE_LAYER
128
+ get_layer(user, new_config_name, end_result)
129
+ end
130
+ return true
131
+ end
132
+ end
133
+ false
134
+ end
135
+
92
136
  def check_gate(user, gate_name, end_result, ignore_local_overrides: false, is_nested: false)
93
137
  unless ignore_local_overrides
94
138
  local_override = lookup_gate_override(gate_name)
@@ -111,11 +155,20 @@ module Statsig
111
155
  end
112
156
 
113
157
  unless @spec_store.has_gate?(gate_name)
158
+ if try_apply_config_mapping(gate_name, user, end_result, Const::TYPE_FEATURE_GATE, Const::EMPTY_STR)
159
+ return
160
+ end
161
+
114
162
  unsupported_or_unrecognized(gate_name, end_result)
115
163
  return
116
164
  end
117
165
 
118
- eval_spec(gate_name, user, @spec_store.get_gate(gate_name), end_result, is_nested: is_nested)
166
+ spec = @spec_store.get_gate(gate_name)
167
+ if try_apply_config_mapping(gate_name, user, end_result, spec[:entity], spec[:salt])
168
+ return
169
+ end
170
+
171
+ eval_spec(gate_name, user, spec, end_result, is_nested: is_nested)
119
172
  end
120
173
 
121
174
  def get_config(user, config_name, end_result, user_persisted_values: nil, ignore_local_overrides: false)
@@ -146,12 +199,20 @@ module Statsig
146
199
  end
147
200
 
148
201
  unless @spec_store.has_config?(config_name)
202
+ if try_apply_config_mapping(config_name, user, end_result, Const::TYPE_DYNAMIC_CONFIG, Const::EMPTY_STR)
203
+ return
204
+ end
205
+
149
206
  unsupported_or_unrecognized(config_name, end_result)
150
207
  return
151
208
  end
152
209
 
153
210
  config = @spec_store.get_config(config_name)
154
211
 
212
+ if try_apply_config_mapping(config_name, user, end_result, config[:entity], config[:salt])
213
+ return
214
+ end
215
+
155
216
  # If persisted values is provided and the experiment is active, return sticky values if exists.
156
217
  if !user_persisted_values.nil? && config[:isActive] == true
157
218
  sticky_values = user_persisted_values[config_name]
@@ -231,7 +292,7 @@ module Statsig
231
292
  group_size = 10_000.0 / cmab[:groups].length
232
293
  group = cmab[:groups][(hash % 10_000) / group_size]
233
294
  end_result.json_value = group[:parameterValues]
234
- end_result.rule_id = group[:id]
295
+ end_result.rule_id = group[:id] + Const::EXPLORE
235
296
  end_result.group_name = group[:name]
236
297
  end_result.is_experiment_group = true
237
298
  finalize_cmab_eval_result(cmab, end_result, did_pass: true)
@@ -338,11 +399,20 @@ module Statsig
338
399
  end
339
400
 
340
401
  unless @spec_store.has_layer?(layer_name)
402
+ if try_apply_config_mapping(layer_name, user, end_result, Const::LAYER, Const::EMPTY_STR)
403
+ return
404
+ end
405
+
341
406
  unsupported_or_unrecognized(layer_name, end_result)
342
407
  return
343
408
  end
344
409
 
345
- eval_spec(layer_name, user, @spec_store.get_layer(layer_name), end_result)
410
+ layer = @spec_store.get_layer(layer_name)
411
+ if try_apply_config_mapping(layer_name, user, end_result, layer[:entity], layer[:salt])
412
+ return
413
+ end
414
+
415
+ eval_spec(layer_name, user, layer, end_result)
346
416
  end
347
417
 
348
418
  def list_gates
@@ -657,6 +727,10 @@ module Statsig
657
727
  when Const::CND_PUBLIC
658
728
  return true
659
729
  when Const::CND_PASS_GATE, Const::CND_FAIL_GATE
730
+ if !target.is_a?(String) || target.empty?
731
+ return type == Const::CND_FAIL_GATE
732
+ end
733
+
660
734
  result = eval_nested_gate(target, user, end_result)
661
735
  if end_result.sampling_rate == nil && !target.start_with?("segment")
662
736
  end_result.has_seen_analytical_gates = true
data/lib/network.rb CHANGED
@@ -53,7 +53,11 @@ module Statsig
53
53
 
54
54
  def download_config_specs(since_time)
55
55
  url = @options.download_config_specs_url
56
- get("#{url}#{@server_secret}.json?sinceTime=#{since_time}")
56
+ dcs_url = "#{url}#{@server_secret}.json"
57
+ if since_time.positive?
58
+ dcs_url += "?sinceTime=#{since_time}"
59
+ end
60
+ get(dcs_url)
57
61
  end
58
62
 
59
63
  def post_logs(events, error_boundary)
data/lib/spec_store.rb CHANGED
@@ -20,6 +20,8 @@ module Statsig
20
20
  attr_accessor :hashed_sdk_keys_to_app_ids
21
21
  attr_accessor :unsupported_configs
22
22
  attr_accessor :cmab_configs
23
+ attr_accessor :overrides
24
+ attr_accessor :override_rules
23
25
 
24
26
  def initialize(network, options, error_callback, diagnostics, error_boundary, logger, secret_key, sdk_config)
25
27
  @init_reason = EvaluationReason::UNINITIALIZED
@@ -40,6 +42,8 @@ module Statsig
40
42
  @experiment_to_layer = {}
41
43
  @sdk_keys_to_app_ids = {}
42
44
  @hashed_sdk_keys_to_app_ids = {}
45
+ @overrides = {}
46
+ @override_rules = {}
43
47
  @diagnostics = diagnostics
44
48
  @error_boundary = error_boundary
45
49
  @logger = logger
@@ -361,6 +365,8 @@ module Statsig
361
365
  @hashed_sdk_keys_to_app_ids = specs_json[:hashed_sdk_keys_to_app_ids] || {}
362
366
  @sdk_configs.set_flags(specs_json[:sdk_flags])
363
367
  @sdk_configs.set_configs(specs_json[:sdk_configs])
368
+ @overrides = specs_json[:overrides] || {}
369
+ @override_rules = specs_json[:override_rules] || {}
364
370
 
365
371
  unless from_adapter
366
372
  save_rulesets_to_storage_adapter(specs_string)
data/lib/statsig.rb CHANGED
@@ -386,7 +386,7 @@ module Statsig
386
386
  def self.get_statsig_metadata
387
387
  {
388
388
  'sdkType' => 'ruby-server',
389
- 'sdkVersion' => '2.5.4',
389
+ 'sdkVersion' => '2.6.0',
390
390
  'languageVersion' => RUBY_VERSION
391
391
  }
392
392
  end
@@ -65,6 +65,9 @@ module Statsig
65
65
  if @debug_info != nil
66
66
  metadata[:debugInfo] = @debug_info
67
67
  end
68
+ unless result.override_config_name.nil?
69
+ metadata[:overrideConfigName] = result.override_config_name
70
+ end
68
71
  return false if not is_unique_exposure(user, $gate_exposure_event, metadata)
69
72
  event.metadata = metadata
70
73
  event.statsig_metadata = {}
@@ -93,6 +96,9 @@ module Statsig
93
96
  if @debug_info != nil
94
97
  metadata[:debugInfo] = @debug_info
95
98
  end
99
+ unless result.override_config_name.nil?
100
+ metadata[:overrideConfigName] = result.override_config_name
101
+ end
96
102
  return false if not is_unique_exposure(user, $config_exposure_event, metadata)
97
103
  event.metadata = metadata
98
104
  event.secondary_exposures = result.secondary_exposures.is_a?(Array) ? result.secondary_exposures : []
@@ -195,7 +201,7 @@ module Statsig
195
201
  events_clone = @events
196
202
  @events = []
197
203
  serialized_events = events_clone.map { |e| e.serialize }
198
-
204
+
199
205
  serialized_events.each_slice(@options.logging_max_buffer_size) do |batch|
200
206
  @network.post_logs(batch, @error_boundary)
201
207
  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.5.4
4
+ version: 2.6.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-06-25 00:00:00.000000000 Z
11
+ date: 2025-10-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -226,14 +226,14 @@ dependencies:
226
226
  requirements:
227
227
  - - "~>"
228
228
  - !ruby/object:Gem::Version
229
- version: 2.15.0
229
+ version: 2.18.0
230
230
  type: :runtime
231
231
  prerelease: false
232
232
  version_requirements: !ruby/object:Gem::Requirement
233
233
  requirements:
234
234
  - - "~>"
235
235
  - !ruby/object:Gem::Version
236
- version: 2.15.0
236
+ version: 2.18.0
237
237
  - !ruby/object:Gem::Dependency
238
238
  name: http
239
239
  requirement: !ruby/object:Gem::Requirement