statsig 2.5.5 → 2.8.3

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: c340de8172bac9c10e0af76b1b34dce4dc812b19f9e33fbf19e67b54ddb90f4a
4
- data.tar.gz: a49b6298cc34ff28a2133ecc96518e66f4c622bb65f163a6468eb6310e53f027
3
+ metadata.gz: a17857d17823d2f8d7f6a13d01c4f3d117e4187233d377441c4e7be7a6499ef1
4
+ data.tar.gz: 34bb6a509d1fca2ab2973edf7ce0f76814440c5197649a68af8c573130ef13d5
5
5
  SHA512:
6
- metadata.gz: f4182f35cff27c25b80d1add5f4e7482021f834711a9a31d99ade432b9366a0f56da18b8a5022797fd9105a87efee40ef19ed3e243ce9d17d94259613f34dec9
7
- data.tar.gz: 456e3f74f8cbb17f0be3aa256381349214df363298603da260bae951b570c8ffe4eac9bf2bc8156af31e6a6c4404e349e3bbbb664606c81f4fc23b1265b99ea5
6
+ metadata.gz: eef23c96fefa5aff93eec2ce8562a45ccb59434bfb68d264f39b50df987689c402286895103295b701ba6f71db970f43bd8832f608a2da0622a5bde1717d9521
7
+ data.tar.gz: 7df3f01db686cf387e30b7d45be967be4d9376340d64506359c0df6a0a9b169f45ef310a7b727b34f87cb9c4ddb97f7ba0a80daf7e79d8102f256f83f32369cd
data/lib/api_config.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'constants'
1
+ require_relative 'constants'
2
2
 
3
3
  class UnsupportedConfigException < StandardError
4
4
  end
@@ -1,7 +1,6 @@
1
+ require_relative 'constants'
1
2
  require_relative 'hash_utils'
2
3
 
3
- require 'constants'
4
-
5
4
  module Statsig
6
5
  class ResponseFormatter
7
6
  def self.get_responses(
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
@@ -1,4 +1,4 @@
1
- require 'statsig_errors'
1
+ require_relative 'statsig_errors'
2
2
 
3
3
  $endpoint = 'https://statsigapi.net/v1/sdk_exception'
4
4
 
data/lib/evaluator.rb CHANGED
@@ -1,15 +1,15 @@
1
- require 'config_result'
2
1
  require 'country_lookup'
3
2
  require 'digest'
4
- require 'evaluation_helpers'
5
- require 'client_initialize_helpers'
6
- require 'spec_store'
7
3
  require 'time'
8
- require 'ua_parser'
9
- require 'evaluation_details'
10
4
  require 'user_agent_parser/operating_system'
11
- require 'user_persistent_storage_utils'
12
- require 'constants'
5
+ require_relative 'client_initialize_helpers'
6
+ require_relative 'config_result'
7
+ require_relative 'constants'
8
+ require_relative 'evaluation_details'
9
+ require_relative 'evaluation_helpers'
10
+ require_relative 'spec_store'
11
+ require_relative 'ua_parser'
12
+ require_relative 'user_persistent_storage_utils'
13
13
 
14
14
  module Statsig
15
15
  class Evaluator
@@ -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]
@@ -317,7 +378,7 @@ module Statsig
317
378
  unless config.nil?
318
379
  cur_count += config[:records]
319
380
  end
320
- sum += 1.0 / (cur_count / total_records)
381
+ sum += 1.0 / cur_count / total_records
321
382
  next unless value < sum
322
383
 
323
384
  end_result.json_value = group[:parameterValues]
@@ -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
@@ -479,7 +549,7 @@ module Statsig
479
549
  value: rule[:returnValue],
480
550
  group_name: rule[:groupName],
481
551
  rule_id: rule[:id],
482
- evaluation_details: EvaluationDetails.local_override(@config_sync_time, @init_time)
552
+ evaluation_details: EvaluationDetails.local_override(@spec_store.last_config_sync_time, @spec_store.initial_config_sync_time)
483
553
  }
484
554
  return
485
555
  end
@@ -490,7 +560,7 @@ module Statsig
490
560
  value: {},
491
561
  group_name: group_name,
492
562
  rule_id: "#{experiment_name}:override",
493
- evaluation_details: EvaluationDetails.local_override(@config_sync_time, @init_time)
563
+ evaluation_details: EvaluationDetails.local_override(@spec_store.last_config_sync_time, @spec_store.initial_config_sync_time)
494
564
  }
495
565
  end
496
566
 
@@ -498,6 +568,10 @@ module Statsig
498
568
  @experiment_overrides.clear
499
569
  end
500
570
 
571
+ def remove_experiment_override(experiment_name)
572
+ @experiment_overrides.delete(experiment_name.to_sym)
573
+ end
574
+
501
575
  def eval_spec(config_name, user, config, end_result, is_nested: false)
502
576
  config[:rules].each do |rule|
503
577
  end_result.sampling_rate = rule[:samplingRate]
@@ -525,7 +599,7 @@ module Statsig
525
599
  end_result.target_app_ids = config[:targetAppIDs]
526
600
  end_result.gate_value = did_pass
527
601
  end_result.forward_all_exposures = config[:forwardAllExposures]
528
- if config[:entity] == Const::TYPE_FEATURE_GATE
602
+ if config[:entity] == Const::TYPE_FEATURE_GATE || config[:entity] == Const::TYPE_HOLDOUT
529
603
  end_result.gate_value = did_pass ? rule[:returnValue] == true : config[:defaultValue] == true
530
604
  end
531
605
  end_result.config_version = config[:version]
@@ -657,6 +731,10 @@ module Statsig
657
731
  when Const::CND_PUBLIC
658
732
  return true
659
733
  when Const::CND_PASS_GATE, Const::CND_FAIL_GATE
734
+ if !target.is_a?(String) || target.empty?
735
+ return type == Const::CND_FAIL_GATE
736
+ end
737
+
660
738
  result = eval_nested_gate(target, user, end_result)
661
739
  if end_result.sampling_rate == nil && !target.start_with?("segment")
662
740
  end_result.has_seen_analytical_gates = true
data/lib/hash_utils.rb CHANGED
@@ -1,5 +1,5 @@
1
- require 'json'
2
1
  require 'digest'
2
+ require 'json'
3
3
 
4
4
  TWO_TO_THE_63 = 1 << 63
5
5
  TWO_TO_THE_64 = 1 << 64
data/lib/network.rb CHANGED
@@ -1,10 +1,9 @@
1
+ require 'connection_pool'
1
2
  require 'http'
2
3
  require 'json'
3
4
  require 'securerandom'
4
5
  require 'zlib'
5
6
 
6
- require 'connection_pool'
7
-
8
7
  RETRY_CODES = [408, 500, 502, 503, 504, 522, 524, 599].freeze
9
8
 
10
9
  module Statsig
@@ -53,7 +52,11 @@ module Statsig
53
52
 
54
53
  def download_config_specs(since_time)
55
54
  url = @options.download_config_specs_url
56
- get("#{url}#{@server_secret}.json?sinceTime=#{since_time}")
55
+ dcs_url = "#{url}#{@server_secret}.json"
56
+ if since_time.positive?
57
+ dcs_url += "?sinceTime=#{since_time}"
58
+ end
59
+ get(dcs_url)
57
60
  end
58
61
 
59
62
  def post_logs(events, error_boundary)
@@ -64,6 +67,10 @@ module Statsig
64
67
  gzip << json_body
65
68
 
66
69
  response, e = post(url, gzip.close.string, @post_logs_retry_limit, 1, true, event_count)
70
+
71
+ # Consume response body to ensure connection can be closed.
72
+ response&.flush
73
+
67
74
  unless e == nil
68
75
  message = "Failed to log #{event_count} events after #{@post_logs_retry_limit} retries"
69
76
  puts "[Statsig]: #{message}"
data/lib/spec_store.rb CHANGED
@@ -1,10 +1,10 @@
1
+ require 'concurrent-ruby'
1
2
  require 'net/http'
2
3
  require 'uri'
3
- require 'evaluation_details'
4
- require 'id_list'
5
- require 'concurrent-ruby'
6
- require 'hash_utils'
7
- require 'api_config'
4
+ require_relative 'api_config'
5
+ require_relative 'evaluation_details'
6
+ require_relative 'hash_utils'
7
+ require_relative 'id_list'
8
8
 
9
9
  module Statsig
10
10
  class SpecStore
@@ -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
@@ -1,6 +1,5 @@
1
- require 'statsig_driver'
2
-
3
- require 'statsig_errors'
1
+ require_relative 'statsig_driver'
2
+ require_relative 'statsig_errors'
4
3
 
5
4
  module Statsig
6
5
 
@@ -92,6 +91,11 @@ module Statsig
92
91
  @shared_instance&.manually_log_gate_exposure(user, gate_name)
93
92
  end
94
93
 
94
+ def self.get_fields_used_for_gate(gate_name)
95
+ ensure_initialized
96
+ @shared_instance&.get_fields_used_for_gate(gate_name)
97
+ end
98
+
95
99
  class GetConfigOptions
96
100
  attr_accessor :disable_log_exposure, :disable_evaluation_details, :ignore_local_overrides
97
101
 
@@ -136,6 +140,11 @@ module Statsig
136
140
  @shared_instance&.manually_log_config_exposure(user, dynamic_config)
137
141
  end
138
142
 
143
+ def self.get_fields_used_for_config(config_name)
144
+ ensure_initialized
145
+ @shared_instance&.get_fields_used_for_config(config_name)
146
+ end
147
+
139
148
  class GetExperimentOptions
140
149
  attr_accessor :disable_log_exposure, :user_persisted_values, :disable_evaluation_details, :ignore_local_overrides
141
150
 
@@ -184,6 +193,11 @@ module Statsig
184
193
  @shared_instance&.get_user_persisted_values(user, id_type)
185
194
  end
186
195
 
196
+ def self.get_fields_used_for_experiment(experiment_name)
197
+ ensure_initialized
198
+ @shared_instance&.get_fields_used_for_config(experiment_name)
199
+ end
200
+
187
201
  class GetLayerOptions
188
202
  attr_accessor :disable_log_exposure, :disable_evaluation_details
189
203
 
@@ -227,6 +241,11 @@ module Statsig
227
241
  @shared_instance&.manually_log_layer_parameter_exposure(user, layer_name, parameter_name)
228
242
  end
229
243
 
244
+ def self.get_fields_used_for_layer(layer_name)
245
+ ensure_initialized
246
+ @shared_instance&.get_fields_used_for_layer(layer_name)
247
+ end
248
+
230
249
  ##
231
250
  # Logs an event to Statsig with the provided values.
232
251
  #
@@ -354,6 +373,11 @@ module Statsig
354
373
  @shared_instance&.clear_experiment_overrides
355
374
  end
356
375
 
376
+ def self.remove_experiment_override(experiment_name)
377
+ ensure_initialized
378
+ @shared_instance&.remove_experiment_override(experiment_name)
379
+ end
380
+
357
381
  ##
358
382
  # @param [HashTable] debug information log with exposure events
359
383
  def self.set_debug_info(debug_info)
@@ -386,7 +410,7 @@ module Statsig
386
410
  def self.get_statsig_metadata
387
411
  {
388
412
  'sdkType' => 'ruby-server',
389
- 'sdkVersion' => '2.5.5',
413
+ 'sdkVersion' => '2.8.3',
390
414
  'languageVersion' => RUBY_VERSION
391
415
  }
392
416
  end
@@ -1,19 +1,20 @@
1
- require 'config_result'
2
- require 'evaluator'
3
- require 'network'
4
- require 'statsig_errors'
5
- require 'statsig_event'
6
- require 'statsig_logger'
7
- require 'statsig_options'
8
- require 'statsig_user'
9
- require 'spec_store'
10
- require 'dynamic_config'
11
- require 'feature_gate'
12
- require 'error_boundary'
13
- require 'layer'
14
- require 'memo'
15
- require 'diagnostics'
16
- require 'sdk_configs'
1
+ require_relative 'api_config'
2
+ require_relative 'config_result'
3
+ require_relative 'diagnostics'
4
+ require_relative 'dynamic_config'
5
+ require_relative 'error_boundary'
6
+ require_relative 'evaluator'
7
+ require_relative 'feature_gate'
8
+ require_relative 'layer'
9
+ require_relative 'memo'
10
+ require_relative 'network'
11
+ require_relative 'sdk_configs'
12
+ require_relative 'spec_store'
13
+ require_relative 'statsig_errors'
14
+ require_relative 'statsig_event'
15
+ require_relative 'statsig_logger'
16
+ require_relative 'statsig_options'
17
+ require_relative 'statsig_user'
17
18
 
18
19
  class StatsigDriver
19
20
 
@@ -117,6 +118,15 @@ class StatsigDriver
117
118
  end
118
119
  end
119
120
 
121
+ def get_fields_used_for_gate(gate_name)
122
+ @err_boundary.capture(caller: __method__, recover: -> { [] }) do
123
+ gate = @store.get_gate(gate_name)
124
+ return [] if gate.nil?
125
+
126
+ gate[:fieldsUsed] || []
127
+ end
128
+ end
129
+
120
130
  def get_config(user, dynamic_config_name, options = nil)
121
131
  @err_boundary.capture(caller: __method__, recover: -> { DynamicConfig.new(dynamic_config_name) }) do
122
132
  run_with_diagnostics(caller: :get_config) do
@@ -132,6 +142,15 @@ class StatsigDriver
132
142
  end
133
143
  end
134
144
 
145
+ def get_fields_used_for_config(config_name)
146
+ @err_boundary.capture(caller: __method__, recover: -> { [] }) do
147
+ config = @store.get_config(config_name)
148
+ return [] if config.nil?
149
+
150
+ config[:fieldsUsed] || []
151
+ end
152
+ end
153
+
135
154
  def get_experiment(user, experiment_name, options = nil)
136
155
  @err_boundary.capture(caller: __method__, recover: -> { DynamicConfig.new(experiment_name) }) do
137
156
  run_with_diagnostics(caller: :get_experiment) do
@@ -201,6 +220,15 @@ class StatsigDriver
201
220
  end
202
221
  end
203
222
 
223
+ def get_fields_used_for_layer(layer_name)
224
+ @err_boundary.capture(caller: __method__, recover: -> { [] }) do
225
+ layer = @store.get_layer(layer_name)
226
+ return [] if layer.nil?
227
+
228
+ layer[:fieldsUsed] || []
229
+ end
230
+ end
231
+
204
232
  def log_event(user, event_name, value = nil, metadata = nil)
205
233
  @err_boundary.capture(caller: __method__) do
206
234
  if !user.nil? && !user.instance_of?(StatsigUser)
@@ -310,6 +338,12 @@ class StatsigDriver
310
338
  end
311
339
  end
312
340
 
341
+ def remove_experiment_override(experiment_name)
342
+ @err_boundary.capture(caller: __method__) do
343
+ @evaluator.remove_experiment_override(experiment_name)
344
+ end
345
+ end
346
+
313
347
  def set_debug_info(debug_info)
314
348
  @err_boundary.capture(caller: __method__) do
315
349
  @logger.set_debug_info(debug_info)
@@ -1,8 +1,8 @@
1
- require 'constants'
2
- require 'statsig_event'
3
- require 'ttl_set'
4
1
  require 'concurrent-ruby'
5
- require 'hash_utils'
2
+ require_relative 'constants'
3
+ require_relative 'hash_utils'
4
+ require_relative 'statsig_event'
5
+ require_relative 'ttl_set'
6
6
 
7
7
  $gate_exposure_event = 'statsig::gate_exposure'
8
8
  $config_exposure_event = 'statsig::config_exposure'
@@ -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
data/lib/statsig_user.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require 'json'
2
- require 'constants'
2
+ require_relative 'constants'
3
3
  ##
4
4
  # The user object to be evaluated against your Statsig configurations (gates/experiments/dynamic configs).
5
5
  class StatsigUser
@@ -1,4 +1,4 @@
1
- require 'statsig_options'
1
+ require_relative 'statsig_options'
2
2
 
3
3
  module Statsig
4
4
  class UserPersistentStorageUtils
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.5
4
+ version: 2.8.3
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-08-11 00:00:00.000000000 Z
11
+ date: 2026-01-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler