statsig 1.9.2 → 1.12.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/dynamic_config.rb +6 -0
- data/lib/evaluator.rb +25 -2
- data/lib/layer.rb +11 -0
- data/lib/network.rb +3 -1
- data/lib/spec_store.rb +12 -6
- data/lib/statsig.rb +12 -2
- data/lib/statsig_driver.rb +19 -5
- data/lib/statsig_logger.rb +6 -4
- data/lib/statsig_options.rb +20 -3
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1590d900487869f3cbd76aae50f38ccb535b372ba0468a446ab5574ccfc782eb
|
4
|
+
data.tar.gz: 1e94c594d470d0baa21a073177677d6c631297aa1fe51747853ef25106b55c6e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b2ceb928a74c83e00b783aa46ea45010dbd9daa4a8932a29528e7edc10d9393a6cad9139c26bcd98b13509957565637593c4d0115bd5f929425dc7872f54260
|
7
|
+
data.tar.gz: db7812f360b4e68b402107fedea8f7d7c591bdb83ad206265b871d22b837c6be04e867c51d879a1730dbcc8cec76097879e47f601d42d8e1c456018dda5dd44e
|
data/lib/dynamic_config.rb
CHANGED
@@ -13,4 +13,10 @@ class DynamicConfig
|
|
13
13
|
return default_value if @value.nil? || !@value.key?(index)
|
14
14
|
@value[index]
|
15
15
|
end
|
16
|
+
|
17
|
+
def get_typed(index, default_value)
|
18
|
+
return default_value if @value.nil? || !@value.key?(index)
|
19
|
+
return default_value if @value[index].class != default_value.class and default_value.class != TrueClass and default_value.class != FalseClass
|
20
|
+
@value[index]
|
21
|
+
end
|
16
22
|
end
|
data/lib/evaluator.rb
CHANGED
@@ -12,19 +12,34 @@ $type_dynamic_config = 'dynamic_config'
|
|
12
12
|
|
13
13
|
module Statsig
|
14
14
|
class Evaluator
|
15
|
-
def initialize(network, error_callback)
|
16
|
-
@spec_store = Statsig::SpecStore.new(network, error_callback)
|
15
|
+
def initialize(network, options, error_callback)
|
16
|
+
@spec_store = Statsig::SpecStore.new(network, error_callback, options.rulesets_sync_interval, options.idlists_sync_interval)
|
17
17
|
@ua_parser = UserAgentParser::Parser.new
|
18
18
|
CountryLookup.initialize
|
19
19
|
@initialized = true
|
20
|
+
|
21
|
+
@gate_overrides = {}
|
22
|
+
@config_overrides = {}
|
20
23
|
end
|
21
24
|
|
22
25
|
def check_gate(user, gate_name)
|
26
|
+
return Statsig::ConfigResult.new(
|
27
|
+
gate_name,
|
28
|
+
@gate_overrides[gate_name],
|
29
|
+
@gate_overrides[gate_name],
|
30
|
+
'override',
|
31
|
+
[]) unless !@gate_overrides.has_key?(gate_name)
|
23
32
|
return nil unless @initialized && @spec_store.has_gate?(gate_name)
|
24
33
|
eval_spec(user, @spec_store.get_gate(gate_name))
|
25
34
|
end
|
26
35
|
|
27
36
|
def get_config(user, config_name)
|
37
|
+
return Statsig::ConfigResult.new(
|
38
|
+
config_name,
|
39
|
+
false,
|
40
|
+
@config_overrides[config_name],
|
41
|
+
'override',
|
42
|
+
[]) unless !@config_overrides.has_key?(config_name)
|
28
43
|
return nil unless @initialized && @spec_store.has_config?(config_name)
|
29
44
|
eval_spec(user, @spec_store.get_config(config_name))
|
30
45
|
end
|
@@ -38,6 +53,14 @@ module Statsig
|
|
38
53
|
@spec_store.shutdown
|
39
54
|
end
|
40
55
|
|
56
|
+
def override_gate(gate, value)
|
57
|
+
@gate_overrides[gate] = value
|
58
|
+
end
|
59
|
+
|
60
|
+
def override_config(config, value)
|
61
|
+
@config_overrides[config] = value
|
62
|
+
end
|
63
|
+
|
41
64
|
private
|
42
65
|
|
43
66
|
def eval_spec(user, config)
|
data/lib/layer.rb
CHANGED
@@ -18,4 +18,15 @@ class Layer
|
|
18
18
|
|
19
19
|
@value[index]
|
20
20
|
end
|
21
|
+
|
22
|
+
def get_typed(index, default_value)
|
23
|
+
return default_value if @value.nil? || !@value.key?(index)
|
24
|
+
return default_value if @value[index].class != default_value.class and default_value.class != TrueClass and default_value.class != FalseClass
|
25
|
+
|
26
|
+
if @exposure_log_func.is_a? Proc
|
27
|
+
@exposure_log_func.call(self, index)
|
28
|
+
end
|
29
|
+
|
30
|
+
@value[index]
|
31
|
+
end
|
21
32
|
end
|
data/lib/network.rb
CHANGED
@@ -6,18 +6,20 @@ $retry_codes = [408, 500, 502, 503, 504, 522, 524, 599]
|
|
6
6
|
|
7
7
|
module Statsig
|
8
8
|
class Network
|
9
|
-
def initialize(server_secret, api, backoff_mult = 10)
|
9
|
+
def initialize(server_secret, api, local_mode, backoff_mult = 10)
|
10
10
|
super()
|
11
11
|
unless api.end_with?('/')
|
12
12
|
api += '/'
|
13
13
|
end
|
14
14
|
@server_secret = server_secret
|
15
15
|
@api = api
|
16
|
+
@local_mode = local_mode
|
16
17
|
@backoff_multiplier = backoff_mult
|
17
18
|
@session_id = SecureRandom.uuid
|
18
19
|
end
|
19
20
|
|
20
21
|
def post_helper(endpoint, body, retries = 0, backoff = 1)
|
22
|
+
return nil unless !@local_mode
|
21
23
|
http = HTTP.headers(
|
22
24
|
{"STATSIG-API-KEY" => @server_secret,
|
23
25
|
"STATSIG-CLIENT-TIME" => (Time.now.to_f * 1000).to_s,
|
data/lib/spec_store.rb
CHANGED
@@ -5,10 +5,10 @@ require 'id_list'
|
|
5
5
|
|
6
6
|
module Statsig
|
7
7
|
class SpecStore
|
8
|
-
def initialize(network, error_callback = nil,
|
8
|
+
def initialize(network, error_callback = nil, rulesets_sync_interval = 10, id_lists_sync_interval = 60)
|
9
9
|
@network = network
|
10
10
|
@last_sync_time = 0
|
11
|
-
@
|
11
|
+
@rulesets_sync_interval = rulesets_sync_interval
|
12
12
|
@id_lists_sync_interval = id_lists_sync_interval
|
13
13
|
@store = {
|
14
14
|
:gates => {},
|
@@ -16,8 +16,8 @@ module Statsig
|
|
16
16
|
:layers => {},
|
17
17
|
:id_lists => {},
|
18
18
|
}
|
19
|
-
|
20
|
-
|
19
|
+
@error_callback = error_callback
|
20
|
+
download_config_specs
|
21
21
|
get_id_lists
|
22
22
|
|
23
23
|
@config_sync_thread = sync_config_specs
|
@@ -65,7 +65,7 @@ module Statsig
|
|
65
65
|
def sync_config_specs
|
66
66
|
Thread.new do
|
67
67
|
loop do
|
68
|
-
sleep @
|
68
|
+
sleep @rulesets_sync_interval
|
69
69
|
download_config_specs
|
70
70
|
end
|
71
71
|
end
|
@@ -81,10 +81,16 @@ module Statsig
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def download_config_specs
|
84
|
+
e = get_config_specs_from_network
|
85
|
+
@error_callback.call(e) unless e.nil? or @error_callback.nil?
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_config_specs_from_network
|
84
89
|
begin
|
85
90
|
response, e = @network.post_helper('download_config_specs', JSON.generate({'sinceTime' => @last_sync_time}))
|
86
91
|
if e.nil?
|
87
92
|
process(JSON.parse(response.body))
|
93
|
+
nil
|
88
94
|
else
|
89
95
|
e
|
90
96
|
end
|
@@ -145,7 +151,7 @@ module Statsig
|
|
145
151
|
next
|
146
152
|
end
|
147
153
|
|
148
|
-
#
|
154
|
+
# reset local list if server list returns a newer file
|
149
155
|
if server_list.file_id != local_list.file_id && server_list.creation_time >= local_list.creation_time
|
150
156
|
local_list = IDList.new(list)
|
151
157
|
local_list.size = 0
|
data/lib/statsig.rb
CHANGED
@@ -30,7 +30,7 @@ module Statsig
|
|
30
30
|
@shared_instance&.get_layer(user, layer_name)
|
31
31
|
end
|
32
32
|
|
33
|
-
def self.log_event(user, event_name, value, metadata)
|
33
|
+
def self.log_event(user, event_name, value = nil, metadata = nil)
|
34
34
|
ensure_initialized
|
35
35
|
@shared_instance&.log_event(user, event_name, value, metadata)
|
36
36
|
end
|
@@ -42,10 +42,20 @@ module Statsig
|
|
42
42
|
@shared_instance = nil
|
43
43
|
end
|
44
44
|
|
45
|
+
def self.override_gate(gate_name, gate_value)
|
46
|
+
ensure_initialized
|
47
|
+
@shared_instance&.override_gate(gate_name, gate_value)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.override_config(config_name, config_value)
|
51
|
+
ensure_initialized
|
52
|
+
@shared_instance&.override_config(config_name, config_value)
|
53
|
+
end
|
54
|
+
|
45
55
|
def self.get_statsig_metadata
|
46
56
|
{
|
47
57
|
'sdkType' => 'ruby-server',
|
48
|
-
'sdkVersion' => '1.
|
58
|
+
'sdkVersion' => '1.12.0',
|
49
59
|
}
|
50
60
|
end
|
51
61
|
|
data/lib/statsig_driver.rb
CHANGED
@@ -22,9 +22,9 @@ class StatsigDriver
|
|
22
22
|
@options = options || StatsigOptions.new
|
23
23
|
@shutdown = false
|
24
24
|
@secret_key = secret_key
|
25
|
-
@net = Statsig::Network.new(secret_key, @options.api_url_base)
|
26
|
-
@logger = Statsig::StatsigLogger.new(@net)
|
27
|
-
@evaluator = Statsig::Evaluator.new(@net, error_callback)
|
25
|
+
@net = Statsig::Network.new(secret_key, @options.api_url_base, @options.local_mode)
|
26
|
+
@logger = Statsig::StatsigLogger.new(@net, @options)
|
27
|
+
@evaluator = Statsig::Evaluator.new(@net, @options, error_callback)
|
28
28
|
end
|
29
29
|
|
30
30
|
def check_gate(user, gate_name)
|
@@ -98,6 +98,14 @@ class StatsigDriver
|
|
98
98
|
@evaluator.shutdown
|
99
99
|
end
|
100
100
|
|
101
|
+
def override_gate(gate_name, gate_value)
|
102
|
+
@evaluator.override_gate(gate_name, gate_value)
|
103
|
+
end
|
104
|
+
|
105
|
+
def override_config(config_name, config_value)
|
106
|
+
@evaluator.override_config(config_name, config_value)
|
107
|
+
end
|
108
|
+
|
101
109
|
private
|
102
110
|
|
103
111
|
def verify_inputs(user, config_name, variable_name)
|
@@ -127,8 +135,14 @@ class StatsigDriver
|
|
127
135
|
end
|
128
136
|
|
129
137
|
def validate_user(user)
|
130
|
-
if user.nil? ||
|
131
|
-
|
138
|
+
if user.nil? ||
|
139
|
+
!user.instance_of?(StatsigUser) ||
|
140
|
+
(
|
141
|
+
# user_id is nil and custom_ids is not a hash with entries
|
142
|
+
!user.user_id.is_a?(String) &&
|
143
|
+
(!user.custom_ids.is_a?(Hash) || user.custom_ids.size == 0)
|
144
|
+
)
|
145
|
+
raise 'Must provide a valid StatsigUser with a user_id or at least a custom ID. See https://docs.statsig.com/messages/serverRequiredUserID/ for more details.'
|
132
146
|
end
|
133
147
|
end
|
134
148
|
|
data/lib/statsig_logger.rb
CHANGED
@@ -6,15 +6,16 @@ $layer_exposure_event = 'statsig::layer_exposure'
|
|
6
6
|
|
7
7
|
module Statsig
|
8
8
|
class StatsigLogger
|
9
|
-
def initialize(network)
|
9
|
+
def initialize(network, options)
|
10
10
|
@network = network
|
11
11
|
@events = []
|
12
12
|
@background_flush = periodic_flush
|
13
|
+
@options = options
|
13
14
|
end
|
14
15
|
|
15
16
|
def log_event(event)
|
16
17
|
@events.push(event)
|
17
|
-
if @events.length >=
|
18
|
+
if @events.length >= @options.logging_max_buffer_size
|
18
19
|
flush
|
19
20
|
end
|
20
21
|
end
|
@@ -70,7 +71,7 @@ module Statsig
|
|
70
71
|
def periodic_flush
|
71
72
|
Thread.new do
|
72
73
|
loop do
|
73
|
-
sleep
|
74
|
+
sleep @options.logging_interval_seconds
|
74
75
|
flush
|
75
76
|
end
|
76
77
|
end
|
@@ -83,8 +84,9 @@ module Statsig
|
|
83
84
|
if @events.length == 0
|
84
85
|
return
|
85
86
|
end
|
86
|
-
|
87
|
+
events_clone = @events
|
87
88
|
@events = []
|
89
|
+
flush_events = events_clone.map { |e| e.serialize }
|
88
90
|
|
89
91
|
@network.post_logs(flush_events)
|
90
92
|
end
|
data/lib/statsig_options.rb
CHANGED
@@ -1,9 +1,26 @@
|
|
1
1
|
class StatsigOptions
|
2
|
-
|
3
|
-
|
2
|
+
attr_accessor :environment
|
3
|
+
attr_accessor :api_url_base
|
4
|
+
attr_accessor :rulesets_sync_interval
|
5
|
+
attr_accessor :idlists_sync_interval
|
6
|
+
attr_accessor :logging_interval_seconds
|
7
|
+
attr_accessor :logging_max_buffer_size
|
8
|
+
attr_accessor :local_mode
|
4
9
|
|
5
|
-
def initialize(
|
10
|
+
def initialize(
|
11
|
+
environment=nil,
|
12
|
+
api_url_base='https://statsigapi.net/v1',
|
13
|
+
rulesets_sync_interval: 10,
|
14
|
+
idlists_sync_interval: 60,
|
15
|
+
logging_interval_seconds: 60,
|
16
|
+
logging_max_buffer_size: 1000,
|
17
|
+
local_mode: false)
|
6
18
|
@environment = environment.is_a?(Hash) ? environment : nil
|
7
19
|
@api_url_base = api_url_base
|
20
|
+
@rulesets_sync_interval = rulesets_sync_interval
|
21
|
+
@idlists_sync_interval = idlists_sync_interval
|
22
|
+
@logging_interval_seconds = logging_interval_seconds
|
23
|
+
@logging_max_buffer_size = [logging_max_buffer_size, 1000].min
|
24
|
+
@local_mode = local_mode
|
8
25
|
end
|
9
26
|
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.12.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-08-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -153,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
153
153
|
- !ruby/object:Gem::Version
|
154
154
|
version: '0'
|
155
155
|
requirements: []
|
156
|
-
rubygems_version: 3.2.
|
156
|
+
rubygems_version: 3.2.32
|
157
157
|
signing_key:
|
158
158
|
specification_version: 4
|
159
159
|
summary: Statsig server SDK for Ruby
|