statsig 1.24.5 → 1.25.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/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
|