statsig 1.9.2 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: afbf6d972c5aeead4eeaa8c352155c41b02a3710f55d80309939d5acc0fd1e80
4
- data.tar.gz: c83f0399868af1d7978aeb2d2723f4bfbc860646de3ea0a7aaf840903489b92a
3
+ metadata.gz: 1590d900487869f3cbd76aae50f38ccb535b372ba0468a446ab5574ccfc782eb
4
+ data.tar.gz: 1e94c594d470d0baa21a073177677d6c631297aa1fe51747853ef25106b55c6e
5
5
  SHA512:
6
- metadata.gz: babe4ac6dc398bd87df7537ea7c9391e72e4a32c4ec80944895adb507351a02042dd2bfc28deb2336e706fcf531ba4d30dfdeeb280404b108a25505fcd749ed8
7
- data.tar.gz: c1bad8f27c2675b7dbc125090a56062e71c5fddb6f0cc8464c0e9ea03b910f9ca918a9ccf4e6c8db99815b7964aaf543d0cc69db5a873bcf58ddc806ef8932cc
6
+ metadata.gz: 7b2ceb928a74c83e00b783aa46ea45010dbd9daa4a8932a29528e7edc10d9393a6cad9139c26bcd98b13509957565637593c4d0115bd5f929425dc7872f54260
7
+ data.tar.gz: db7812f360b4e68b402107fedea8f7d7c591bdb83ad206265b871d22b837c6be04e867c51d879a1730dbcc8cec76097879e47f601d42d8e1c456018dda5dd44e
@@ -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, config_sync_interval = 10, id_lists_sync_interval = 60)
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
- @config_sync_interval = config_sync_interval
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
- e = download_config_specs
20
- error_callback.call(e) unless error_callback.nil?
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 @config_sync_interval
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
- # skip if server list returns a newer file
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.9.2',
58
+ 'sdkVersion' => '1.12.0',
49
59
  }
50
60
  end
51
61
 
@@ -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? || !user.instance_of?(StatsigUser) || !user.user_id.is_a?(String)
131
- 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.'
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
 
@@ -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 >= 500
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 60
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
- flush_events = @events.map { |e| e.serialize }
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
@@ -1,9 +1,26 @@
1
1
  class StatsigOptions
2
- attr_reader :environment
3
- attr_reader :api_url_base
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(environment = nil, api_url_base = 'https://statsigapi.net/v1')
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.9.2
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-05-29 00:00:00.000000000 Z
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.3
156
+ rubygems_version: 3.2.32
157
157
  signing_key:
158
158
  specification_version: 4
159
159
  summary: Statsig server SDK for Ruby