statsig 1.10.0 → 1.20.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 +4 -4
- data/lib/client_initialize_helpers.rb +132 -0
- data/lib/config_result.rb +16 -3
- data/lib/diagnostics.rb +44 -0
- data/lib/dynamic_config.rb +37 -0
- data/lib/error_boundary.rb +57 -0
- data/lib/evaluation_details.rb +42 -0
- data/lib/evaluation_helpers.rb +1 -0
- data/lib/evaluator.rb +127 -14
- data/lib/id_list.rb +1 -0
- data/lib/interfaces/data_store.rb +19 -0
- data/lib/layer.rb +39 -0
- data/lib/network.rb +39 -11
- data/lib/spec_store.rb +183 -43
- data/lib/statsig.rb +213 -4
- data/lib/statsig_driver.rb +192 -62
- data/lib/statsig_errors.rb +11 -0
- data/lib/statsig_event.rb +6 -1
- data/lib/statsig_logger.rb +81 -13
- data/lib/statsig_options.rb +114 -7
- data/lib/statsig_user.rb +79 -16
- metadata +70 -8
data/lib/statsig_logger.rb
CHANGED
@@ -1,50 +1,69 @@
|
|
1
|
+
# typed: true
|
1
2
|
require 'statsig_event'
|
3
|
+
require 'concurrent-ruby'
|
2
4
|
|
3
5
|
$gate_exposure_event = 'statsig::gate_exposure'
|
4
6
|
$config_exposure_event = 'statsig::config_exposure'
|
5
7
|
$layer_exposure_event = 'statsig::layer_exposure'
|
8
|
+
$diagnostics_event = 'statsig::diagnostics'
|
6
9
|
|
7
10
|
module Statsig
|
8
11
|
class StatsigLogger
|
9
|
-
def initialize(network)
|
12
|
+
def initialize(network, options)
|
10
13
|
@network = network
|
11
14
|
@events = []
|
15
|
+
@options = options
|
16
|
+
|
17
|
+
@logging_pool = Concurrent::ThreadPoolExecutor.new(
|
18
|
+
min_threads: [2, Concurrent.processor_count].min,
|
19
|
+
max_threads: [2, Concurrent.processor_count].max,
|
20
|
+
# max jobs pending before we start dropping
|
21
|
+
max_queue: [2, Concurrent.processor_count].max * 5,
|
22
|
+
fallback_policy: :discard,
|
23
|
+
)
|
24
|
+
|
12
25
|
@background_flush = periodic_flush
|
13
26
|
end
|
14
27
|
|
15
28
|
def log_event(event)
|
16
29
|
@events.push(event)
|
17
|
-
if @events.length >=
|
18
|
-
|
30
|
+
if @events.length >= @options.logging_max_buffer_size
|
31
|
+
flush_async
|
19
32
|
end
|
20
33
|
end
|
21
34
|
|
22
|
-
def log_gate_exposure(user, gate_name, value, rule_id, secondary_exposures)
|
35
|
+
def log_gate_exposure(user, gate_name, value, rule_id, secondary_exposures, eval_details, context = nil)
|
23
36
|
event = StatsigEvent.new($gate_exposure_event)
|
24
37
|
event.user = user
|
25
38
|
event.metadata = {
|
26
39
|
'gate' => gate_name,
|
27
40
|
'gateValue' => value.to_s,
|
28
|
-
'ruleID' => rule_id
|
41
|
+
'ruleID' => rule_id,
|
29
42
|
}
|
30
43
|
event.statsig_metadata = Statsig.get_statsig_metadata
|
31
44
|
event.secondary_exposures = secondary_exposures.is_a?(Array) ? secondary_exposures : []
|
45
|
+
|
46
|
+
safe_add_eval_details(eval_details, event)
|
47
|
+
safe_add_exposure_context(context, event)
|
32
48
|
log_event(event)
|
33
49
|
end
|
34
50
|
|
35
|
-
def log_config_exposure(user, config_name, rule_id, secondary_exposures)
|
51
|
+
def log_config_exposure(user, config_name, rule_id, secondary_exposures, eval_details, context = nil)
|
36
52
|
event = StatsigEvent.new($config_exposure_event)
|
37
53
|
event.user = user
|
38
54
|
event.metadata = {
|
39
55
|
'config' => config_name,
|
40
|
-
'ruleID' => rule_id
|
56
|
+
'ruleID' => rule_id,
|
41
57
|
}
|
42
58
|
event.statsig_metadata = Statsig.get_statsig_metadata
|
43
59
|
event.secondary_exposures = secondary_exposures.is_a?(Array) ? secondary_exposures : []
|
60
|
+
|
61
|
+
safe_add_eval_details(eval_details, event)
|
62
|
+
safe_add_exposure_context(context, event)
|
44
63
|
log_event(event)
|
45
64
|
end
|
46
65
|
|
47
|
-
def log_layer_exposure(user, layer, parameter_name, config_evaluation)
|
66
|
+
def log_layer_exposure(user, layer, parameter_name, config_evaluation, context = nil)
|
48
67
|
exposures = config_evaluation.undelegated_sec_exps
|
49
68
|
allocated_experiment = ''
|
50
69
|
is_explicit = (config_evaluation.explicit_parameters&.include? parameter_name) || false
|
@@ -60,26 +79,46 @@ module Statsig
|
|
60
79
|
'ruleID' => layer.rule_id,
|
61
80
|
'allocatedExperiment' => allocated_experiment,
|
62
81
|
'parameterName' => parameter_name,
|
63
|
-
'isExplicitParameter' => String(is_explicit)
|
82
|
+
'isExplicitParameter' => String(is_explicit),
|
64
83
|
}
|
65
84
|
event.statsig_metadata = Statsig.get_statsig_metadata
|
66
85
|
event.secondary_exposures = exposures.is_a?(Array) ? exposures : []
|
86
|
+
|
87
|
+
safe_add_eval_details(config_evaluation.evaluation_details, event)
|
88
|
+
safe_add_exposure_context(context, event)
|
89
|
+
log_event(event)
|
90
|
+
end
|
91
|
+
|
92
|
+
def log_diagnostics_event(diagnostics, user = nil)
|
93
|
+
event = StatsigEvent.new($diagnostics_event)
|
94
|
+
event.user = user
|
95
|
+
event.metadata = diagnostics.serialize
|
67
96
|
log_event(event)
|
68
97
|
end
|
69
98
|
|
70
99
|
def periodic_flush
|
71
100
|
Thread.new do
|
72
101
|
loop do
|
73
|
-
sleep
|
102
|
+
sleep @options.logging_interval_seconds
|
74
103
|
flush
|
75
104
|
end
|
76
105
|
end
|
77
106
|
end
|
78
107
|
|
79
|
-
def
|
80
|
-
|
81
|
-
|
108
|
+
def shutdown
|
109
|
+
@background_flush&.exit
|
110
|
+
@logging_pool.shutdown
|
111
|
+
@logging_pool.wait_for_termination(timeout = 3)
|
112
|
+
flush
|
113
|
+
end
|
114
|
+
|
115
|
+
def flush_async
|
116
|
+
@logging_pool.post do
|
117
|
+
flush
|
82
118
|
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def flush
|
83
122
|
if @events.length == 0
|
84
123
|
return
|
85
124
|
end
|
@@ -89,5 +128,34 @@ module Statsig
|
|
89
128
|
|
90
129
|
@network.post_logs(flush_events)
|
91
130
|
end
|
131
|
+
|
132
|
+
def maybe_restart_background_threads
|
133
|
+
if @background_flush.nil? or !@background_flush.alive?
|
134
|
+
@background_flush = periodic_flush
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def safe_add_eval_details(eval_details, event)
|
141
|
+
if eval_details.nil?
|
142
|
+
return
|
143
|
+
end
|
144
|
+
|
145
|
+
event.metadata['reason'] = eval_details.reason
|
146
|
+
event.metadata['configSyncTime'] = eval_details.config_sync_time
|
147
|
+
event.metadata['initTime'] = eval_details.init_time
|
148
|
+
event.metadata['serverTime'] = eval_details.server_time
|
149
|
+
end
|
150
|
+
|
151
|
+
def safe_add_exposure_context(context, event)
|
152
|
+
if context.nil?
|
153
|
+
return
|
154
|
+
end
|
155
|
+
|
156
|
+
if context['is_manual_exposure']
|
157
|
+
event.metadata['isManualExposure'] = 'true'
|
158
|
+
end
|
159
|
+
end
|
92
160
|
end
|
93
161
|
end
|
data/lib/statsig_options.rb
CHANGED
@@ -1,17 +1,124 @@
|
|
1
|
+
# typed: true
|
2
|
+
|
3
|
+
require 'sorbet-runtime'
|
4
|
+
require_relative 'interfaces/data_store'
|
5
|
+
|
6
|
+
##
|
7
|
+
# Configuration options for the Statsig SDK.
|
1
8
|
class StatsigOptions
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
sig { returns(T.any(T::Hash[String, String], NilClass)) }
|
12
|
+
# Hash you can use to set environment variables that apply to all of your users in
|
13
|
+
# the same session and will be used for targeting purposes.
|
14
|
+
# eg. { "tier" => "development" }
|
15
|
+
attr_accessor :environment
|
16
|
+
|
17
|
+
sig { returns(String) }
|
18
|
+
# The base url used to make network calls to Statsig.
|
19
|
+
# default: https://statsigapi.net/v1
|
20
|
+
attr_accessor :api_url_base
|
21
|
+
|
22
|
+
sig { returns(T.any(Float, Integer)) }
|
23
|
+
# The interval (in seconds) to poll for changes to your Statsig configuration
|
24
|
+
# default: 10s
|
25
|
+
attr_accessor :rulesets_sync_interval
|
26
|
+
|
27
|
+
sig { returns(T.any(Float, Integer)) }
|
28
|
+
# The interval (in seconds) to poll for changes to your id lists
|
29
|
+
# default: 60s
|
30
|
+
attr_accessor :idlists_sync_interval
|
31
|
+
|
32
|
+
sig { returns(T.any(Float, Integer)) }
|
33
|
+
# How often to flush logs to Statsig
|
34
|
+
# default: 60s
|
35
|
+
attr_accessor :logging_interval_seconds
|
36
|
+
|
37
|
+
sig { returns(Integer) }
|
38
|
+
# The maximum number of events to batch before flushing logs to the server
|
39
|
+
# default: 1000
|
40
|
+
attr_accessor :logging_max_buffer_size
|
41
|
+
|
42
|
+
sig { returns(T::Boolean) }
|
43
|
+
# Restricts the SDK to not issue any network requests and only respond with default values (or local overrides)
|
44
|
+
# default: false
|
45
|
+
attr_accessor :local_mode
|
46
|
+
|
47
|
+
sig { returns(T.any(String, NilClass)) }
|
48
|
+
# A string that represents all rules for all feature gates, dynamic configs and experiments.
|
49
|
+
# It can be provided to bootstrap the Statsig server SDK at initialization in case your server runs
|
50
|
+
# into network issue or Statsig is down temporarily.
|
51
|
+
attr_accessor :bootstrap_values
|
52
|
+
|
53
|
+
sig { returns(T.any(Method, Proc, NilClass)) }
|
54
|
+
# A callback function that will be called anytime the rulesets are updated.
|
55
|
+
attr_accessor :rules_updated_callback
|
56
|
+
|
57
|
+
sig { returns(T.any(Statsig::Interfaces::IDataStore, NilClass)) }
|
58
|
+
# A class that extends IDataStore. Can be used to provide values from a
|
59
|
+
# common data store (like Redis) to initialize the Statsig SDK.
|
60
|
+
attr_accessor :data_store
|
61
|
+
|
62
|
+
sig { returns(Integer) }
|
63
|
+
# The number of threads allocated to syncing IDLists.
|
64
|
+
# default: 3
|
65
|
+
attr_accessor :idlist_threadpool_size
|
66
|
+
|
67
|
+
sig { returns(T::Boolean) }
|
68
|
+
# Should diagnostics be logged. These include performance metrics for initialize.
|
69
|
+
# default: false
|
70
|
+
attr_accessor :disable_diagnostics_logging
|
71
|
+
|
72
|
+
sig { returns(T::Boolean) }
|
73
|
+
# Statsig utilizes Sorbet (https://sorbet.org) to ensure type safety of the SDK. This includes logging
|
74
|
+
# to console when errors are detected. You can disable this logging by setting this flag to true.
|
75
|
+
# default: false
|
76
|
+
attr_accessor :disable_sorbet_logging_handlers
|
77
|
+
|
78
|
+
sig do
|
79
|
+
params(
|
80
|
+
environment: T.any(T::Hash[String, String], NilClass),
|
81
|
+
api_url_base: String,
|
82
|
+
rulesets_sync_interval: T.any(Float, Integer),
|
83
|
+
idlists_sync_interval: T.any(Float, Integer),
|
84
|
+
logging_interval_seconds: T.any(Float, Integer),
|
85
|
+
logging_max_buffer_size: Integer,
|
86
|
+
local_mode: T::Boolean,
|
87
|
+
bootstrap_values: T.any(String, NilClass),
|
88
|
+
rules_updated_callback: T.any(Method, Proc, NilClass),
|
89
|
+
data_store: T.any(Statsig::Interfaces::IDataStore, NilClass),
|
90
|
+
idlist_threadpool_size: Integer,
|
91
|
+
disable_diagnostics_logging: T::Boolean,
|
92
|
+
disable_sorbet_logging_handlers: T::Boolean
|
93
|
+
).void
|
94
|
+
end
|
6
95
|
|
7
96
|
def initialize(
|
8
|
-
environment=nil,
|
9
|
-
api_url_base='https://statsigapi.net/v1',
|
97
|
+
environment = nil,
|
98
|
+
api_url_base = 'https://statsigapi.net/v1',
|
10
99
|
rulesets_sync_interval: 10,
|
11
|
-
idlists_sync_interval: 60
|
100
|
+
idlists_sync_interval: 60,
|
101
|
+
logging_interval_seconds: 60,
|
102
|
+
logging_max_buffer_size: 1000,
|
103
|
+
local_mode: false,
|
104
|
+
bootstrap_values: nil,
|
105
|
+
rules_updated_callback: nil,
|
106
|
+
data_store: nil,
|
107
|
+
idlist_threadpool_size: 3,
|
108
|
+
disable_diagnostics_logging: false,
|
109
|
+
disable_sorbet_logging_handlers: false)
|
12
110
|
@environment = environment.is_a?(Hash) ? environment : nil
|
13
111
|
@api_url_base = api_url_base
|
14
112
|
@rulesets_sync_interval = rulesets_sync_interval
|
15
113
|
@idlists_sync_interval = idlists_sync_interval
|
114
|
+
@logging_interval_seconds = logging_interval_seconds
|
115
|
+
@logging_max_buffer_size = [logging_max_buffer_size, 1000].min
|
116
|
+
@local_mode = local_mode
|
117
|
+
@bootstrap_values = bootstrap_values
|
118
|
+
@rules_updated_callback = rules_updated_callback
|
119
|
+
@data_store = data_store
|
120
|
+
@idlist_threadpool_size = idlist_threadpool_size
|
121
|
+
@disable_diagnostics_logging = disable_diagnostics_logging
|
122
|
+
@disable_sorbet_logging_handlers = disable_sorbet_logging_handlers
|
16
123
|
end
|
17
124
|
end
|
data/lib/statsig_user.rb
CHANGED
@@ -1,40 +1,78 @@
|
|
1
|
+
# typed: true
|
2
|
+
|
3
|
+
require 'sorbet-runtime'
|
4
|
+
|
5
|
+
##
|
6
|
+
# The user object to be evaluated against your Statsig configurations (gates/experiments/dynamic configs).
|
1
7
|
class StatsigUser
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { returns(T.any(String, NilClass)) }
|
11
|
+
# An identifier for this user. Evaluated against the User ID criteria. (https://docs.statsig.com/feature-gates/conditions#userid)
|
2
12
|
attr_accessor :user_id
|
13
|
+
|
14
|
+
sig { returns(T.any(String, NilClass)) }
|
15
|
+
# An identifier for this user. Evaluated against the Email criteria. (https://docs.statsig.com/feature-gates/conditions#email)
|
3
16
|
attr_accessor :email
|
17
|
+
|
18
|
+
sig { returns(T.any(String, NilClass)) }
|
19
|
+
# An IP address associated with this user. Evaluated against the IP Address criteria. (https://docs.statsig.com/feature-gates/conditions#ip)
|
4
20
|
attr_accessor :ip
|
21
|
+
|
22
|
+
sig { returns(T.any(String, NilClass)) }
|
23
|
+
# A user agent string associated with this user. Evaluated against Browser Version and Name (https://docs.statsig.com/feature-gates/conditions#browser-version)
|
5
24
|
attr_accessor :user_agent
|
25
|
+
|
26
|
+
sig { returns(T.any(String, NilClass)) }
|
27
|
+
# The country code associated with this user (e.g New Zealand => NZ). Evaluated against the Country criteria. (https://docs.statsig.com/feature-gates/conditions#country)
|
6
28
|
attr_accessor :country
|
29
|
+
|
30
|
+
sig { returns(T.any(String, NilClass)) }
|
31
|
+
# An locale for this user.
|
7
32
|
attr_accessor :locale
|
33
|
+
|
34
|
+
sig { returns(T.any(String, NilClass)) }
|
35
|
+
# The current app version the user is interacting with. Evaluated against the App Version criteria. (https://docs.statsig.com/feature-gates/conditions#app-version)
|
8
36
|
attr_accessor :app_version
|
37
|
+
|
38
|
+
sig { returns(T.any(T::Hash[String, String], NilClass)) }
|
39
|
+
# A Hash you can use to set environment variables that apply to this user. e.g. { "tier" => "development" }
|
9
40
|
attr_accessor :statsig_environment
|
41
|
+
|
42
|
+
sig { returns(T.any(T::Hash[String, String], NilClass)) }
|
43
|
+
# Any Custom IDs to associated with the user. (See https://docs.statsig.com/guides/experiment-on-custom-id-types)
|
10
44
|
attr_accessor :custom_ids
|
45
|
+
|
46
|
+
sig { returns(T.any(T::Hash[String, String], NilClass)) }
|
47
|
+
# Any value you wish to use in evaluation, but do not want logged with events, can be stored in this field.
|
11
48
|
attr_accessor :private_attributes
|
12
49
|
|
50
|
+
sig { returns(T.any(T::Hash[String, T.untyped], NilClass)) }
|
51
|
+
|
13
52
|
def custom
|
14
53
|
@custom
|
15
54
|
end
|
16
55
|
|
56
|
+
sig { params(value: T.any(T::Hash[String, T.untyped], NilClass)).void }
|
57
|
+
# Any custom fields for this user. Evaluated against the Custom criteria. (https://docs.statsig.com/feature-gates/conditions#custom)
|
17
58
|
def custom=(value)
|
18
59
|
@custom = value.is_a?(Hash) ? value : Hash.new
|
19
60
|
end
|
20
61
|
|
62
|
+
sig { params(user_hash: T.any(T::Hash[T.any(String, Symbol), T.untyped], NilClass)).void }
|
63
|
+
|
21
64
|
def initialize(user_hash)
|
22
|
-
@
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@statsig_environment = user_hash['statsigEnvironment']
|
34
|
-
@private_attributes = user_hash['privateAttributes'] if user_hash['privateAttributes'].is_a? Hash
|
35
|
-
custom_ids = user_hash['customIDs'] || user_hash['custom_ids']
|
36
|
-
@custom_ids = custom_ids if custom_ids.is_a? Hash
|
37
|
-
end
|
65
|
+
@user_id = from_hash(user_hash, [:user_id, :userID], String)
|
66
|
+
@email = from_hash(user_hash, [:email], String)
|
67
|
+
@ip = from_hash(user_hash, [:ip], String)
|
68
|
+
@user_agent = from_hash(user_hash, [:user_agent, :userAgent], String)
|
69
|
+
@country = from_hash(user_hash, [:country], String)
|
70
|
+
@locale = from_hash(user_hash, [:locale], String)
|
71
|
+
@app_version = from_hash(user_hash, [:app_version, :appVersion], String)
|
72
|
+
@custom = from_hash(user_hash, [:custom], Hash)
|
73
|
+
@private_attributes = from_hash(user_hash, [:private_attributes, :privateAttributes], Hash)
|
74
|
+
@custom_ids = from_hash(user_hash, [:custom_ids, :customIDs], Hash)
|
75
|
+
@statsig_environment = from_hash(user_hash, [:statsig_environment, :statsigEnvironment], Hash)
|
38
76
|
end
|
39
77
|
|
40
78
|
def serialize(for_logging)
|
@@ -76,4 +114,29 @@ class StatsigUser
|
|
76
114
|
'privateAttributes' => @private_attributes,
|
77
115
|
}
|
78
116
|
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
sig {
|
121
|
+
params(user_hash: T.any(T::Hash[T.any(String, Symbol), T.untyped], NilClass),
|
122
|
+
keys: T::Array[Symbol],
|
123
|
+
type: T.untyped)
|
124
|
+
.returns(T.untyped)
|
125
|
+
}
|
126
|
+
# Pulls fields from the user hash via Symbols and Strings
|
127
|
+
def from_hash(user_hash, keys, type)
|
128
|
+
if user_hash.nil?
|
129
|
+
return nil
|
130
|
+
end
|
131
|
+
|
132
|
+
keys.each do |key|
|
133
|
+
val = user_hash[key] || user_hash[key.to_s]
|
134
|
+
if not val.nil? and val.is_a? type
|
135
|
+
return val
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
nil
|
140
|
+
end
|
141
|
+
|
79
142
|
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: 1.
|
4
|
+
version: 1.20.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:
|
11
|
+
date: 2023-01-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '2.
|
19
|
+
version: '2.0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '2.
|
26
|
+
version: '2.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: webmock
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +66,34 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '1.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sorbet
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.5.10461
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.5.10461
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: tapioca
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.4.27
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.4.27
|
69
97
|
- !ruby/object:Gem::Dependency
|
70
98
|
name: user_agent_parser
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -102,34 +130,68 @@ dependencies:
|
|
102
130
|
version: '6.0'
|
103
131
|
- !ruby/object:Gem::Dependency
|
104
132
|
name: ip3country
|
133
|
+
requirement: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - '='
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: 0.1.1
|
138
|
+
type: :runtime
|
139
|
+
prerelease: false
|
140
|
+
version_requirements: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - '='
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: 0.1.1
|
145
|
+
- !ruby/object:Gem::Dependency
|
146
|
+
name: sorbet-runtime
|
147
|
+
requirement: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - "~>"
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: 0.5.10461
|
152
|
+
type: :runtime
|
153
|
+
prerelease: false
|
154
|
+
version_requirements: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - "~>"
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: 0.5.10461
|
159
|
+
- !ruby/object:Gem::Dependency
|
160
|
+
name: concurrent-ruby
|
105
161
|
requirement: !ruby/object:Gem::Requirement
|
106
162
|
requirements:
|
107
163
|
- - "~>"
|
108
164
|
- !ruby/object:Gem::Version
|
109
|
-
version: '
|
165
|
+
version: '1.1'
|
110
166
|
type: :runtime
|
111
167
|
prerelease: false
|
112
168
|
version_requirements: !ruby/object:Gem::Requirement
|
113
169
|
requirements:
|
114
170
|
- - "~>"
|
115
171
|
- !ruby/object:Gem::Version
|
116
|
-
version: '
|
172
|
+
version: '1.1'
|
117
173
|
description: Statsig server SDK for feature gates and experimentation in Ruby
|
118
174
|
email: support@statsig.com
|
119
175
|
executables: []
|
120
176
|
extensions: []
|
121
177
|
extra_rdoc_files: []
|
122
178
|
files:
|
179
|
+
- lib/client_initialize_helpers.rb
|
123
180
|
- lib/config_result.rb
|
181
|
+
- lib/diagnostics.rb
|
124
182
|
- lib/dynamic_config.rb
|
183
|
+
- lib/error_boundary.rb
|
184
|
+
- lib/evaluation_details.rb
|
125
185
|
- lib/evaluation_helpers.rb
|
126
186
|
- lib/evaluator.rb
|
127
187
|
- lib/id_list.rb
|
188
|
+
- lib/interfaces/data_store.rb
|
128
189
|
- lib/layer.rb
|
129
190
|
- lib/network.rb
|
130
191
|
- lib/spec_store.rb
|
131
192
|
- lib/statsig.rb
|
132
193
|
- lib/statsig_driver.rb
|
194
|
+
- lib/statsig_errors.rb
|
133
195
|
- lib/statsig_event.rb
|
134
196
|
- lib/statsig_logger.rb
|
135
197
|
- lib/statsig_options.rb
|
@@ -146,14 +208,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
146
208
|
requirements:
|
147
209
|
- - ">="
|
148
210
|
- !ruby/object:Gem::Version
|
149
|
-
version:
|
211
|
+
version: 2.5.0
|
150
212
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
213
|
requirements:
|
152
214
|
- - ">="
|
153
215
|
- !ruby/object:Gem::Version
|
154
216
|
version: '0'
|
155
217
|
requirements: []
|
156
|
-
rubygems_version: 3.
|
218
|
+
rubygems_version: 3.3.26
|
157
219
|
signing_key:
|
158
220
|
specification_version: 4
|
159
221
|
summary: Statsig server SDK for Ruby
|