statsig 1.8.4.beta.pre.1 → 1.9.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/config_result.rb +3 -1
- data/lib/evaluator.rb +28 -6
- data/lib/layer.rb +15 -0
- data/lib/network.rb +0 -1
- data/lib/spec_store.rb +15 -1
- data/lib/statsig.rb +6 -1
- data/lib/statsig_driver.rb +46 -24
- data/lib/statsig_logger.rb +14 -5
- metadata +6 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a832901a7d272c5b27408418836ded1042f720bfc0a374489ed0a81c476a1606
|
|
4
|
+
data.tar.gz: c89426f15ab949980299bfc21931466d076e8b23fbe26ddc290e13d1d92cb948
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c92f462bf372241bdd214a9a153b597792970b70631b8631be3df01b189953b6574016eedf437e397e07f62c8ef8f1c3e7ab7834f10080febe9843514cb059e0
|
|
7
|
+
data.tar.gz: 228deaad6a9c224c80321ddc6bd33ab13a4cd6eae86cc83f822f5d8325f25aaa13bfd0903369468b3664d28847b3239df27a446b32b349e5b3d987f77ef3fb74
|
data/lib/config_result.rb
CHANGED
|
@@ -6,13 +6,15 @@ module Statsig
|
|
|
6
6
|
attr_accessor :json_value
|
|
7
7
|
attr_accessor :rule_id
|
|
8
8
|
attr_accessor :secondary_exposures
|
|
9
|
+
attr_accessor :config_delegate
|
|
9
10
|
|
|
10
|
-
def initialize(name, gate_value = false, json_value = {}, rule_id = '', secondary_exposures = [])
|
|
11
|
+
def initialize(name, gate_value = false, json_value = {}, rule_id = '', secondary_exposures = [], config_delegate = '')
|
|
11
12
|
@name = name
|
|
12
13
|
@gate_value = gate_value
|
|
13
14
|
@json_value = json_value
|
|
14
15
|
@rule_id = rule_id
|
|
15
16
|
@secondary_exposures = secondary_exposures.is_a?(Array) ? secondary_exposures : []
|
|
17
|
+
@config_delegate = config_delegate
|
|
16
18
|
end
|
|
17
19
|
end
|
|
18
20
|
end
|
data/lib/evaluator.rb
CHANGED
|
@@ -29,6 +29,11 @@ module Statsig
|
|
|
29
29
|
eval_spec(user, @spec_store.get_config(config_name))
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
def get_layer(user, layer_name)
|
|
33
|
+
return nil unless @initialized && @spec_store.has_layer?(layer_name)
|
|
34
|
+
eval_spec(user, @spec_store.get_layer(layer_name))
|
|
35
|
+
end
|
|
36
|
+
|
|
32
37
|
def shutdown
|
|
33
38
|
@spec_store.shutdown
|
|
34
39
|
end
|
|
@@ -44,15 +49,16 @@ module Statsig
|
|
|
44
49
|
rule = config['rules'][i]
|
|
45
50
|
result = eval_rule(user, rule)
|
|
46
51
|
return $fetch_from_server if result == $fetch_from_server
|
|
47
|
-
exposures = exposures + result
|
|
48
|
-
if result
|
|
52
|
+
exposures = exposures + result.secondary_exposures
|
|
53
|
+
if result.gate_value
|
|
49
54
|
pass = eval_pass_percent(user, rule, config['salt'])
|
|
50
55
|
return Statsig::ConfigResult.new(
|
|
51
56
|
config['name'],
|
|
52
57
|
pass,
|
|
53
|
-
pass ?
|
|
54
|
-
|
|
55
|
-
exposures
|
|
58
|
+
pass ? result.json_value : config['defaultValue'],
|
|
59
|
+
result.rule_id,
|
|
60
|
+
exposures,
|
|
61
|
+
result.config_delegate
|
|
56
62
|
)
|
|
57
63
|
end
|
|
58
64
|
|
|
@@ -83,7 +89,16 @@ module Statsig
|
|
|
83
89
|
end
|
|
84
90
|
i += 1
|
|
85
91
|
end
|
|
86
|
-
|
|
92
|
+
|
|
93
|
+
delegate = rule['configDelegate']
|
|
94
|
+
if pass and @spec_store.get_config(delegate)
|
|
95
|
+
delegated_result = self.eval_spec(user, @spec_store.get_config(delegate))
|
|
96
|
+
delegated_result.config_delegate = delegate
|
|
97
|
+
delegated_result.secondary_exposures = exposures + delegated_result.secondary_exposures
|
|
98
|
+
return delegated_result
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
Statsig::ConfigResult.new('', pass, rule['returnValue'], rule['id'], exposures)
|
|
87
102
|
end
|
|
88
103
|
|
|
89
104
|
def eval_condition(user, condition)
|
|
@@ -159,17 +174,24 @@ module Statsig
|
|
|
159
174
|
return EvaluationHelpers::compare_numbers(value, target, ->(a, b) { a <= b })
|
|
160
175
|
|
|
161
176
|
# version comparison
|
|
177
|
+
# need to check for nil or empty value because Version takes them as valid values
|
|
162
178
|
when 'version_gt'
|
|
179
|
+
return false if value.to_s.empty?
|
|
163
180
|
return (Gem::Version.new(value) > Gem::Version.new(target) rescue false)
|
|
164
181
|
when 'version_gte'
|
|
182
|
+
return false if value.to_s.empty?
|
|
165
183
|
return (Gem::Version.new(value) >= Gem::Version.new(target) rescue false)
|
|
166
184
|
when 'version_lt'
|
|
185
|
+
return false if value.to_s.empty?
|
|
167
186
|
return (Gem::Version.new(value) < Gem::Version.new(target) rescue false)
|
|
168
187
|
when 'version_lte'
|
|
188
|
+
return false if value.to_s.empty?
|
|
169
189
|
return (Gem::Version.new(value) <= Gem::Version.new(target) rescue false)
|
|
170
190
|
when 'version_eq'
|
|
191
|
+
return false if value.to_s.empty?
|
|
171
192
|
return (Gem::Version.new(value) == Gem::Version.new(target) rescue false)
|
|
172
193
|
when 'version_neq'
|
|
194
|
+
return false if value.to_s.empty?
|
|
173
195
|
return (Gem::Version.new(value) != Gem::Version.new(target) rescue false)
|
|
174
196
|
|
|
175
197
|
# array operations
|
data/lib/layer.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class Layer
|
|
2
|
+
attr_accessor :name
|
|
3
|
+
attr_accessor :rule_id
|
|
4
|
+
|
|
5
|
+
def initialize(name, value = {}, rule_id = '')
|
|
6
|
+
@name = name
|
|
7
|
+
@value = value
|
|
8
|
+
@rule_id = rule_id
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def get(index, default_value)
|
|
12
|
+
return default_value if @value.nil? || !@value.key?(index)
|
|
13
|
+
@value[index]
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/network.rb
CHANGED
data/lib/spec_store.rb
CHANGED
|
@@ -11,6 +11,7 @@ module Statsig
|
|
|
11
11
|
@store = {
|
|
12
12
|
:gates => {},
|
|
13
13
|
:configs => {},
|
|
14
|
+
:layers => {},
|
|
14
15
|
:id_lists => {},
|
|
15
16
|
}
|
|
16
17
|
e = download_config_specs
|
|
@@ -34,6 +35,10 @@ module Statsig
|
|
|
34
35
|
@store[:configs].key?(config_name)
|
|
35
36
|
end
|
|
36
37
|
|
|
38
|
+
def has_layer?(layer_name)
|
|
39
|
+
@store[:layers].key?(layer_name)
|
|
40
|
+
end
|
|
41
|
+
|
|
37
42
|
def get_gate(gate_name)
|
|
38
43
|
return nil unless has_gate?(gate_name)
|
|
39
44
|
@store[:gates][gate_name]
|
|
@@ -44,6 +49,11 @@ module Statsig
|
|
|
44
49
|
@store[:configs][config_name]
|
|
45
50
|
end
|
|
46
51
|
|
|
52
|
+
def get_layer(layer_name)
|
|
53
|
+
return nil unless has_layer?(layer_name)
|
|
54
|
+
@store[:layers][layer_name]
|
|
55
|
+
end
|
|
56
|
+
|
|
47
57
|
def get_id_list(list_name)
|
|
48
58
|
@store[:id_lists][list_name]
|
|
49
59
|
end
|
|
@@ -89,15 +99,19 @@ module Statsig
|
|
|
89
99
|
@last_sync_time = specs_json['time'] || @last_sync_time
|
|
90
100
|
return unless specs_json['has_updates'] == true &&
|
|
91
101
|
!specs_json['feature_gates'].nil? &&
|
|
92
|
-
!specs_json['dynamic_configs'].nil?
|
|
102
|
+
!specs_json['dynamic_configs'].nil? &&
|
|
103
|
+
!specs_json['layer_configs'].nil?
|
|
93
104
|
|
|
94
105
|
new_gates = {}
|
|
95
106
|
new_configs = {}
|
|
107
|
+
new_layers = {}
|
|
96
108
|
|
|
97
109
|
specs_json['feature_gates'].map{|gate| new_gates[gate['name']] = gate }
|
|
98
110
|
specs_json['dynamic_configs'].map{|config| new_configs[config['name']] = config }
|
|
111
|
+
specs_json['layer_configs'].map{|layer| new_layers[layer['name']] = layer }
|
|
99
112
|
@store[:gates] = new_gates
|
|
100
113
|
@store[:configs] = new_configs
|
|
114
|
+
@store[:layers] = new_layers
|
|
101
115
|
|
|
102
116
|
new_id_lists = specs_json['id_lists']
|
|
103
117
|
if new_id_lists.is_a? Hash
|
data/lib/statsig.rb
CHANGED
|
@@ -25,6 +25,11 @@ module Statsig
|
|
|
25
25
|
@shared_instance&.get_config(user, experiment_name)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
def self.get_layer(user, layer_name)
|
|
29
|
+
ensure_initialized
|
|
30
|
+
@shared_instance&.get_config(user, layer_name)
|
|
31
|
+
end
|
|
32
|
+
|
|
28
33
|
def self.log_event(user, event_name, value, metadata)
|
|
29
34
|
ensure_initialized
|
|
30
35
|
@shared_instance&.log_event(user, event_name, value, metadata)
|
|
@@ -40,7 +45,7 @@ module Statsig
|
|
|
40
45
|
def self.get_statsig_metadata
|
|
41
46
|
{
|
|
42
47
|
'sdkType' => 'ruby-server',
|
|
43
|
-
'sdkVersion' => '1.
|
|
48
|
+
'sdkVersion' => '1.9.0',
|
|
44
49
|
}
|
|
45
50
|
end
|
|
46
51
|
|
data/lib/statsig_driver.rb
CHANGED
|
@@ -26,12 +26,7 @@ class StatsigDriver
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def check_gate(user, gate_name)
|
|
29
|
-
|
|
30
|
-
user = normalize_user(user)
|
|
31
|
-
if !gate_name.is_a?(String) || gate_name.empty?
|
|
32
|
-
raise 'Invalid gate_name provided'
|
|
33
|
-
end
|
|
34
|
-
check_shutdown
|
|
29
|
+
user = verify_inputs(user, gate_name, "gate_name")
|
|
35
30
|
|
|
36
31
|
res = @evaluator.check_gate(user, gate_name)
|
|
37
32
|
if res.nil?
|
|
@@ -49,33 +44,34 @@ class StatsigDriver
|
|
|
49
44
|
end
|
|
50
45
|
|
|
51
46
|
def get_config(user, dynamic_config_name)
|
|
52
|
-
|
|
53
|
-
user
|
|
54
|
-
|
|
55
|
-
raise "Invalid dynamic_config_name provided"
|
|
56
|
-
end
|
|
57
|
-
check_shutdown
|
|
47
|
+
user = verify_inputs(user, dynamic_config_name, "dynamic_config_name")
|
|
48
|
+
get_config_impl(user, dynamic_config_name)
|
|
49
|
+
end
|
|
58
50
|
|
|
59
|
-
|
|
51
|
+
def get_experiment(user, experiment_name)
|
|
52
|
+
user = verify_inputs(user, experiment_name, "experiment_name")
|
|
53
|
+
get_config_impl(user, experiment_name)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def get_layer(user, layer_name)
|
|
57
|
+
user = verify_inputs(user, layer_name, "layer_name")
|
|
58
|
+
|
|
59
|
+
res = @evaluator.get_layer(user, layer_name)
|
|
60
60
|
if res.nil?
|
|
61
|
-
res = Statsig::ConfigResult.new(
|
|
61
|
+
res = Statsig::ConfigResult.new(layer_name)
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
if res == $fetch_from_server
|
|
65
|
-
res
|
|
65
|
+
if res.config_delegate.empty?
|
|
66
|
+
return Layer.new(layer_name)
|
|
67
|
+
end
|
|
68
|
+
res = get_config_fallback(user, res.config_delegate)
|
|
66
69
|
# exposure logged by the server
|
|
67
70
|
else
|
|
68
|
-
@logger.
|
|
71
|
+
@logger.log_layer_exposure(user, res.name, res.rule_id, res.secondary_exposures, res.config_delegate)
|
|
69
72
|
end
|
|
70
73
|
|
|
71
|
-
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def get_experiment(user, experiment_name)
|
|
75
|
-
if !experiment_name.is_a?(String) || experiment_name.empty?
|
|
76
|
-
raise "Invalid experiment_name provided"
|
|
77
|
-
end
|
|
78
|
-
get_config(user, experiment_name)
|
|
74
|
+
Layer.new(res.name, res.json_value, res.rule_id)
|
|
79
75
|
end
|
|
80
76
|
|
|
81
77
|
def log_event(user, event_name, value = nil, metadata = nil)
|
|
@@ -102,6 +98,32 @@ class StatsigDriver
|
|
|
102
98
|
|
|
103
99
|
private
|
|
104
100
|
|
|
101
|
+
def verify_inputs(user, config_name, variable_name)
|
|
102
|
+
validate_user(user)
|
|
103
|
+
if !config_name.is_a?(String) || config_name.empty?
|
|
104
|
+
raise "Invalid " + variable_name +" provided"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
check_shutdown
|
|
108
|
+
normalize_user(user)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def get_config_impl(user, config_name)
|
|
112
|
+
res = @evaluator.get_config(user, config_name)
|
|
113
|
+
if res.nil?
|
|
114
|
+
res = Statsig::ConfigResult.new(config_name)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
if res == $fetch_from_server
|
|
118
|
+
res = get_config_fallback(user, config_name)
|
|
119
|
+
# exposure logged by the server
|
|
120
|
+
else
|
|
121
|
+
@logger.log_config_exposure(user, res.name, res.rule_id, res.secondary_exposures)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
DynamicConfig.new(res.name, res.json_value, res.rule_id)
|
|
125
|
+
end
|
|
126
|
+
|
|
105
127
|
def validate_user(user)
|
|
106
128
|
if user.nil? || !user.instance_of?(StatsigUser) || !user.user_id.is_a?(String)
|
|
107
129
|
raise 'Must provide a valid StatsigUser with a user_id to use the server SDK. See https://docs.statsig.com/messages/serverRequiredUserID/ for more details.'
|
data/lib/statsig_logger.rb
CHANGED
|
@@ -2,6 +2,7 @@ require 'statsig_event'
|
|
|
2
2
|
|
|
3
3
|
$gate_exposure_event = 'statsig::gate_exposure'
|
|
4
4
|
$config_exposure_event = 'statsig::config_exposure'
|
|
5
|
+
$layer_exposure_event = 'statsig::layer_exposure'
|
|
5
6
|
|
|
6
7
|
module Statsig
|
|
7
8
|
class StatsigLogger
|
|
@@ -43,15 +44,24 @@ module Statsig
|
|
|
43
44
|
log_event(event)
|
|
44
45
|
end
|
|
45
46
|
|
|
47
|
+
def log_layer_exposure(user, config_name, rule_id, secondary_exposures, allocated_experiment)
|
|
48
|
+
event = StatsigEvent.new($layer_exposure_event)
|
|
49
|
+
event.user = user
|
|
50
|
+
event.metadata = {
|
|
51
|
+
'config' => config_name,
|
|
52
|
+
'ruleID' => rule_id,
|
|
53
|
+
'allocatedExperiment' => allocated_experiment
|
|
54
|
+
}
|
|
55
|
+
event.statsig_metadata = Statsig.get_statsig_metadata
|
|
56
|
+
event.secondary_exposures = secondary_exposures.is_a?(Array) ? secondary_exposures : []
|
|
57
|
+
log_event(event)
|
|
58
|
+
end
|
|
59
|
+
|
|
46
60
|
def periodic_flush
|
|
47
|
-
puts "Statsig:: starting a thread"
|
|
48
61
|
Thread.new do
|
|
49
62
|
loop do
|
|
50
|
-
puts "Statsig:: beginning to sleep for 60 seconds"
|
|
51
63
|
sleep 60
|
|
52
|
-
puts "Statsig:: finished sleeping"
|
|
53
64
|
flush
|
|
54
|
-
puts "Statsig:: flushed"
|
|
55
65
|
end
|
|
56
66
|
end
|
|
57
67
|
end
|
|
@@ -63,7 +73,6 @@ module Statsig
|
|
|
63
73
|
if @events.length == 0
|
|
64
74
|
return
|
|
65
75
|
end
|
|
66
|
-
puts "Statsig:: flushing events, count: #{@events.length.to_s}"
|
|
67
76
|
flush_events = @events.map { |e| e.serialize }
|
|
68
77
|
@events = []
|
|
69
78
|
|
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.9.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: 2022-
|
|
11
|
+
date: 2022-03-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -118,6 +118,7 @@ files:
|
|
|
118
118
|
- lib/dynamic_config.rb
|
|
119
119
|
- lib/evaluation_helpers.rb
|
|
120
120
|
- lib/evaluator.rb
|
|
121
|
+
- lib/layer.rb
|
|
121
122
|
- lib/network.rb
|
|
122
123
|
- lib/spec_store.rb
|
|
123
124
|
- lib/statsig.rb
|
|
@@ -141,11 +142,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
141
142
|
version: '0'
|
|
142
143
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
144
|
requirements:
|
|
144
|
-
- - "
|
|
145
|
+
- - ">="
|
|
145
146
|
- !ruby/object:Gem::Version
|
|
146
|
-
version:
|
|
147
|
+
version: '0'
|
|
147
148
|
requirements: []
|
|
148
|
-
rubygems_version: 3.
|
|
149
|
+
rubygems_version: 3.3.7
|
|
149
150
|
signing_key:
|
|
150
151
|
specification_version: 4
|
|
151
152
|
summary: Statsig server SDK for Ruby
|