posthog-ruby 2.9.0 → 2.10.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/bin/posthog +7 -7
- data/lib/posthog/client.rb +120 -44
- data/lib/posthog/defaults.rb +4 -5
- data/lib/posthog/feature_flag.rb +27 -26
- data/lib/posthog/feature_flags.rb +247 -177
- data/lib/posthog/field_parser.rb +30 -19
- data/lib/posthog/logging.rb +1 -1
- data/lib/posthog/noop_worker.rb +2 -1
- data/lib/posthog/send_worker.rb +2 -1
- data/lib/posthog/transport.rb +8 -10
- data/lib/posthog/utils.rb +21 -28
- data/lib/posthog/version.rb +1 -1
- data/lib/posthog-ruby.rb +2 -0
- metadata +5 -105
data/lib/posthog/field_parser.rb
CHANGED
@@ -1,30 +1,36 @@
|
|
1
|
+
require 'posthog/logging'
|
2
|
+
|
1
3
|
class PostHog
|
2
4
|
class FieldParser
|
3
5
|
class << self
|
4
6
|
include PostHog::Utils
|
7
|
+
include PostHog::Logging
|
5
8
|
|
6
9
|
# In addition to the common fields, capture accepts:
|
7
10
|
#
|
8
11
|
# - "event"
|
9
12
|
# - "properties"
|
10
13
|
# - "groups"
|
14
|
+
# - "uuid"
|
11
15
|
def parse_for_capture(fields)
|
12
16
|
common = parse_common_fields(fields)
|
13
17
|
|
14
18
|
event = fields[:event]
|
15
19
|
properties = fields[:properties] || {}
|
16
20
|
groups = fields[:groups]
|
17
|
-
|
21
|
+
uuid = fields[:uuid]
|
18
22
|
check_presence!(event, 'event')
|
19
23
|
check_is_hash!(properties, 'properties')
|
20
24
|
|
21
25
|
if groups
|
22
26
|
check_is_hash!(groups, 'groups')
|
23
|
-
properties[
|
27
|
+
properties['$groups'] = groups
|
24
28
|
end
|
25
29
|
|
26
30
|
isoify_dates! properties
|
27
31
|
|
32
|
+
common['uuid'] = uuid if uuid? uuid
|
33
|
+
|
28
34
|
common.merge(
|
29
35
|
{
|
30
36
|
type: 'capture',
|
@@ -49,7 +55,7 @@ class PostHog
|
|
49
55
|
{
|
50
56
|
type: 'identify',
|
51
57
|
event: '$identify',
|
52
|
-
'$set'
|
58
|
+
:'$set' => properties,
|
53
59
|
properties: properties.merge(common[:properties] || {})
|
54
60
|
}
|
55
61
|
)
|
@@ -73,10 +79,10 @@ class PostHog
|
|
73
79
|
{
|
74
80
|
event: '$groupidentify',
|
75
81
|
properties: {
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
}
|
82
|
+
:'$group_type' => group_type,
|
83
|
+
:'$group_key' => group_key,
|
84
|
+
:'$group_set' => properties.merge(common[:properties] || {})
|
85
|
+
}
|
80
86
|
}
|
81
87
|
)
|
82
88
|
end
|
@@ -139,34 +145,39 @@ class PostHog
|
|
139
145
|
active_feature_variants = {}
|
140
146
|
feature_variants.each do |key, value|
|
141
147
|
parsed[:properties]["$feature/#{key}"] = value
|
142
|
-
if value != false
|
143
|
-
active_feature_variants[key] = value
|
144
|
-
end
|
148
|
+
active_feature_variants[key] = value if value != false
|
145
149
|
end
|
146
|
-
parsed[:properties][
|
150
|
+
parsed[:properties]['$active_feature_flags'] = active_feature_variants.keys
|
147
151
|
end
|
148
152
|
parsed
|
149
153
|
end
|
150
154
|
|
151
155
|
def check_timestamp!(timestamp)
|
152
|
-
|
153
|
-
|
154
|
-
|
156
|
+
return if timestamp.is_a? Time
|
157
|
+
|
158
|
+
raise ArgumentError, 'Timestamp must be a Time'
|
155
159
|
end
|
156
160
|
|
157
161
|
# private: Ensures that a string is non-empty
|
158
162
|
#
|
159
|
-
# obj - String|Number that must be
|
160
|
-
# name -
|
163
|
+
# obj - String|Number that must be not blank
|
164
|
+
# name - The name of the validated value
|
161
165
|
def check_presence!(obj, name)
|
162
|
-
|
163
|
-
|
164
|
-
|
166
|
+
return unless obj.nil? || (obj.is_a?(String) && obj.empty?)
|
167
|
+
|
168
|
+
raise ArgumentError, "#{name} must be given"
|
165
169
|
end
|
166
170
|
|
167
171
|
def check_is_hash!(obj, name)
|
168
172
|
raise ArgumentError, "#{name} must be a Hash" unless obj.is_a? Hash
|
169
173
|
end
|
174
|
+
|
175
|
+
def uuid?(uuid)
|
176
|
+
is_valid_uuid = uuid.match?(/^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$/i) if uuid
|
177
|
+
logger.warn "UUID is not valid: #{uuid}. Ignoring it." unless is_valid_uuid
|
178
|
+
|
179
|
+
is_valid_uuid
|
180
|
+
end
|
170
181
|
end
|
171
182
|
end
|
172
183
|
end
|
data/lib/posthog/logging.rb
CHANGED
data/lib/posthog/noop_worker.rb
CHANGED
data/lib/posthog/send_worker.rb
CHANGED
@@ -52,7 +52,8 @@ class PostHog
|
|
52
52
|
|
53
53
|
# public: Check whether we have outstanding requests.
|
54
54
|
#
|
55
|
-
|
55
|
+
# TODO: Rename to `requesting?` in future version
|
56
|
+
def is_requesting? # rubocop:disable Naming/PredicateName
|
56
57
|
@lock.synchronize { !@batch.empty? }
|
57
58
|
end
|
58
59
|
|
data/lib/posthog/transport.rb
CHANGED
@@ -21,10 +21,10 @@ class PostHog
|
|
21
21
|
options[:port] = uri.port
|
22
22
|
end
|
23
23
|
|
24
|
-
options[:host] =
|
25
|
-
options[:port] =
|
26
|
-
options[:ssl] =
|
27
|
-
|
24
|
+
options[:host] = options[:host].nil? ? HOST : options[:host]
|
25
|
+
options[:port] = options[:port].nil? ? PORT : options[:port]
|
26
|
+
options[:ssl] = options[:ssl].nil? ? SSL : options[:ssl]
|
27
|
+
|
28
28
|
@headers = options[:headers] || HEADERS
|
29
29
|
@path = options[:path] || PATH
|
30
30
|
@retries = options[:retries] || RETRIES
|
@@ -34,9 +34,7 @@ class PostHog
|
|
34
34
|
http.use_ssl = options[:ssl]
|
35
35
|
http.read_timeout = 8
|
36
36
|
http.open_timeout = 4
|
37
|
-
if options[:skip_ssl_verification]
|
38
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
39
|
-
end
|
37
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if options[:skip_ssl_verification]
|
40
38
|
|
41
39
|
@http = http
|
42
40
|
end
|
@@ -77,7 +75,7 @@ class PostHog
|
|
77
75
|
def should_retry_request?(status_code, body)
|
78
76
|
if status_code >= 500
|
79
77
|
true # Server error
|
80
|
-
elsif status_code == 429
|
78
|
+
elsif status_code == 429 # rubocop:disable Lint/DuplicateBranch
|
81
79
|
true # Rate limited
|
82
80
|
elsif status_code >= 400
|
83
81
|
logger.error(body)
|
@@ -123,7 +121,7 @@ class PostHog
|
|
123
121
|
|
124
122
|
if self.class.stub
|
125
123
|
logger.debug "stubbed request to #{@path}: " \
|
126
|
-
|
124
|
+
"api key = #{api_key}, batch = #{JSON.generate(batch)}"
|
127
125
|
|
128
126
|
[200, '{}']
|
129
127
|
else
|
@@ -137,7 +135,7 @@ class PostHog
|
|
137
135
|
attr_writer :stub
|
138
136
|
|
139
137
|
def stub
|
140
|
-
@stub || ENV
|
138
|
+
@stub || ENV.fetch('STUB', nil)
|
141
139
|
end
|
142
140
|
end
|
143
141
|
end
|
data/lib/posthog/utils.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
require 'securerandom'
|
2
2
|
|
3
3
|
class PostHog
|
4
|
-
|
5
4
|
class InconclusiveMatchError < StandardError
|
6
5
|
end
|
7
6
|
|
8
7
|
module Utils
|
9
|
-
|
8
|
+
module_function
|
10
9
|
|
11
10
|
# public: Return a new hash with keys converted from strings to symbols
|
12
11
|
#
|
@@ -47,7 +46,8 @@ class PostHog
|
|
47
46
|
arr = SecureRandom.random_bytes(16).unpack('NnnnnN')
|
48
47
|
arr[2] = (arr[2] & 0x0fff) | 0x4000
|
49
48
|
arr[3] = (arr[3] & 0x3fff) | 0x8000
|
50
|
-
|
49
|
+
|
50
|
+
'%08x-%04x-%04x-%04x-%04x%08x' % arr # rubocop:disable Style/FormatStringToken, Style/FormatString
|
51
51
|
end
|
52
52
|
|
53
53
|
def datetime_in_iso8601(datetime)
|
@@ -65,7 +65,7 @@ class PostHog
|
|
65
65
|
|
66
66
|
def time_in_iso8601(time, fraction_digits = 3)
|
67
67
|
fraction =
|
68
|
-
(('.%06i' % time.usec)[0, fraction_digits + 1] if fraction_digits > 0)
|
68
|
+
(('.%06i' % time.usec)[0, fraction_digits + 1] if fraction_digits > 0) # rubocop:disable Style/FormatString
|
69
69
|
|
70
70
|
"#{time.strftime('%Y-%m-%dT%H:%M:%S')}#{fraction}#{formatted_offset(time, true, 'Z')}"
|
71
71
|
end
|
@@ -75,44 +75,39 @@ class PostHog
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def formatted_offset(time, colon = true, alternate_utc_string = nil)
|
78
|
-
time.utc? && alternate_utc_string ||
|
78
|
+
(time.utc? && alternate_utc_string) ||
|
79
79
|
seconds_to_utc_offset(time.utc_offset, colon)
|
80
80
|
end
|
81
81
|
|
82
82
|
def seconds_to_utc_offset(seconds, colon = true)
|
83
|
-
(colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON)
|
84
|
-
|
85
|
-
(seconds.abs / 3600),
|
86
|
-
((seconds.abs % 3600) / 60)
|
87
|
-
]
|
83
|
+
format((colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON), (seconds < 0 ? '-' : '+'),
|
84
|
+
seconds.abs / 3600, (seconds.abs % 3600) / 60)
|
88
85
|
end
|
89
86
|
|
90
87
|
def convert_to_datetime(value)
|
91
88
|
if value.respond_to?(:strftime)
|
92
|
-
|
93
|
-
|
89
|
+
value
|
90
|
+
|
94
91
|
elsif value.respond_to?(:to_str)
|
95
92
|
begin
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
raise InconclusiveMatchError.new("#{value} is not in a valid date format")
|
93
|
+
DateTime.parse(value)
|
94
|
+
rescue ArgumentError
|
95
|
+
raise InconclusiveMatchError, "#{value} is not in a valid date format"
|
100
96
|
end
|
101
97
|
else
|
102
|
-
raise InconclusiveMatchError
|
98
|
+
raise InconclusiveMatchError, 'The date provided must be a string or date object'
|
103
99
|
end
|
104
100
|
end
|
105
101
|
|
106
|
-
UTC_OFFSET_WITH_COLON = '%s%02d:%02d'
|
102
|
+
UTC_OFFSET_WITH_COLON = '%s%02d:%02d'.freeze
|
107
103
|
UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.sub(':', '')
|
108
104
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
105
|
+
# TODO: Rename to `valid_regex?` in future version
|
106
|
+
def is_valid_regex(regex) # rubocop:disable Naming/PredicateName
|
107
|
+
Regexp.new(regex)
|
108
|
+
true
|
109
|
+
rescue RegexpError
|
110
|
+
false
|
116
111
|
end
|
117
112
|
|
118
113
|
class SizeLimitedHash < Hash
|
@@ -122,9 +117,7 @@ class PostHog
|
|
122
117
|
end
|
123
118
|
|
124
119
|
def []=(key, value)
|
125
|
-
if length >= @max_length
|
126
|
-
clear
|
127
|
-
end
|
120
|
+
clear if length >= @max_length
|
128
121
|
super
|
129
122
|
end
|
130
123
|
end
|
data/lib/posthog/version.rb
CHANGED
data/lib/posthog-ruby.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: posthog-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ''
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: concurrent-ruby
|
@@ -24,104 +23,6 @@ dependencies:
|
|
24
23
|
- - "~>"
|
25
24
|
- !ruby/object:Gem::Version
|
26
25
|
version: '1'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: commander
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '4.4'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '4.4'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rake
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '13.1'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '13.1'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: rspec
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '3.13'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '3.13'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: tzinfo
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '2.0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '2.0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: activesupport
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - "~>"
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '7.1'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - "~>"
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '7.1'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: oj
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - "~>"
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: 3.16.3
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - "~>"
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: 3.16.3
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: rubocop
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - "~>"
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: 0.51.0
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - "~>"
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: 0.51.0
|
125
26
|
description: The PostHog ruby library
|
126
27
|
email: hey@posthog.com
|
127
28
|
executables:
|
@@ -151,8 +52,8 @@ files:
|
|
151
52
|
homepage: https://github.com/PostHog/posthog-ruby
|
152
53
|
licenses:
|
153
54
|
- MIT
|
154
|
-
metadata:
|
155
|
-
|
55
|
+
metadata:
|
56
|
+
rubygems_mfa_required: 'true'
|
156
57
|
rdoc_options: []
|
157
58
|
require_paths:
|
158
59
|
- lib
|
@@ -167,8 +68,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
68
|
- !ruby/object:Gem::Version
|
168
69
|
version: '0'
|
169
70
|
requirements: []
|
170
|
-
rubygems_version: 3.
|
171
|
-
signing_key:
|
71
|
+
rubygems_version: 3.6.9
|
172
72
|
specification_version: 4
|
173
73
|
summary: PostHog library
|
174
74
|
test_files: []
|