statsig 1.24.5 → 1.25.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/evaluator.rb +7 -5
- data/lib/layer.rb +1 -0
- data/lib/network.rb +13 -3
- data/lib/spec_store.rb +12 -15
- data/lib/statsig.rb +2 -2
- data/lib/statsig_driver.rb +1 -1
- data/lib/statsig_event.rb +2 -4
- data/lib/statsig_logger.rb +6 -8
- data/lib/statsig_options.rb +25 -2
- data/lib/ua_parser.rb +57 -0
- metadata +81 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 267f2a16a3bb05a6b046d493bfe98f00b7c083f387eba7862641801ec0579b2b
|
4
|
+
data.tar.gz: a20de08fe3b12bcba40795ec1ff53ce5d3c2c27aca3ab41d72541a49e730414c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7afa3f47ea19f3d759a648f12bd98c8e56c1c757f2c86bc72859ace8ca081e205af90f315cd26c08598fed408a8b4163b452e2312066120d5ead9644f1028ec
|
7
|
+
data.tar.gz: d671a37cf7ad21212d71ad90c8fddab13110641a278c475c6f4cec3ff922c0d6b6bceaa1367b902933db481be56601af2ab203c03768a65e9dd959fb900a2438
|
data/lib/evaluator.rb
CHANGED
@@ -6,7 +6,7 @@ require 'evaluation_helpers'
|
|
6
6
|
require 'client_initialize_helpers'
|
7
7
|
require 'spec_store'
|
8
8
|
require 'time'
|
9
|
-
require '
|
9
|
+
require 'ua_parser'
|
10
10
|
require 'evaluation_details'
|
11
11
|
require 'user_agent_parser/operating_system'
|
12
12
|
|
@@ -19,8 +19,8 @@ module Statsig
|
|
19
19
|
|
20
20
|
def initialize(network, options, error_callback, init_diagnostics = nil)
|
21
21
|
@spec_store = Statsig::SpecStore.new(network, options, error_callback, init_diagnostics)
|
22
|
-
|
23
|
-
CountryLookup.
|
22
|
+
UAParser.initialize_async
|
23
|
+
CountryLookup.initialize_async
|
24
24
|
|
25
25
|
@gate_overrides = {}
|
26
26
|
@config_overrides = {}
|
@@ -447,16 +447,18 @@ module Statsig
|
|
447
447
|
ua = get_value_from_user(user, 'userAgent')
|
448
448
|
return nil unless ua.is_a?(String)
|
449
449
|
|
450
|
-
parsed = @ua_parser.parse ua
|
451
|
-
os = parsed.os
|
452
450
|
case field.downcase
|
453
451
|
when 'os_name', 'osname'
|
452
|
+
os = UAParser.parse_os(ua)
|
454
453
|
return os&.family
|
455
454
|
when 'os_version', 'osversion'
|
455
|
+
os = UAParser.parse_os(ua)
|
456
456
|
return os&.version unless os&.version.nil?
|
457
457
|
when 'browser_name', 'browsername'
|
458
|
+
parsed = UAParser.parse_ua(ua)
|
458
459
|
return parsed.family
|
459
460
|
when 'browser_version', 'browserversion'
|
461
|
+
parsed = UAParser.parse_ua(ua)
|
460
462
|
return parsed.version.to_s
|
461
463
|
else
|
462
464
|
nil
|
data/lib/layer.rb
CHANGED
data/lib/network.rb
CHANGED
@@ -33,6 +33,8 @@ module Statsig
|
|
33
33
|
@local_mode = options.local_mode
|
34
34
|
@timeout = options.network_timeout
|
35
35
|
@backoff_multiplier = backoff_mult
|
36
|
+
@post_logs_retry_backoff = options.post_logs_retry_backoff
|
37
|
+
@post_logs_retry_limit = options.post_logs_retry_limit
|
36
38
|
@session_id = SecureRandom.uuid
|
37
39
|
end
|
38
40
|
|
@@ -57,18 +59,26 @@ module Statsig
|
|
57
59
|
if @timeout
|
58
60
|
http = http.timeout(@timeout)
|
59
61
|
end
|
62
|
+
backoff_adjusted = backoff > 10 ? backoff += Random.rand(10) : backoff # to deter overlap
|
63
|
+
if @post_logs_retry_backoff
|
64
|
+
if @post_logs_retry_backoff.is_a? Integer
|
65
|
+
backoff_adjusted = @post_logs_retry_backoff
|
66
|
+
else
|
67
|
+
backoff_adjusted = @post_logs_retry_backoff.call(retries)
|
68
|
+
end
|
69
|
+
end
|
60
70
|
begin
|
61
71
|
res = http.post(@api + endpoint, body: body)
|
62
72
|
rescue StandardError => e
|
63
73
|
## network error retry
|
64
74
|
return nil, e unless retries > 0
|
65
|
-
sleep
|
75
|
+
sleep backoff_adjusted
|
66
76
|
return post_helper(endpoint, body, retries - 1, backoff * @backoff_multiplier)
|
67
77
|
end
|
68
78
|
return res, nil if res.status.success?
|
69
79
|
return nil, NetworkError.new("Got an exception when making request to #{@api + endpoint}: #{res.to_s}", res.status.to_i) unless retries > 0 && $retry_codes.include?(res.code)
|
70
80
|
## status code retry
|
71
|
-
sleep
|
81
|
+
sleep backoff_adjusted
|
72
82
|
post_helper(endpoint, body, retries - 1, backoff * @backoff_multiplier)
|
73
83
|
end
|
74
84
|
|
@@ -97,7 +107,7 @@ module Statsig
|
|
97
107
|
def post_logs(events)
|
98
108
|
begin
|
99
109
|
json_body = JSON.generate({ 'events' => events, 'statsigMetadata' => Statsig.get_statsig_metadata })
|
100
|
-
post_helper('log_event', json_body,
|
110
|
+
post_helper('log_event', json_body, @post_logs_retry_limit)
|
101
111
|
rescue
|
102
112
|
end
|
103
113
|
end
|
data/lib/spec_store.rb
CHANGED
@@ -32,6 +32,7 @@ module Statsig
|
|
32
32
|
|
33
33
|
@id_list_thread_pool = Concurrent::FixedThreadPool.new(
|
34
34
|
options.idlist_threadpool_size,
|
35
|
+
name: 'statsig-idlist',
|
35
36
|
max_queue: 100,
|
36
37
|
fallback_policy: :discard,
|
37
38
|
)
|
@@ -309,6 +310,16 @@ module Statsig
|
|
309
310
|
|
310
311
|
init_diagnostics&.mark("get_id_lists", "start", "process", new_id_lists.length)
|
311
312
|
|
313
|
+
delete_lists = []
|
314
|
+
local_id_lists.each do |list_name, list|
|
315
|
+
unless new_id_lists.key? list_name
|
316
|
+
delete_lists.push list_name
|
317
|
+
end
|
318
|
+
end
|
319
|
+
delete_lists.each do |list_name|
|
320
|
+
local_id_lists.delete list_name
|
321
|
+
end
|
322
|
+
|
312
323
|
new_id_lists.each do |list_name, list|
|
313
324
|
new_list = IDList.new(list)
|
314
325
|
local_list = get_id_list(list_name)
|
@@ -346,21 +357,7 @@ module Statsig
|
|
346
357
|
end
|
347
358
|
|
348
359
|
result = Concurrent::Promise.all?(*tasks).execute.wait(@id_lists_sync_interval)
|
349
|
-
|
350
|
-
init_diagnostics&.mark("get_id_lists", "end", "process", false)
|
351
|
-
return # timed out
|
352
|
-
end
|
353
|
-
|
354
|
-
delete_lists = []
|
355
|
-
local_id_lists.each do |list_name, list|
|
356
|
-
unless new_id_lists.key? list_name
|
357
|
-
delete_lists.push list_name
|
358
|
-
end
|
359
|
-
end
|
360
|
-
delete_lists.each do |list_name|
|
361
|
-
local_id_lists.delete list_name
|
362
|
-
end
|
363
|
-
init_diagnostics&.mark("get_id_lists", "end", "process", true)
|
360
|
+
init_diagnostics&.mark("get_id_lists", "end", "process", result.state == :fulfilled)
|
364
361
|
end
|
365
362
|
|
366
363
|
def get_single_id_list_from_adapter(list)
|
data/lib/statsig.rb
CHANGED
@@ -148,7 +148,7 @@ module Statsig
|
|
148
148
|
@shared_instance&.get_layer(user, layer_name, StatsigDriver::GetLayerOptions.new(log_exposure: false))
|
149
149
|
end
|
150
150
|
|
151
|
-
sig { params(user: StatsigUser, layer_name: String, parameter_name: String).
|
151
|
+
sig { params(user: StatsigUser, layer_name: String, parameter_name: String).void }
|
152
152
|
##
|
153
153
|
# Logs an exposure event for the parameter in the given layer
|
154
154
|
#
|
@@ -227,7 +227,7 @@ module Statsig
|
|
227
227
|
def self.get_statsig_metadata
|
228
228
|
{
|
229
229
|
'sdkType' => 'ruby-server',
|
230
|
-
'sdkVersion' => '1.24.
|
230
|
+
'sdkVersion' => '1.24.6',
|
231
231
|
}
|
232
232
|
end
|
233
233
|
|
data/lib/statsig_driver.rb
CHANGED
@@ -170,7 +170,6 @@ class StatsigDriver
|
|
170
170
|
event.user = user
|
171
171
|
event.value = value
|
172
172
|
event.metadata = metadata
|
173
|
-
event.statsig_metadata = Statsig.get_statsig_metadata
|
174
173
|
@logger.log_event(event)
|
175
174
|
})
|
176
175
|
end
|
@@ -199,6 +198,7 @@ class StatsigDriver
|
|
199
198
|
# @return [Hash]
|
200
199
|
def get_client_initialize_response(user)
|
201
200
|
@err_boundary.capture(-> {
|
201
|
+
validate_user(user)
|
202
202
|
normalize_user(user)
|
203
203
|
@evaluator.get_client_initialize_response(user)
|
204
204
|
}, -> { nil })
|
data/lib/statsig_event.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
# typed: true
|
2
2
|
class StatsigEvent
|
3
|
-
attr_accessor :value
|
4
|
-
attr_accessor :metadata
|
5
|
-
attr_accessor :statsig_metadata
|
6
|
-
attr_accessor :secondary_exposures
|
3
|
+
attr_accessor :value, :metadata, :statsig_metadata, :secondary_exposures
|
7
4
|
attr_reader :user
|
8
5
|
|
9
6
|
def initialize(event_name)
|
@@ -13,6 +10,7 @@ class StatsigEvent
|
|
13
10
|
@secondary_exposures = nil
|
14
11
|
@user = nil
|
15
12
|
@time = (Time.now.to_f * 1000).to_i
|
13
|
+
@statsig_metadata = Statsig.get_statsig_metadata
|
16
14
|
end
|
17
15
|
|
18
16
|
def user=(value)
|
data/lib/statsig_logger.rb
CHANGED
@@ -15,11 +15,12 @@ module Statsig
|
|
15
15
|
@options = options
|
16
16
|
|
17
17
|
@logging_pool = Concurrent::ThreadPoolExecutor.new(
|
18
|
-
|
19
|
-
|
18
|
+
name: 'statsig-logger',
|
19
|
+
min_threads: @options.logger_threadpool_size,
|
20
|
+
max_threads: @options.logger_threadpool_size,
|
20
21
|
# max jobs pending before we start dropping
|
21
|
-
max_queue:
|
22
|
-
fallback_policy: :discard
|
22
|
+
max_queue: 100,
|
23
|
+
fallback_policy: :discard
|
23
24
|
)
|
24
25
|
|
25
26
|
@background_flush = periodic_flush
|
@@ -44,7 +45,6 @@ module Statsig
|
|
44
45
|
}
|
45
46
|
return false if not is_unique_exposure(user, $gate_exposure_event, metadata)
|
46
47
|
event.metadata = metadata
|
47
|
-
event.statsig_metadata = Statsig.get_statsig_metadata
|
48
48
|
|
49
49
|
event.secondary_exposures = secondary_exposures.is_a?(Array) ? secondary_exposures : []
|
50
50
|
|
@@ -62,7 +62,6 @@ module Statsig
|
|
62
62
|
}
|
63
63
|
return false if not is_unique_exposure(user, $config_exposure_event, metadata)
|
64
64
|
event.metadata = metadata
|
65
|
-
event.statsig_metadata = Statsig.get_statsig_metadata
|
66
65
|
event.secondary_exposures = secondary_exposures.is_a?(Array) ? secondary_exposures : []
|
67
66
|
|
68
67
|
safe_add_eval_details(eval_details, event)
|
@@ -90,7 +89,6 @@ module Statsig
|
|
90
89
|
}
|
91
90
|
return false if not is_unique_exposure(user, $layer_exposure_event, metadata)
|
92
91
|
event.metadata = metadata
|
93
|
-
event.statsig_metadata = Statsig.get_statsig_metadata
|
94
92
|
event.secondary_exposures = exposures.is_a?(Array) ? exposures : []
|
95
93
|
|
96
94
|
safe_add_eval_details(config_evaluation.evaluation_details, event)
|
@@ -109,7 +107,7 @@ module Statsig
|
|
109
107
|
Thread.new do
|
110
108
|
loop do
|
111
109
|
sleep @options.logging_interval_seconds
|
112
|
-
|
110
|
+
flush_async
|
113
111
|
@interval += 1
|
114
112
|
@deduper.clear if @interval % 2 == 0
|
115
113
|
end
|
data/lib/statsig_options.rb
CHANGED
@@ -64,6 +64,11 @@ class StatsigOptions
|
|
64
64
|
# default: 3
|
65
65
|
attr_accessor :idlist_threadpool_size
|
66
66
|
|
67
|
+
sig { returns(Integer) }
|
68
|
+
# The number of threads allocated to posting event logs.
|
69
|
+
# default: 3
|
70
|
+
attr_accessor :logger_threadpool_size
|
71
|
+
|
67
72
|
sig { returns(T::Boolean) }
|
68
73
|
# Should diagnostics be logged. These include performance metrics for initialize.
|
69
74
|
# default: false
|
@@ -79,6 +84,15 @@ class StatsigOptions
|
|
79
84
|
# Number of seconds before a network call is timed out
|
80
85
|
attr_accessor :network_timeout
|
81
86
|
|
87
|
+
sig { returns(Integer) }
|
88
|
+
# Number of times to retry sending a batch of failed log events
|
89
|
+
attr_accessor :post_logs_retry_limit
|
90
|
+
|
91
|
+
sig { returns(T.any(Method, Proc, Integer, NilClass)) }
|
92
|
+
# The number of seconds, or a function that returns the number of seconds based on the number of retries remaining
|
93
|
+
# which overrides the default backoff time between retries
|
94
|
+
attr_accessor :post_logs_retry_backoff
|
95
|
+
|
82
96
|
sig do
|
83
97
|
params(
|
84
98
|
environment: T.any(T::Hash[String, String], NilClass),
|
@@ -92,9 +106,12 @@ class StatsigOptions
|
|
92
106
|
rules_updated_callback: T.any(Method, Proc, NilClass),
|
93
107
|
data_store: T.any(Statsig::Interfaces::IDataStore, NilClass),
|
94
108
|
idlist_threadpool_size: Integer,
|
109
|
+
logger_threadpool_size: Integer,
|
95
110
|
disable_diagnostics_logging: T::Boolean,
|
96
111
|
disable_sorbet_logging_handlers: T::Boolean,
|
97
|
-
network_timeout: T.any(Integer, NilClass)
|
112
|
+
network_timeout: T.any(Integer, NilClass),
|
113
|
+
post_logs_retry_limit: Integer,
|
114
|
+
post_logs_retry_backoff: T.any(Method, Proc, Integer, NilClass)
|
98
115
|
).void
|
99
116
|
end
|
100
117
|
|
@@ -110,9 +127,12 @@ class StatsigOptions
|
|
110
127
|
rules_updated_callback: nil,
|
111
128
|
data_store: nil,
|
112
129
|
idlist_threadpool_size: 3,
|
130
|
+
logger_threadpool_size: 3,
|
113
131
|
disable_diagnostics_logging: false,
|
114
132
|
disable_sorbet_logging_handlers: false,
|
115
|
-
network_timeout: nil
|
133
|
+
network_timeout: nil,
|
134
|
+
post_logs_retry_limit: 3,
|
135
|
+
post_logs_retry_backoff: nil)
|
116
136
|
@environment = environment.is_a?(Hash) ? environment : nil
|
117
137
|
@api_url_base = api_url_base
|
118
138
|
@rulesets_sync_interval = rulesets_sync_interval
|
@@ -124,8 +144,11 @@ class StatsigOptions
|
|
124
144
|
@rules_updated_callback = rules_updated_callback
|
125
145
|
@data_store = data_store
|
126
146
|
@idlist_threadpool_size = idlist_threadpool_size
|
147
|
+
@logger_threadpool_size = logger_threadpool_size
|
127
148
|
@disable_diagnostics_logging = disable_diagnostics_logging
|
128
149
|
@disable_sorbet_logging_handlers = disable_sorbet_logging_handlers
|
129
150
|
@network_timeout = network_timeout
|
151
|
+
@post_logs_retry_limit = post_logs_retry_limit
|
152
|
+
@post_logs_retry_backoff = post_logs_retry_backoff
|
130
153
|
end
|
131
154
|
end
|
data/lib/ua_parser.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'user_agent_parser'
|
2
|
+
|
3
|
+
module UAParser
|
4
|
+
class Parser
|
5
|
+
def initialize
|
6
|
+
@ua_parser = UserAgentParser::Parser.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse_os(*args)
|
10
|
+
@ua_parser.parse_os(*args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse_ua(*args)
|
14
|
+
@ua_parser.parse_ua(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse_device(*args)
|
18
|
+
@ua_parser.parse_device(*args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.initialize
|
23
|
+
if !@initialize_bg_thread.nil? && @initialize_bg_thread.alive?
|
24
|
+
@initialize_bg_thread.kill.join
|
25
|
+
end
|
26
|
+
@parser = Parser.new
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.initialize_async
|
30
|
+
if !@initialize_bg_thread.nil? && @initialize_bg_thread.alive?
|
31
|
+
@initialize_bg_thread.kill.join
|
32
|
+
end
|
33
|
+
@initialize_bg_thread = Thread.new { @parser = Parser.new }
|
34
|
+
@initialize_bg_thread
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.parse_os(*args)
|
38
|
+
if @parser.nil?
|
39
|
+
initialize
|
40
|
+
end
|
41
|
+
@parser.parse_os(*args)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.parse_ua(*args)
|
45
|
+
if @parser.nil?
|
46
|
+
initialize
|
47
|
+
end
|
48
|
+
@parser.parse_ua(*args)
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.parse_device(*args)
|
52
|
+
if @parser.nil?
|
53
|
+
initialize
|
54
|
+
end
|
55
|
+
@parser.parse_device(*args)
|
56
|
+
end
|
57
|
+
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.25.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: 2023-
|
11
|
+
date: 2023-05-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 5.14.0
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 5.14.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: spy
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -123,19 +123,89 @@ dependencies:
|
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '6.0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: rubocop
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 1.28.2
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 1.28.2
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: parallel_tests
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
128
142
|
requirements:
|
129
143
|
- - "~>"
|
130
144
|
- !ruby/object:Gem::Version
|
131
145
|
version: '2.7'
|
132
|
-
type: :
|
146
|
+
type: :development
|
133
147
|
prerelease: false
|
134
148
|
version_requirements: !ruby/object:Gem::Requirement
|
135
149
|
requirements:
|
136
150
|
- - "~>"
|
137
151
|
- !ruby/object:Gem::Version
|
138
152
|
version: '2.7'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: simplecov
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0.21'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0.21'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: simplecov-lcov
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - "~>"
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: 0.7.0
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - "~>"
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: 0.7.0
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: simplecov-cobertura
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - "~>"
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '2.1'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - "~>"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '2.1'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: user_agent_parser
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - "~>"
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: 2.15.0
|
202
|
+
type: :runtime
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - "~>"
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: 2.15.0
|
139
209
|
- !ruby/object:Gem::Dependency
|
140
210
|
name: http
|
141
211
|
requirement: !ruby/object:Gem::Requirement
|
@@ -160,16 +230,16 @@ dependencies:
|
|
160
230
|
name: ip3country
|
161
231
|
requirement: !ruby/object:Gem::Requirement
|
162
232
|
requirements:
|
163
|
-
- -
|
233
|
+
- - "~>"
|
164
234
|
- !ruby/object:Gem::Version
|
165
|
-
version: 0.
|
235
|
+
version: 0.2.1
|
166
236
|
type: :runtime
|
167
237
|
prerelease: false
|
168
238
|
version_requirements: !ruby/object:Gem::Requirement
|
169
239
|
requirements:
|
170
|
-
- -
|
240
|
+
- - "~>"
|
171
241
|
- !ruby/object:Gem::Version
|
172
|
-
version: 0.
|
242
|
+
version: 0.2.1
|
173
243
|
- !ruby/object:Gem::Dependency
|
174
244
|
name: sorbet-runtime
|
175
245
|
requirement: !ruby/object:Gem::Requirement
|
@@ -224,6 +294,7 @@ files:
|
|
224
294
|
- lib/statsig_logger.rb
|
225
295
|
- lib/statsig_options.rb
|
226
296
|
- lib/statsig_user.rb
|
297
|
+
- lib/ua_parser.rb
|
227
298
|
homepage: https://rubygems.org/gems/statsig
|
228
299
|
licenses:
|
229
300
|
- ISC
|