posthog-ruby 1.2.0 → 1.2.1
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/bin/posthog +57 -39
- data/lib/posthog/backoff_policy.rb +3 -6
- data/lib/posthog/client.rb +34 -23
- data/lib/posthog/defaults.rb +8 -6
- data/lib/posthog/feature_flags.rb +94 -88
- data/lib/posthog/field_parser.rb +37 -29
- data/lib/posthog/logging.rb +8 -8
- data/lib/posthog/message_batch.rb +2 -1
- data/lib/posthog/response.rb +1 -2
- data/lib/posthog/transport.rb +13 -17
- data/lib/posthog/utils.rb +11 -12
- data/lib/posthog/version.rb +1 -1
- data/lib/posthog/worker.rb +0 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1c78909b0d23aea369a70193696c779c04de90bf5c7725c2d3402c982ba2568
|
4
|
+
data.tar.gz: bf4c5400441cefdf1b9037b27e3b2bfd572317cd7309b30fc89ae56548a2c1c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d4c65100cf0971d2382ec47c29346fd88e2d1ceab61a82694a96ad60d28607edcc08a3f4023d13235e87350f4ac3e9ccace34d7c2138b70a850895c6fa2482c
|
7
|
+
data.tar.gz: 7054d04f4c9346e18c638370d39858b98dfd9653e5731bcd286556f686978d652ef414c37f040a11272030624d86858419eccab147a50c88c4ca0226916451e3
|
data/bin/posthog
CHANGED
@@ -11,32 +11,39 @@ program :version, '1.0.0'
|
|
11
11
|
program :description, 'PostHog API'
|
12
12
|
|
13
13
|
def json_hash(str)
|
14
|
-
if str
|
15
|
-
return JSON.parse(str)
|
16
|
-
end
|
14
|
+
return JSON.parse(str) if str
|
17
15
|
end
|
18
16
|
|
19
17
|
command :capture do |c|
|
20
18
|
c.description = 'capture an event'
|
21
19
|
|
22
20
|
c.option '--api-key=<string>', String, 'The PostHog API Key'
|
23
|
-
c.option '--api-host=<url>',
|
24
|
-
|
21
|
+
c.option '--api-host=<url>',
|
22
|
+
String,
|
23
|
+
'The PostHog API URL host part (scheme+domain)'
|
24
|
+
c.option '--distinct-id=<distinct_id>',
|
25
|
+
String,
|
26
|
+
'The distinct id to send the event as'
|
25
27
|
c.option '--event=<event>', String, 'The event name to send with the event'
|
26
28
|
c.option '--properties=<properties>', 'The properties to send (JSON-encoded)'
|
27
29
|
|
28
30
|
c.action do |args, options|
|
29
|
-
posthog =
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
31
|
+
posthog =
|
32
|
+
PostHog::Client.new(
|
33
|
+
{
|
34
|
+
api_key: options.api_key,
|
35
|
+
api_host: options.api_host,
|
36
|
+
on_error: Proc.new { |status, msg| print msg }
|
37
|
+
}
|
38
|
+
)
|
39
|
+
|
40
|
+
posthog.capture(
|
41
|
+
{
|
42
|
+
distinct_id: options.distinct_id,
|
43
|
+
event: options.event,
|
44
|
+
properties: json_hash(options.properties)
|
45
|
+
}
|
46
|
+
)
|
40
47
|
|
41
48
|
posthog.flush
|
42
49
|
end
|
@@ -46,21 +53,30 @@ command :identify do |c|
|
|
46
53
|
c.description = 'identify the user'
|
47
54
|
|
48
55
|
c.option '--api-key=<api_key>', String, 'The PostHog API Key'
|
49
|
-
c.option '--api-host=<url>',
|
50
|
-
|
56
|
+
c.option '--api-host=<url>',
|
57
|
+
String,
|
58
|
+
'The PostHog API URL host part (scheme+domain)'
|
59
|
+
c.option '--distinct-id=<distinct_id>',
|
60
|
+
String,
|
61
|
+
'The distinct id to send the event as'
|
51
62
|
c.option '--properties=<properties>', 'The properties to send (JSON-encoded)'
|
52
63
|
|
53
64
|
c.action do |args, options|
|
54
|
-
posthog =
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
65
|
+
posthog =
|
66
|
+
PostHog::Client.new(
|
67
|
+
{
|
68
|
+
api_key: options.api_key,
|
69
|
+
api_host: options.api_host,
|
70
|
+
on_error: Proc.new { |status, msg| print msg }
|
71
|
+
}
|
72
|
+
)
|
73
|
+
|
74
|
+
posthog.identify(
|
75
|
+
{
|
76
|
+
distinct_id: options.distinct_id,
|
77
|
+
properties: json_hash(options.properties)
|
78
|
+
}
|
79
|
+
)
|
64
80
|
|
65
81
|
posthog.flush
|
66
82
|
end
|
@@ -70,21 +86,23 @@ command :alias do |c|
|
|
70
86
|
c.description = 'set an alias for a distinct id'
|
71
87
|
|
72
88
|
c.option '--api-key=<api_key>', String, 'The PostHog API Key'
|
73
|
-
c.option '--api-host=<url>',
|
89
|
+
c.option '--api-host=<url>',
|
90
|
+
String,
|
91
|
+
'The PostHog API URL host part (scheme+domain)'
|
74
92
|
c.option '--distinct-id=<distinct_id>', String, 'The distinct id'
|
75
93
|
c.option '--alias=<alias>', 'The alias to give to the distinct id'
|
76
94
|
|
77
95
|
c.action do |args, options|
|
78
|
-
posthog =
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
})
|
96
|
+
posthog =
|
97
|
+
PostHog::Client.new(
|
98
|
+
{
|
99
|
+
api_key: options.api_key,
|
100
|
+
api_host: options.api_host,
|
101
|
+
on_error: Proc.new { |status, msg| print msg }
|
102
|
+
}
|
103
|
+
)
|
104
|
+
|
105
|
+
posthog.alias({ distinct_id: options.distinct_id, alias: options.alias })
|
88
106
|
|
89
107
|
posthog.flush
|
90
108
|
end
|
@@ -15,7 +15,8 @@ class PostHog
|
|
15
15
|
@min_timeout_ms = opts[:min_timeout_ms] || MIN_TIMEOUT_MS
|
16
16
|
@max_timeout_ms = opts[:max_timeout_ms] || MAX_TIMEOUT_MS
|
17
17
|
@multiplier = opts[:multiplier] || MULTIPLIER
|
18
|
-
@randomization_factor =
|
18
|
+
@randomization_factor =
|
19
|
+
opts[:randomization_factor] || RANDOMIZATION_FACTOR
|
19
20
|
|
20
21
|
@attempts = 0
|
21
22
|
end
|
@@ -37,11 +38,7 @@ class PostHog
|
|
37
38
|
max_deviation = base * randomization_factor
|
38
39
|
deviation = random_number * max_deviation
|
39
40
|
|
40
|
-
|
41
|
-
base - deviation
|
42
|
-
else
|
43
|
-
base + deviation
|
44
|
-
end
|
41
|
+
random_number < 0.5 ? base - deviation : base + deviation
|
45
42
|
end
|
46
43
|
end
|
47
44
|
end
|
data/lib/posthog/client.rb
CHANGED
@@ -7,7 +7,6 @@ require 'posthog/utils'
|
|
7
7
|
require 'posthog/worker'
|
8
8
|
require 'posthog/feature_flags'
|
9
9
|
|
10
|
-
|
11
10
|
class PostHog
|
12
11
|
class Client
|
13
12
|
include PostHog::Utils
|
@@ -34,14 +33,18 @@ class PostHog
|
|
34
33
|
|
35
34
|
if opts[:personal_api_key].present?
|
36
35
|
@personal_api_key = opts[:personal_api_key]
|
37
|
-
@feature_flags_poller =
|
36
|
+
@feature_flags_poller =
|
37
|
+
FeatureFlagsPoller.new(
|
38
|
+
opts[:feature_flags_polling_interval],
|
39
|
+
opts[:personal_api_key],
|
40
|
+
@api_key,
|
41
|
+
opts[:host]
|
42
|
+
)
|
38
43
|
end
|
39
44
|
|
40
|
-
|
41
45
|
at_exit { @worker_thread && @worker_thread[:should_exit] = true }
|
42
46
|
end
|
43
47
|
|
44
|
-
|
45
48
|
# Synchronously waits until the worker has flushed the queue.
|
46
49
|
#
|
47
50
|
# Use only for scripts which are not long-running, and will specifically
|
@@ -98,26 +101,37 @@ class PostHog
|
|
98
101
|
@queue.length
|
99
102
|
end
|
100
103
|
|
101
|
-
def is_feature_enabled(flag_key, distinct_id, default_value=false)
|
104
|
+
def is_feature_enabled(flag_key, distinct_id, default_value = false)
|
102
105
|
unless @personal_api_key
|
103
|
-
logger.error(
|
106
|
+
logger.error(
|
107
|
+
'You need to specify a personal_api_key to use feature flags'
|
108
|
+
)
|
104
109
|
return
|
105
110
|
end
|
106
|
-
is_enabled =
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
111
|
+
is_enabled =
|
112
|
+
@feature_flags_poller.is_feature_enabled(
|
113
|
+
flag_key,
|
114
|
+
distinct_id,
|
115
|
+
default_value
|
116
|
+
)
|
117
|
+
capture(
|
118
|
+
{
|
119
|
+
'distinct_id': distinct_id,
|
120
|
+
'event': '$feature_flag_called',
|
121
|
+
'properties': {
|
122
|
+
'$feature_flag': flag_key,
|
123
|
+
'$feature_flag_response': is_enabled
|
124
|
+
}
|
125
|
+
}
|
126
|
+
)
|
127
|
+
return is_enabled
|
116
128
|
end
|
117
129
|
|
118
130
|
def reload_feature_flags
|
119
131
|
unless @personal_api_key
|
120
|
-
logger.error(
|
132
|
+
logger.error(
|
133
|
+
'You need to specify a personal_api_key to use feature flags'
|
134
|
+
)
|
121
135
|
return
|
122
136
|
end
|
123
137
|
@feature_flags_poller.load_feature_flags(true)
|
@@ -145,8 +159,8 @@ class PostHog
|
|
145
159
|
else
|
146
160
|
logger.warn(
|
147
161
|
'Queue is full, dropping events. The :max_queue_size ' \
|
148
|
-
|
149
|
-
|
162
|
+
'configuration parameter can be increased to prevent this from ' \
|
163
|
+
'happening.'
|
150
164
|
)
|
151
165
|
false
|
152
166
|
end
|
@@ -161,9 +175,7 @@ class PostHog
|
|
161
175
|
return if worker_running?
|
162
176
|
@worker_mutex.synchronize do
|
163
177
|
return if worker_running?
|
164
|
-
@worker_thread = Thread.new
|
165
|
-
@worker.run
|
166
|
-
end
|
178
|
+
@worker_thread = Thread.new { @worker.run }
|
167
179
|
end
|
168
180
|
end
|
169
181
|
|
@@ -172,4 +184,3 @@ class PostHog
|
|
172
184
|
end
|
173
185
|
end
|
174
186
|
end
|
175
|
-
|
data/lib/posthog/defaults.rb
CHANGED
@@ -5,18 +5,20 @@ class PostHog
|
|
5
5
|
PORT = 443
|
6
6
|
PATH = '/batch/'
|
7
7
|
SSL = true
|
8
|
-
HEADERS = {
|
9
|
-
|
10
|
-
|
8
|
+
HEADERS = {
|
9
|
+
'Accept' => 'application/json',
|
10
|
+
'Content-Type' => 'application/json',
|
11
|
+
'User-Agent' => "posthog-ruby/#{PostHog::VERSION}"
|
12
|
+
}
|
11
13
|
RETRIES = 10
|
12
14
|
end
|
13
15
|
|
14
16
|
module Queue
|
15
|
-
MAX_SIZE =
|
17
|
+
MAX_SIZE = 10_000
|
16
18
|
end
|
17
19
|
|
18
20
|
module Message
|
19
|
-
MAX_BYTES =
|
21
|
+
MAX_BYTES = 32_768 # 32Kb
|
20
22
|
end
|
21
23
|
|
22
24
|
module MessageBatch
|
@@ -26,7 +28,7 @@ class PostHog
|
|
26
28
|
|
27
29
|
module BackoffPolicy
|
28
30
|
MIN_TIMEOUT_MS = 100
|
29
|
-
MAX_TIMEOUT_MS =
|
31
|
+
MAX_TIMEOUT_MS = 10_000
|
30
32
|
MULTIPLIER = 1.5
|
31
33
|
RANDOMIZATION_FACTOR = 0.5
|
32
34
|
end
|
@@ -5,119 +5,125 @@ require 'posthog/version'
|
|
5
5
|
require 'posthog/logging'
|
6
6
|
require 'digest'
|
7
7
|
class PostHog
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
def is_feature_enabled(key, distinct_id, default_result = false)
|
31
|
-
# make sure they're loaded on first run
|
32
|
-
load_feature_flags
|
8
|
+
class FeatureFlagsPoller
|
9
|
+
include PostHog::Logging
|
10
|
+
|
11
|
+
def initialize(polling_interval, personal_api_key, project_api_key, host)
|
12
|
+
@polling_interval = polling_interval || 60 * 5
|
13
|
+
@personal_api_key = personal_api_key
|
14
|
+
@project_api_key = project_api_key
|
15
|
+
@host = host || 'app.posthog.com'
|
16
|
+
@feature_flags = Concurrent::Array.new
|
17
|
+
@loaded_flags_successfully_once = Concurrent::AtomicBoolean.new
|
18
|
+
|
19
|
+
@task =
|
20
|
+
Concurrent::TimerTask.new(
|
21
|
+
execution_interval: polling_interval,
|
22
|
+
timeout_interval: 15
|
23
|
+
) { _load_feature_flags }
|
24
|
+
|
25
|
+
# load once before timer
|
26
|
+
load_feature_flags
|
27
|
+
@task.execute
|
28
|
+
end
|
33
29
|
|
30
|
+
def is_feature_enabled(key, distinct_id, default_result = false)
|
31
|
+
# make sure they're loaded on first run
|
32
|
+
load_feature_flags
|
34
33
|
|
35
|
-
|
36
|
-
return default_result
|
37
|
-
end
|
34
|
+
return default_result unless @loaded_flags_successfully_once
|
38
35
|
|
39
|
-
|
36
|
+
feature_flag = nil
|
40
37
|
|
41
|
-
# puts @feature_flags
|
42
38
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
39
|
+
@feature_flags.each do |flag|
|
40
|
+
if key == flag['key']
|
41
|
+
feature_flag = flag
|
42
|
+
break
|
48
43
|
end
|
44
|
+
end
|
49
45
|
|
50
|
-
|
51
|
-
return default_result
|
52
|
-
end
|
46
|
+
return default_result if !feature_flag
|
53
47
|
|
54
|
-
|
55
|
-
if feature_flag['
|
56
|
-
|
48
|
+
flag_rollout_pctg =
|
49
|
+
if feature_flag['rollout_percentage']
|
50
|
+
feature_flag['rollout_percentage']
|
57
51
|
else
|
58
|
-
|
59
|
-
res = _request('POST', 'decide', false, data)
|
60
|
-
return res['featureFlags'].include? key
|
52
|
+
100
|
61
53
|
end
|
62
|
-
|
63
|
-
return
|
54
|
+
if feature_flag['is_simple_flag']
|
55
|
+
return is_simple_flag_enabled(key, distinct_id, flag_rollout_pctg)
|
56
|
+
else
|
57
|
+
data = { 'distinct_id' => distinct_id }
|
58
|
+
res = _request('POST', 'decide', false, data)
|
59
|
+
return res['featureFlags'].include? key
|
64
60
|
end
|
65
61
|
|
66
|
-
|
67
|
-
|
68
|
-
return (Integer(hash[0..14], 16).to_f / 0xfffffffffffffff) <= (rollout_percentage / 100)
|
69
|
-
end
|
62
|
+
return false
|
63
|
+
end
|
70
64
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
65
|
+
def is_simple_flag_enabled(key, distinct_id, rollout_percentage)
|
66
|
+
hash = Digest::SHA1.hexdigest "#{key}.#{distinct_id}"
|
67
|
+
return(
|
68
|
+
(Integer(hash[0..14], 16).to_f / 0xfffffffffffffff) <=
|
69
|
+
(rollout_percentage / 100)
|
70
|
+
)
|
71
|
+
end
|
77
72
|
|
78
|
-
|
79
|
-
|
73
|
+
def load_feature_flags(force_reload = false)
|
74
|
+
if @loaded_flags_successfully_once.false? || force_reload
|
75
|
+
_load_feature_flags
|
80
76
|
end
|
77
|
+
end
|
81
78
|
|
82
|
-
|
79
|
+
def shutdown_poller()
|
80
|
+
@task.shutdown
|
81
|
+
end
|
83
82
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
83
|
+
private
|
84
|
+
|
85
|
+
def _load_feature_flags()
|
86
|
+
res = _request('GET', 'api/feature_flag', true)
|
87
|
+
@feature_flags.clear
|
88
|
+
@feature_flags = res['results'].filter { |flag| flag['active'] }
|
89
|
+
if @loaded_flags_successfully_once.false?
|
90
|
+
@loaded_flags_successfully_once.make_true
|
91
91
|
end
|
92
|
+
end
|
92
93
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
94
|
+
def _request(method, endpoint, use_personal_api_key = false, data = {})
|
95
|
+
uri = URI("https://#{@host}/#{endpoint}/?token=#{@project_api_key}")
|
96
|
+
req = nil
|
97
|
+
if use_personal_api_key
|
98
|
+
req = Net::HTTP::Get.new(uri)
|
99
|
+
req['Authorization'] = "Bearer #{@personal_api_key}"
|
100
|
+
else
|
101
|
+
req = Net::HTTP::Post.new(uri)
|
102
|
+
req['Content-Type'] = 'application/json'
|
103
|
+
data['token'] = @project_api_key
|
104
|
+
req.body = data.to_json
|
105
|
+
end
|
105
106
|
|
106
|
-
|
107
|
+
req['User-Agent'] = "posthog-ruby#{PostHog::VERSION}"
|
107
108
|
|
108
|
-
|
109
|
-
|
110
|
-
|
109
|
+
begin
|
110
|
+
res_body = nil
|
111
|
+
res =
|
112
|
+
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
|
111
113
|
res = http.request(req)
|
112
114
|
res_body = JSON.parse(res.body)
|
113
115
|
return res_body
|
114
116
|
end
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
117
|
+
rescue Timeout::Error,
|
118
|
+
Errno::EINVAL,
|
119
|
+
Errno::ECONNRESET,
|
120
|
+
EOFError,
|
121
|
+
Net::HTTPBadResponse,
|
122
|
+
Net::HTTPHeaderSyntaxError,
|
123
|
+
Net::ProtocolError => e
|
124
|
+
logger.debug("Unable to complete request to #{uri}")
|
125
|
+
throw e
|
119
126
|
end
|
120
|
-
|
121
127
|
end
|
122
128
|
end
|
123
|
-
|
129
|
+
end
|
data/lib/posthog/field_parser.rb
CHANGED
@@ -18,11 +18,13 @@ class PostHog
|
|
18
18
|
|
19
19
|
isoify_dates! properties
|
20
20
|
|
21
|
-
common.merge(
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
common.merge(
|
22
|
+
{
|
23
|
+
type: 'capture',
|
24
|
+
event: event.to_s,
|
25
|
+
properties: properties.merge(common[:properties] || {})
|
26
|
+
}
|
27
|
+
)
|
26
28
|
end
|
27
29
|
|
28
30
|
# In addition to the common fields, identify accepts:
|
@@ -36,12 +38,14 @@ class PostHog
|
|
36
38
|
|
37
39
|
isoify_dates! properties
|
38
40
|
|
39
|
-
common.merge(
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
common.merge(
|
42
|
+
{
|
43
|
+
type: 'identify',
|
44
|
+
event: '$identify',
|
45
|
+
'$set': properties,
|
46
|
+
properties: properties.merge(common[:properties] || {})
|
47
|
+
}
|
48
|
+
)
|
45
49
|
end
|
46
50
|
|
47
51
|
# In addition to the common fields, alias accepts:
|
@@ -55,15 +59,17 @@ class PostHog
|
|
55
59
|
alias_field = fields[:alias]
|
56
60
|
check_presence! alias_field, 'alias'
|
57
61
|
|
58
|
-
common.merge(
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
:
|
64
|
-
|
65
|
-
|
66
|
-
|
62
|
+
common.merge(
|
63
|
+
{
|
64
|
+
type: 'alias',
|
65
|
+
event: '$create_alias',
|
66
|
+
distinct_id: nil,
|
67
|
+
properties:
|
68
|
+
{ distinct_id: distinct_id, alias: alias_field }.merge(
|
69
|
+
common[:properties] || {}
|
70
|
+
)
|
71
|
+
}
|
72
|
+
)
|
67
73
|
end
|
68
74
|
|
69
75
|
private
|
@@ -82,21 +88,23 @@ class PostHog
|
|
82
88
|
check_presence! distinct_id, 'distinct_id'
|
83
89
|
|
84
90
|
parsed = {
|
85
|
-
:
|
86
|
-
:
|
87
|
-
:
|
88
|
-
:
|
89
|
-
:
|
90
|
-
:
|
91
|
-
|
92
|
-
|
91
|
+
timestamp: datetime_in_iso8601(timestamp),
|
92
|
+
library: 'posthog-ruby',
|
93
|
+
library_version: PostHog::VERSION.to_s,
|
94
|
+
messageId: message_id,
|
95
|
+
distinct_id: distinct_id,
|
96
|
+
properties: {
|
97
|
+
'$lib' => 'posthog-ruby',
|
98
|
+
'$lib_version' => PostHog::VERSION.to_s
|
93
99
|
}
|
94
100
|
}
|
95
101
|
parsed
|
96
102
|
end
|
97
103
|
|
98
104
|
def check_timestamp!(timestamp)
|
99
|
-
|
105
|
+
unless timestamp.is_a? Time
|
106
|
+
raise ArgumentError, 'Timestamp must be a Time'
|
107
|
+
end
|
100
108
|
end
|
101
109
|
|
102
110
|
# private: Ensures that a string is non-empty
|
data/lib/posthog/logging.rb
CHANGED
@@ -30,13 +30,14 @@ class PostHog
|
|
30
30
|
def logger
|
31
31
|
return @logger if @logger
|
32
32
|
|
33
|
-
base_logger =
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
base_logger =
|
34
|
+
if defined?(Rails)
|
35
|
+
Rails.logger
|
36
|
+
else
|
37
|
+
logger = Logger.new STDOUT
|
38
|
+
logger.progname = 'PostHog'
|
39
|
+
logger
|
40
|
+
end
|
40
41
|
@logger = PrefixedLogger.new(base_logger, '[posthog-ruby]')
|
41
42
|
end
|
42
43
|
|
@@ -56,4 +57,3 @@ class PostHog
|
|
56
57
|
end
|
57
58
|
end
|
58
59
|
end
|
59
|
-
|
data/lib/posthog/response.rb
CHANGED
data/lib/posthog/transport.rb
CHANGED
@@ -26,8 +26,7 @@ class PostHog
|
|
26
26
|
@headers = options[:headers] || HEADERS
|
27
27
|
@path = options[:path] || PATH
|
28
28
|
@retries = options[:retries] || RETRIES
|
29
|
-
@backoff_policy =
|
30
|
-
options[:backoff_policy] || PostHog::BackoffPolicy.new
|
29
|
+
@backoff_policy = options[:backoff_policy] || PostHog::BackoffPolicy.new
|
31
30
|
|
32
31
|
http = Net::HTTP.new(options[:host], options[:port])
|
33
32
|
http.use_ssl = options[:ssl]
|
@@ -43,15 +42,16 @@ class PostHog
|
|
43
42
|
def send(api_key, batch)
|
44
43
|
logger.debug("Sending request for #{batch.length} items")
|
45
44
|
|
46
|
-
last_response, exception =
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
45
|
+
last_response, exception =
|
46
|
+
retry_with_backoff(@retries) do
|
47
|
+
status_code, body = send_request(api_key, batch)
|
48
|
+
error = JSON.parse(body)['error']
|
49
|
+
should_retry = should_retry_request?(status_code, body)
|
50
|
+
logger.debug("Response status code: #{status_code}")
|
51
|
+
logger.debug("Response error: #{error}") if error
|
52
52
|
|
53
|
-
|
54
|
-
|
53
|
+
[Response.new(status_code, error), should_retry]
|
54
|
+
end
|
55
55
|
|
56
56
|
if exception
|
57
57
|
logger.error(exception.message)
|
@@ -95,7 +95,7 @@ class PostHog
|
|
95
95
|
|
96
96
|
begin
|
97
97
|
result, should_retry = yield
|
98
|
-
return
|
98
|
+
return result, nil unless should_retry
|
99
99
|
rescue StandardError => e
|
100
100
|
should_retry = true
|
101
101
|
caught_exception = e
|
@@ -112,16 +112,13 @@ class PostHog
|
|
112
112
|
|
113
113
|
# Sends a request for the batch, returns [status_code, body]
|
114
114
|
def send_request(api_key, batch)
|
115
|
-
payload = JSON.generate(
|
116
|
-
api_key: api_key,
|
117
|
-
batch: batch
|
118
|
-
)
|
115
|
+
payload = JSON.generate(api_key: api_key, batch: batch)
|
119
116
|
|
120
117
|
request = Net::HTTP::Post.new(@path, @headers)
|
121
118
|
|
122
119
|
if self.class.stub
|
123
120
|
logger.debug "stubbed request to #{@path}: " \
|
124
|
-
|
121
|
+
"api key = #{api_key}, batch = #{JSON.generate(batch)}"
|
125
122
|
|
126
123
|
[200, '{}']
|
127
124
|
else
|
@@ -140,4 +137,3 @@ class PostHog
|
|
140
137
|
end
|
141
138
|
end
|
142
139
|
end
|
143
|
-
|
data/lib/posthog/utils.rb
CHANGED
@@ -7,9 +7,7 @@ class PostHog
|
|
7
7
|
# public: Return a new hash with keys converted from strings to symbols
|
8
8
|
#
|
9
9
|
def symbolize_keys(hash)
|
10
|
-
hash.each_with_object({})
|
11
|
-
memo[k.to_sym] = v
|
12
|
-
end
|
10
|
+
hash.each_with_object({}) { |(k, v), memo| memo[k.to_sym] = v }
|
13
11
|
end
|
14
12
|
|
15
13
|
# public: Convert hash keys from strings to symbols in place
|
@@ -21,9 +19,7 @@ class PostHog
|
|
21
19
|
# public: Return a new hash with keys as strings
|
22
20
|
#
|
23
21
|
def stringify_keys(hash)
|
24
|
-
hash.each_with_object({})
|
25
|
-
memo[k.to_s] = v
|
26
|
-
end
|
22
|
+
hash.each_with_object({}) { |(k, v), memo| memo[k.to_s] = v }
|
27
23
|
end
|
28
24
|
|
29
25
|
# public: Returns a new hash with all the date values in the into iso8601
|
@@ -64,9 +60,8 @@ class PostHog
|
|
64
60
|
end
|
65
61
|
|
66
62
|
def time_in_iso8601(time, fraction_digits = 3)
|
67
|
-
fraction =
|
68
|
-
|
69
|
-
end
|
63
|
+
fraction =
|
64
|
+
(('.%06i' % time.usec)[0, fraction_digits + 1] if fraction_digits > 0)
|
70
65
|
|
71
66
|
"#{time.strftime('%Y-%m-%dT%H:%M:%S')}#{fraction}#{formatted_offset(time, true, 'Z')}"
|
72
67
|
end
|
@@ -76,15 +71,19 @@ class PostHog
|
|
76
71
|
end
|
77
72
|
|
78
73
|
def formatted_offset(time, colon = true, alternate_utc_string = nil)
|
79
|
-
time.utc? && alternate_utc_string ||
|
74
|
+
time.utc? && alternate_utc_string ||
|
75
|
+
seconds_to_utc_offset(time.utc_offset, colon)
|
80
76
|
end
|
81
77
|
|
82
78
|
def seconds_to_utc_offset(seconds, colon = true)
|
83
|
-
(colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON) % [
|
79
|
+
(colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON) % [
|
80
|
+
(seconds < 0 ? '-' : '+'),
|
81
|
+
(seconds.abs / 3600),
|
82
|
+
((seconds.abs % 3600) / 60)
|
83
|
+
]
|
84
84
|
end
|
85
85
|
|
86
86
|
UTC_OFFSET_WITH_COLON = '%s%02d:%02d'
|
87
87
|
UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.sub(':', '')
|
88
88
|
end
|
89
89
|
end
|
90
|
-
|
data/lib/posthog/version.rb
CHANGED
data/lib/posthog/worker.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: posthog-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ''
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: concurrent-ruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: commander
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|