statsig 1.30.0 → 1.32.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/api_config.rb +128 -0
- data/lib/client_initialize_helpers.rb +79 -108
- data/lib/config_result.rb +17 -32
- data/lib/constants.rb +60 -0
- data/lib/diagnostics.rb +21 -70
- data/lib/dynamic_config.rb +1 -24
- data/lib/error_boundary.rb +0 -5
- data/lib/evaluation_details.rb +5 -1
- data/lib/evaluation_helpers.rb +35 -3
- data/lib/evaluator.rb +332 -316
- data/lib/feature_gate.rb +0 -24
- data/lib/id_list.rb +1 -1
- data/lib/interfaces/data_store.rb +1 -1
- data/lib/interfaces/user_persistent_storage.rb +1 -1
- data/lib/layer.rb +1 -20
- data/lib/network.rb +6 -53
- data/lib/spec_store.rb +124 -115
- data/lib/statsig.rb +72 -97
- data/lib/statsig_driver.rb +73 -128
- data/lib/statsig_errors.rb +1 -1
- data/lib/statsig_event.rb +8 -8
- data/lib/statsig_logger.rb +29 -26
- data/lib/statsig_options.rb +0 -50
- data/lib/statsig_user.rb +27 -68
- data/lib/ua_parser.rb +1 -1
- data/lib/uri_helper.rb +1 -9
- data/lib/user_persistent_storage_utils.rb +0 -17
- metadata +4 -2
data/lib/statsig_user.rb
CHANGED
@@ -1,65 +1,49 @@
|
|
1
|
-
# typed: true
|
2
|
-
|
3
|
-
require 'sorbet-runtime'
|
4
1
|
require 'json'
|
2
|
+
require 'constants'
|
5
3
|
|
6
4
|
##
|
7
5
|
# The user object to be evaluated against your Statsig configurations (gates/experiments/dynamic configs).
|
8
6
|
class StatsigUser
|
9
|
-
extend T::Sig
|
10
7
|
|
11
|
-
sig { returns(T.any(String, NilClass)) }
|
12
8
|
# An identifier for this user. Evaluated against the User ID criteria. (https://docs.statsig.com/feature-gates/conditions#userid)
|
13
9
|
attr_accessor :user_id
|
14
10
|
|
15
|
-
sig { returns(T.any(String, NilClass)) }
|
16
11
|
# An identifier for this user. Evaluated against the Email criteria. (https://docs.statsig.com/feature-gates/conditions#email)
|
17
12
|
attr_accessor :email
|
18
13
|
|
19
|
-
sig { returns(T.any(String, NilClass)) }
|
20
14
|
# An IP address associated with this user. Evaluated against the IP Address criteria. (https://docs.statsig.com/feature-gates/conditions#ip)
|
21
15
|
attr_accessor :ip
|
22
16
|
|
23
|
-
sig { returns(T.any(String, NilClass)) }
|
24
17
|
# A user agent string associated with this user. Evaluated against Browser Version and Name (https://docs.statsig.com/feature-gates/conditions#browser-version)
|
25
18
|
attr_accessor :user_agent
|
26
19
|
|
27
|
-
sig { returns(T.any(String, NilClass)) }
|
28
20
|
# 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)
|
29
21
|
attr_accessor :country
|
30
22
|
|
31
|
-
sig { returns(T.any(String, NilClass)) }
|
32
23
|
# An locale for this user.
|
33
24
|
attr_accessor :locale
|
34
25
|
|
35
|
-
sig { returns(T.any(String, NilClass)) }
|
36
26
|
# The current app version the user is interacting with. Evaluated against the App Version criteria. (https://docs.statsig.com/feature-gates/conditions#app-version)
|
37
27
|
attr_accessor :app_version
|
38
28
|
|
39
|
-
sig { returns(T.any(T::Hash[String, String], NilClass)) }
|
40
29
|
# A Hash you can use to set environment variables that apply to this user. e.g. { "tier" => "development" }
|
41
30
|
attr_accessor :statsig_environment
|
42
31
|
|
43
|
-
sig { returns(T.any(T::Hash[String, String], NilClass)) }
|
44
32
|
# Any Custom IDs to associated with the user. (See https://docs.statsig.com/guides/experiment-on-custom-id-types)
|
45
33
|
attr_accessor :custom_ids
|
46
34
|
|
47
|
-
sig { returns(T.any(T::Hash[String, String], NilClass)) }
|
48
35
|
# Any value you wish to use in evaluation, but do not want logged with events, can be stored in this field.
|
49
36
|
attr_accessor :private_attributes
|
50
37
|
|
51
|
-
sig { returns(T.any(T::Hash[String, T.untyped], NilClass)) }
|
52
38
|
def custom
|
53
39
|
@custom
|
54
40
|
end
|
55
41
|
|
56
|
-
sig { params(value: T.any(T::Hash[String, T.untyped], NilClass)).void }
|
57
42
|
# Any custom fields for this user. Evaluated against the Custom criteria. (https://docs.statsig.com/feature-gates/conditions#custom)
|
58
43
|
def custom=(value)
|
59
44
|
@custom = value.is_a?(Hash) ? value : Hash.new
|
60
45
|
end
|
61
46
|
|
62
|
-
sig { params(user_hash: T.any(T::Hash[T.any(String, Symbol), T.untyped], NilClass)).void }
|
63
47
|
def initialize(user_hash)
|
64
48
|
the_hash = user_hash
|
65
49
|
begin
|
@@ -83,55 +67,56 @@ class StatsigUser
|
|
83
67
|
|
84
68
|
def serialize(for_logging)
|
85
69
|
hash = {
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
70
|
+
:userID => @user_id,
|
71
|
+
:email => @email,
|
72
|
+
:ip => @ip,
|
73
|
+
:userAgent => @user_agent,
|
74
|
+
:country => @country,
|
75
|
+
:locale => @locale,
|
76
|
+
:appVersion => @app_version,
|
77
|
+
:custom => @custom,
|
78
|
+
:statsigEnvironment => @statsig_environment,
|
79
|
+
:privateAttributes => @private_attributes,
|
80
|
+
:customIDs => @custom_ids,
|
97
81
|
}
|
98
82
|
if for_logging
|
99
|
-
hash.delete(
|
83
|
+
hash.delete(:privateAttributes)
|
100
84
|
end
|
101
85
|
hash.compact
|
102
86
|
end
|
103
87
|
|
104
|
-
def to_hash_without_stable_id
|
88
|
+
def to_hash_without_stable_id
|
105
89
|
hash = {}
|
90
|
+
|
106
91
|
if @user_id != nil
|
107
|
-
hash[
|
92
|
+
hash[:userID] = @user_id
|
108
93
|
end
|
109
94
|
if @email != nil
|
110
|
-
hash[
|
95
|
+
hash[:email] = @email
|
111
96
|
end
|
112
97
|
if @ip != nil
|
113
|
-
hash[
|
98
|
+
hash[:ip] = @ip
|
114
99
|
end
|
115
100
|
if @user_agent != nil
|
116
|
-
hash[
|
101
|
+
hash[:userAgent] = @user_agent
|
117
102
|
end
|
118
103
|
if @country != nil
|
119
|
-
hash[
|
104
|
+
hash[:country] = @country
|
120
105
|
end
|
121
106
|
if @locale != nil
|
122
|
-
hash[
|
107
|
+
hash[:locale] = @locale
|
123
108
|
end
|
124
109
|
if @app_version != nil
|
125
|
-
hash[
|
110
|
+
hash[:appVersion] = @app_version
|
126
111
|
end
|
127
112
|
if @custom != nil
|
128
|
-
hash[
|
113
|
+
hash[:custom] = Statsig::HashUtils.sortHash(@custom)
|
129
114
|
end
|
130
115
|
if @statsig_environment != nil
|
131
|
-
hash[
|
116
|
+
hash[:statsigEnvironment] = @statsig_environment.clone.sort_by { |key| key }.to_h
|
132
117
|
end
|
133
118
|
if @private_attributes != nil
|
134
|
-
hash[
|
119
|
+
hash[:privateAttributes] = Statsig::HashUtils.sortHash(@private_attributes)
|
135
120
|
end
|
136
121
|
custom_ids = {}
|
137
122
|
if @custom_ids != nil
|
@@ -140,32 +125,12 @@ class StatsigUser
|
|
140
125
|
custom_ids.delete("stableID")
|
141
126
|
end
|
142
127
|
end
|
143
|
-
hash[
|
128
|
+
hash[:customIDs] = custom_ids.sort_by { |key| key }.to_h
|
144
129
|
return Statsig::HashUtils.djb2ForHash(hash.sort_by { |key| key }.to_h)
|
145
130
|
end
|
146
131
|
|
147
|
-
def value_lookup
|
148
|
-
{
|
149
|
-
'userID' => @user_id,
|
150
|
-
'userid' => @user_id,
|
151
|
-
'user_id' => @user_id,
|
152
|
-
'email' => @email,
|
153
|
-
'ip' => @ip,
|
154
|
-
'userAgent' => @user_agent,
|
155
|
-
'useragent' => @user_agent,
|
156
|
-
'user_agent' => @user_agent,
|
157
|
-
'country' => @country,
|
158
|
-
'locale' => @locale,
|
159
|
-
'appVersion' => @app_version,
|
160
|
-
'appversion' => @app_version,
|
161
|
-
'app_version' => @app_version,
|
162
|
-
'custom' => @custom,
|
163
|
-
'privateAttributes' => @private_attributes,
|
164
|
-
}
|
165
|
-
end
|
166
|
-
|
167
132
|
def get_unit_id(id_type)
|
168
|
-
if id_type.is_a?(String) && id_type
|
133
|
+
if id_type.is_a?(String) && id_type != Statsig::Const::CML_USER_ID
|
169
134
|
return nil unless @custom_ids.is_a? Hash
|
170
135
|
|
171
136
|
return @custom_ids[id_type] || @custom_ids[id_type.downcase]
|
@@ -175,12 +140,6 @@ class StatsigUser
|
|
175
140
|
|
176
141
|
private
|
177
142
|
|
178
|
-
sig {
|
179
|
-
params(user_hash: T.any(T::Hash[T.any(String, Symbol), T.untyped], NilClass),
|
180
|
-
keys: T::Array[Symbol],
|
181
|
-
type: T.untyped)
|
182
|
-
.returns(T.untyped)
|
183
|
-
}
|
184
143
|
# Pulls fields from the user hash via Symbols and Strings
|
185
144
|
def from_hash(user_hash, keys, type)
|
186
145
|
if user_hash.nil?
|
data/lib/ua_parser.rb
CHANGED
data/lib/uri_helper.rb
CHANGED
@@ -1,24 +1,16 @@
|
|
1
|
-
# typed: true
|
2
|
-
|
3
|
-
require 'sorbet-runtime'
|
4
|
-
|
5
1
|
class URIHelper
|
6
2
|
class URIBuilder
|
7
|
-
extend T::Sig
|
8
3
|
|
9
|
-
sig { returns(StatsigOptions) }
|
10
4
|
attr_accessor :options
|
11
5
|
|
12
|
-
sig { params(options: StatsigOptions).void }
|
13
6
|
def initialize(options)
|
14
7
|
@options = options
|
15
8
|
end
|
16
9
|
|
17
|
-
sig { params(endpoint: String).returns(String) }
|
18
10
|
def build_url(endpoint)
|
19
11
|
api = @options.api_url_base
|
20
12
|
if endpoint.include?('download_config_specs')
|
21
|
-
api =
|
13
|
+
api = @options.api_url_download_config_specs
|
22
14
|
end
|
23
15
|
unless api.end_with?('/')
|
24
16
|
api += '/'
|
@@ -1,27 +1,17 @@
|
|
1
|
-
# typed: false
|
2
|
-
|
3
|
-
require 'sorbet-runtime'
|
4
1
|
require 'statsig_options'
|
5
2
|
|
6
3
|
module Statsig
|
7
|
-
UserPersistedValues = T.type_alias { T::Hash[String, Hash] }
|
8
|
-
|
9
4
|
class UserPersistentStorageUtils
|
10
|
-
extend T::Sig
|
11
5
|
|
12
|
-
sig { returns(T::Hash[String, UserPersistedValues]) }
|
13
6
|
attr_accessor :cache
|
14
7
|
|
15
|
-
sig { returns(T.nilable(Interfaces::IUserPersistentStorage)) }
|
16
8
|
attr_accessor :storage
|
17
9
|
|
18
|
-
sig { params(options: StatsigOptions).void }
|
19
10
|
def initialize(options)
|
20
11
|
@storage = options.user_persistent_storage
|
21
12
|
@cache = {}
|
22
13
|
end
|
23
14
|
|
24
|
-
sig { params(user: StatsigUser, id_type: String).returns(T.nilable(UserPersistedValues)) }
|
25
15
|
def get_user_persisted_values(user, id_type)
|
26
16
|
key = self.class.get_storage_key(user, id_type)
|
27
17
|
return @cache[key] unless @cache[key].nil?
|
@@ -29,7 +19,6 @@ module Statsig
|
|
29
19
|
return load_from_storage(key)
|
30
20
|
end
|
31
21
|
|
32
|
-
sig { params(key: String).returns(T.nilable(UserPersistedValues)) }
|
33
22
|
def load_from_storage(key)
|
34
23
|
return if @storage.nil?
|
35
24
|
|
@@ -50,7 +39,6 @@ module Statsig
|
|
50
39
|
return nil
|
51
40
|
end
|
52
41
|
|
53
|
-
sig { params(user: StatsigUser, id_type: String, user_persisted_values: UserPersistedValues).void }
|
54
42
|
def save_to_storage(user, id_type, user_persisted_values)
|
55
43
|
return if @storage.nil?
|
56
44
|
|
@@ -65,7 +53,6 @@ module Statsig
|
|
65
53
|
end
|
66
54
|
end
|
67
55
|
|
68
|
-
sig { params(user: StatsigUser, id_type: String, config_name: String).void }
|
69
56
|
def remove_experiment_from_storage(user, id_type, config_name)
|
70
57
|
persisted_values = get_user_persisted_values(user, id_type)
|
71
58
|
unless persisted_values.nil?
|
@@ -74,7 +61,6 @@ module Statsig
|
|
74
61
|
end
|
75
62
|
end
|
76
63
|
|
77
|
-
sig { params(user_persisted_values: T.nilable(UserPersistedValues), config_name: String, evaluation: ConfigResult).void }
|
78
64
|
def add_evaluation_to_user_persisted_values(user_persisted_values, config_name, evaluation)
|
79
65
|
if user_persisted_values.nil?
|
80
66
|
user_persisted_values = {}
|
@@ -84,21 +70,18 @@ module Statsig
|
|
84
70
|
|
85
71
|
private
|
86
72
|
|
87
|
-
sig { params(values_string: String).returns(T.nilable(UserPersistedValues)) }
|
88
73
|
def self.parse(values_string)
|
89
74
|
return JSON.parse(values_string)
|
90
75
|
rescue JSON::ParserError
|
91
76
|
return nil
|
92
77
|
end
|
93
78
|
|
94
|
-
sig { params(values_object: UserPersistedValues).returns(T.nilable(String)) }
|
95
79
|
def self.stringify(values_object)
|
96
80
|
return JSON.generate(values_object)
|
97
81
|
rescue StandardError
|
98
82
|
return nil
|
99
83
|
end
|
100
84
|
|
101
|
-
sig { params(user: StatsigUser, id_type: String).returns(String) }
|
102
85
|
def self.get_storage_key(user, id_type)
|
103
86
|
"#{user.get_unit_id(id_type)}:#{id_type}"
|
104
87
|
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.32.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: 2024-01-
|
11
|
+
date: 2024-01-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -322,8 +322,10 @@ executables: []
|
|
322
322
|
extensions: []
|
323
323
|
extra_rdoc_files: []
|
324
324
|
files:
|
325
|
+
- lib/api_config.rb
|
325
326
|
- lib/client_initialize_helpers.rb
|
326
327
|
- lib/config_result.rb
|
328
|
+
- lib/constants.rb
|
327
329
|
- lib/diagnostics.rb
|
328
330
|
- lib/dynamic_config.rb
|
329
331
|
- lib/error_boundary.rb
|