statsig 1.4.0 → 1.6.2
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/evaluation_helpers.rb +3 -11
- data/lib/evaluator.rb +30 -17
- data/lib/network.rb +3 -3
- data/lib/statsig_driver.rb +8 -5
- data/lib/statsig_event.rb +9 -2
- data/lib/statsig_logger.rb +1 -3
- data/lib/statsig_user.rb +10 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 325d5430e48a3d71b68d2e284e20b66b0052bf1a4d9e99e7a92a84069b680ab8
|
4
|
+
data.tar.gz: b227e1fede44928035b1a03ebc8e8c006425466b50b007f89c452f6a47b51cd6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28017886b1a5d3f5c37e36680f0dfbd300cf5a0f6e0678430af69631486bc88689a622d74a63da1687ef7d610d612227d68cb58b85a7572b7cc5cfcaee0794dd
|
7
|
+
data.tar.gz: 10a66e7674df116a65351b97b8d27055bacd46f021309c3256bc5605fe3a972416e2c595135262fc9d18117e447a5038db3ebe1b89cb71495f308430fe4d2a32
|
data/lib/evaluation_helpers.rb
CHANGED
@@ -6,19 +6,11 @@ module EvaluationHelpers
|
|
6
6
|
func.call(a.to_f, b.to_f) rescue false
|
7
7
|
end
|
8
8
|
|
9
|
-
# returns true if array contains value, ignoring case when comparing strings
|
10
|
-
def self.array_contains(array, value, ignore_case)
|
11
|
-
return false unless array.is_a?(Array) && !value.nil?
|
12
|
-
if value.is_a?(String) && match_string_in_array(array, value, ignore_case, ->(a, b) { a == b })
|
13
|
-
return true
|
14
|
-
end
|
15
|
-
return array.include?(value)
|
16
|
-
end
|
17
|
-
|
18
9
|
# returns true if array has any element that evaluates to true with value using func lambda, ignoring case
|
19
10
|
def self.match_string_in_array(array, value, ignore_case, func)
|
20
|
-
return false unless array.is_a?(Array) && value.
|
21
|
-
|
11
|
+
return false unless array.is_a?(Array) && !value.nil?
|
12
|
+
str_value = value.to_s
|
13
|
+
array.any?{ |s| !s.nil? && ((ignore_case && func.call(str_value.downcase, s.to_s.downcase)) || func.call(str_value, s.to_s)) } rescue false
|
22
14
|
end
|
23
15
|
|
24
16
|
def self.compare_times(a, b, func)
|
data/lib/evaluator.rb
CHANGED
@@ -84,10 +84,10 @@ class Evaluator
|
|
84
84
|
return $fetch_from_server if other_gate_result == $fetch_from_server
|
85
85
|
return type == 'pass_gate' ? other_gate_result.gate_value : !other_gate_result.gate_value
|
86
86
|
when 'ip_based'
|
87
|
-
value = get_value_from_user(user, field) || get_value_from_ip(user
|
87
|
+
value = get_value_from_user(user, field) || get_value_from_ip(user, field)
|
88
88
|
return $fetch_from_server if value == $fetch_from_server
|
89
89
|
when 'ua_based'
|
90
|
-
value = get_value_from_user(user, field) || get_value_from_ua(user
|
90
|
+
value = get_value_from_user(user, field) || get_value_from_ua(user, field)
|
91
91
|
return $fetch_from_server if value == $fetch_from_server
|
92
92
|
when 'user_field'
|
93
93
|
value = get_value_from_user(user, field)
|
@@ -138,13 +138,13 @@ class Evaluator
|
|
138
138
|
|
139
139
|
# array operations
|
140
140
|
when 'any'
|
141
|
-
return EvaluationHelpers::
|
141
|
+
return EvaluationHelpers::match_string_in_array(target, value, true, ->(a, b) { a == b })
|
142
142
|
when 'none'
|
143
|
-
return !EvaluationHelpers::
|
143
|
+
return !EvaluationHelpers::match_string_in_array(target, value, true, ->(a, b) { a == b })
|
144
144
|
when 'any_case_sensitive'
|
145
|
-
return EvaluationHelpers::
|
145
|
+
return EvaluationHelpers::match_string_in_array(target, value, false, ->(a, b) { a == b })
|
146
146
|
when 'none_case_sensitive'
|
147
|
-
return !EvaluationHelpers::
|
147
|
+
return !EvaluationHelpers::match_string_in_array(target, value, false, ->(a, b) { a == b })
|
148
148
|
|
149
149
|
#string
|
150
150
|
when 'str_starts_with_any'
|
@@ -153,6 +153,8 @@ class Evaluator
|
|
153
153
|
return EvaluationHelpers::match_string_in_array(target, value, true, ->(a, b) { a.end_with?(b) })
|
154
154
|
when 'str_contains_any'
|
155
155
|
return EvaluationHelpers::match_string_in_array(target, value, true, ->(a, b) { a.include?(b) })
|
156
|
+
when 'str_contains_none'
|
157
|
+
return !EvaluationHelpers::match_string_in_array(target, value, true, ->(a, b) { a.include?(b) })
|
156
158
|
when 'str_matches'
|
157
159
|
return (value.is_a?(String) && !(value =~ Regexp.new(target)).nil? rescue false)
|
158
160
|
when 'eq'
|
@@ -177,13 +179,22 @@ class Evaluator
|
|
177
179
|
|
178
180
|
user_lookup_table = user&.value_lookup
|
179
181
|
return nil unless user_lookup_table.is_a?(Hash)
|
180
|
-
return user_lookup_table[field.downcase] if user_lookup_table.has_key?(field.downcase)
|
182
|
+
return user_lookup_table[field.downcase] if user_lookup_table.has_key?(field.downcase) && !user_lookup_table[field.downcase].nil?
|
181
183
|
|
182
184
|
user_custom = user_lookup_table['custom']
|
183
|
-
|
184
|
-
|
185
|
-
|
185
|
+
if user_custom.is_a?(Hash)
|
186
|
+
user_custom.each do |key, value|
|
187
|
+
return value if key.downcase.casecmp?(field.downcase) && !value.nil?
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
private_attributes = user_lookup_table['privateAttributes']
|
192
|
+
if private_attributes.is_a?(Hash)
|
193
|
+
private_attributes.each do |key, value|
|
194
|
+
return value if key.downcase.casecmp?(field.downcase) && !value.nil?
|
195
|
+
end
|
186
196
|
end
|
197
|
+
|
187
198
|
nil
|
188
199
|
end
|
189
200
|
|
@@ -197,17 +208,19 @@ class Evaluator
|
|
197
208
|
nil
|
198
209
|
end
|
199
210
|
|
200
|
-
def get_value_from_ip(
|
201
|
-
return nil unless
|
211
|
+
def get_value_from_ip(user, field)
|
212
|
+
return nil unless user.is_a?(StatsigUser) && field.is_a?(String) && field.downcase == 'country'
|
213
|
+
ip = get_value_from_user(user, 'ip')
|
214
|
+
return nil unless ip.is_a?(String)
|
202
215
|
|
203
|
-
if field.downcase != 'country'
|
204
|
-
return $fetch_from_server
|
205
|
-
end
|
206
216
|
CountryLookup.lookup_ip_string(ip)
|
207
217
|
end
|
208
218
|
|
209
|
-
def get_value_from_ua(
|
210
|
-
return nil unless
|
219
|
+
def get_value_from_ua(user, field)
|
220
|
+
return nil unless user.is_a?(StatsigUser) && field.is_a?(String)
|
221
|
+
ua = get_value_from_user(user, 'userAgent')
|
222
|
+
return nil unless ua.is_a?(String)
|
223
|
+
|
211
224
|
parsed = @ua_parser.parse ua
|
212
225
|
os = parsed.os
|
213
226
|
case field.downcase
|
data/lib/network.rb
CHANGED
@@ -39,7 +39,7 @@ class Network
|
|
39
39
|
|
40
40
|
def check_gate(user, gate_name)
|
41
41
|
begin
|
42
|
-
request_body = JSON.generate({'user' => user&.serialize, 'gateName' => gate_name})
|
42
|
+
request_body = JSON.generate({'user' => user&.serialize(false), 'gateName' => gate_name})
|
43
43
|
response = post_helper('check_gate', request_body)
|
44
44
|
return JSON.parse(response.body) unless response.nil?
|
45
45
|
false
|
@@ -50,7 +50,7 @@ class Network
|
|
50
50
|
|
51
51
|
def get_config(user, dynamic_config_name)
|
52
52
|
begin
|
53
|
-
request_body = JSON.generate({'user' => user&.serialize, 'configName' => dynamic_config_name})
|
53
|
+
request_body = JSON.generate({'user' => user&.serialize(false), 'configName' => dynamic_config_name})
|
54
54
|
response = post_helper('get_config', request_body)
|
55
55
|
return JSON.parse(response.body) unless response.nil?
|
56
56
|
nil
|
@@ -86,7 +86,7 @@ class Network
|
|
86
86
|
def post_logs(events, statsig_metadata)
|
87
87
|
begin
|
88
88
|
json_body = JSON.generate({'events' => events, 'statsigMetadata' => statsig_metadata})
|
89
|
-
post_helper('log_event',
|
89
|
+
post_helper('log_event', json_body, retries: 5)
|
90
90
|
rescue
|
91
91
|
end
|
92
92
|
end
|
data/lib/statsig_driver.rb
CHANGED
@@ -56,9 +56,11 @@ class StatsigDriver
|
|
56
56
|
|
57
57
|
if res == $fetch_from_server
|
58
58
|
res = check_gate_fallback(user, gate_name)
|
59
|
+
# exposure logged by the server
|
60
|
+
else
|
61
|
+
@logger.log_gate_exposure(user, res.name, res.gate_value, res.rule_id)
|
59
62
|
end
|
60
63
|
|
61
|
-
@logger.log_gate_exposure(user, res.name, res.gate_value, res.rule_id)
|
62
64
|
res.gate_value
|
63
65
|
end
|
64
66
|
|
@@ -80,11 +82,12 @@ class StatsigDriver
|
|
80
82
|
|
81
83
|
if res == $fetch_from_server
|
82
84
|
res = get_config_fallback(user, dynamic_config_name)
|
85
|
+
# exposure logged by the server
|
86
|
+
else
|
87
|
+
@logger.log_config_exposure(user, res.name, res.rule_id)
|
83
88
|
end
|
84
89
|
|
85
|
-
|
86
|
-
@logger.log_config_exposure(user, result_config.name, result_config.rule_id)
|
87
|
-
result_config
|
90
|
+
DynamicConfig.new(res.name, res.json_value, res.rule_id)
|
88
91
|
end
|
89
92
|
|
90
93
|
def log_event(user, event_name, value = nil, metadata = nil)
|
@@ -96,7 +99,7 @@ class StatsigDriver
|
|
96
99
|
user = normalize_user(user)
|
97
100
|
|
98
101
|
event = StatsigEvent.new(event_name)
|
99
|
-
event.user = user
|
102
|
+
event.user = user
|
100
103
|
event.value = value
|
101
104
|
event.metadata = metadata
|
102
105
|
event.statsig_metadata = @statsig_metadata
|
data/lib/statsig_event.rb
CHANGED
@@ -1,15 +1,22 @@
|
|
1
1
|
class StatsigEvent
|
2
2
|
attr_accessor :value
|
3
|
-
attr_accessor :user
|
4
3
|
attr_accessor :metadata
|
5
4
|
attr_accessor :statsig_metadata
|
5
|
+
attr_reader :user
|
6
|
+
|
6
7
|
def initialize(event_name)
|
7
8
|
@event_name = event_name
|
8
9
|
@time = Time.now.to_f * 1000
|
9
10
|
end
|
10
11
|
|
12
|
+
def user=(value)
|
13
|
+
if value.is_a?(StatsigUser)
|
14
|
+
@user = value.serialize(true)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
11
18
|
def serialize
|
12
|
-
|
19
|
+
{
|
13
20
|
'eventName' => @event_name,
|
14
21
|
'metadata' => @metadata,
|
15
22
|
'value' => @value,
|
data/lib/statsig_logger.rb
CHANGED
data/lib/statsig_user.rb
CHANGED
@@ -7,6 +7,7 @@ class StatsigUser
|
|
7
7
|
attr_accessor :locale
|
8
8
|
attr_accessor :app_version
|
9
9
|
attr_accessor :statsig_environment
|
10
|
+
attr_accessor :private_attributes
|
10
11
|
|
11
12
|
def custom
|
12
13
|
@custom
|
@@ -29,11 +30,12 @@ class StatsigUser
|
|
29
30
|
@app_version = user_hash['appVersion'] || user_hash['app_version']
|
30
31
|
@custom = user_hash['custom']
|
31
32
|
@statsig_environment = user_hash['statsigEnvironment']
|
33
|
+
@private_attributes = user_hash['privateAttributes']
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
35
|
-
def serialize
|
36
|
-
{
|
37
|
+
def serialize(for_logging)
|
38
|
+
hash = {
|
37
39
|
'userID' => @user_id,
|
38
40
|
'email' => @email,
|
39
41
|
'ip' => @ip,
|
@@ -43,7 +45,12 @@ class StatsigUser
|
|
43
45
|
'appVersion' => @app_version,
|
44
46
|
'custom' => @custom,
|
45
47
|
'statsigEnvironment' => @statsig_environment,
|
48
|
+
'privateAttributes' => @private_attributes,
|
46
49
|
}
|
50
|
+
if for_logging
|
51
|
+
hash.delete('privateAttributes')
|
52
|
+
end
|
53
|
+
hash
|
47
54
|
end
|
48
55
|
|
49
56
|
def value_lookup
|
@@ -62,6 +69,7 @@ class StatsigUser
|
|
62
69
|
'appversion' => @app_version,
|
63
70
|
'app_version' => @app_version,
|
64
71
|
'custom' => @custom,
|
72
|
+
'privateAttributes' => @private_attributes,
|
65
73
|
}
|
66
74
|
end
|
67
75
|
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.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Statsig, Inc
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-09-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|