posthog-ruby 2.8.1 → 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.
@@ -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["$groups"] = groups
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': properties,
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
- "$group_type": group_type,
77
- "$group_key": group_key,
78
- "$group_set": properties.merge(common[:properties] || {})
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]["$active_feature_flags"] = active_feature_variants.keys
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
- unless timestamp.is_a? Time
153
- raise ArgumentError, 'Timestamp must be a Time'
154
- end
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 non-blank
160
- # name - Name of the validated value
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
- if obj.nil? || (obj.is_a?(String) && obj.empty?)
163
- raise ArgumentError, "#{name} must be given"
164
- end
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
@@ -42,7 +42,7 @@ class PostHog
42
42
  if defined?(Rails)
43
43
  Rails.logger
44
44
  else
45
- logger = Logger.new STDOUT
45
+ logger = Logger.new $stdout
46
46
  logger.progname = 'PostHog'
47
47
  logger.level = Logger::WARN
48
48
  logger
@@ -9,7 +9,8 @@ class PostHog
9
9
  # Does nothing
10
10
  end
11
11
 
12
- def is_requesting?
12
+ # TODO: Rename to `requesting?` in future version
13
+ def is_requesting? # rubocop:disable Naming/PredicateName
13
14
  false
14
15
  end
15
16
  end
@@ -52,7 +52,8 @@ class PostHog
52
52
 
53
53
  # public: Check whether we have outstanding requests.
54
54
  #
55
- def is_requesting?
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
 
@@ -21,10 +21,10 @@ class PostHog
21
21
  options[:port] = uri.port
22
22
  end
23
23
 
24
- options[:host] = !options[:host].nil? ? options[:host] : HOST
25
- options[:port] = !options[:port].nil? ? options[:port] : PORT
26
- options[:ssl] = !options[:ssl].nil? ? options[:ssl] : 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
- "api key = #{api_key}, batch = #{JSON.generate(batch)}"
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['STUB']
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
- extend self
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
- '%08x-%04x-%04x-%04x-%04x%08x' % arr
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
- (seconds < 0 ? '-' : '+'),
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
- parsed_date = value
93
- return parsed_date
89
+ value
90
+
94
91
  elsif value.respond_to?(:to_str)
95
92
  begin
96
- parsed_date = DateTime.parse(value)
97
- return parsed_date
98
- rescue ArgumentError => e
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.new("The date provided must be a string or date object")
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
- def is_valid_regex(regex)
110
- begin
111
- Regexp.new(regex)
112
- return true
113
- rescue RegexpError
114
- return false
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
@@ -1,3 +1,3 @@
1
1
  class PostHog
2
- VERSION = '2.8.1'
2
+ VERSION = '2.10.0'.freeze
3
3
  end
data/lib/posthog-ruby.rb CHANGED
@@ -1 +1,3 @@
1
+ # rubocop:disable Naming/FileName
1
2
  require 'posthog'
3
+ # rubocop:enable Naming/FileName
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: posthog-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.1
4
+ version: 2.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-04-18 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: concurrent-ruby
@@ -23,104 +23,6 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '1'
26
- - !ruby/object:Gem::Dependency
27
- name: commander
28
- requirement: !ruby/object:Gem::Requirement
29
- requirements:
30
- - - "~>"
31
- - !ruby/object:Gem::Version
32
- version: '4.4'
33
- type: :development
34
- prerelease: false
35
- version_requirements: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '4.4'
40
- - !ruby/object:Gem::Dependency
41
- name: rake
42
- requirement: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - "~>"
45
- - !ruby/object:Gem::Version
46
- version: '13.1'
47
- type: :development
48
- prerelease: false
49
- version_requirements: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - "~>"
52
- - !ruby/object:Gem::Version
53
- version: '13.1'
54
- - !ruby/object:Gem::Dependency
55
- name: rspec
56
- requirement: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - "~>"
59
- - !ruby/object:Gem::Version
60
- version: '3.13'
61
- type: :development
62
- prerelease: false
63
- version_requirements: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - "~>"
66
- - !ruby/object:Gem::Version
67
- version: '3.13'
68
- - !ruby/object:Gem::Dependency
69
- name: tzinfo
70
- requirement: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - "~>"
73
- - !ruby/object:Gem::Version
74
- version: '2.0'
75
- type: :development
76
- prerelease: false
77
- version_requirements: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - "~>"
80
- - !ruby/object:Gem::Version
81
- version: '2.0'
82
- - !ruby/object:Gem::Dependency
83
- name: activesupport
84
- requirement: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - "~>"
87
- - !ruby/object:Gem::Version
88
- version: '7.1'
89
- type: :development
90
- prerelease: false
91
- version_requirements: !ruby/object:Gem::Requirement
92
- requirements:
93
- - - "~>"
94
- - !ruby/object:Gem::Version
95
- version: '7.1'
96
- - !ruby/object:Gem::Dependency
97
- name: oj
98
- requirement: !ruby/object:Gem::Requirement
99
- requirements:
100
- - - "~>"
101
- - !ruby/object:Gem::Version
102
- version: 3.16.3
103
- type: :development
104
- prerelease: false
105
- version_requirements: !ruby/object:Gem::Requirement
106
- requirements:
107
- - - "~>"
108
- - !ruby/object:Gem::Version
109
- version: 3.16.3
110
- - !ruby/object:Gem::Dependency
111
- name: rubocop
112
- requirement: !ruby/object:Gem::Requirement
113
- requirements:
114
- - - "~>"
115
- - !ruby/object:Gem::Version
116
- version: 0.51.0
117
- type: :development
118
- prerelease: false
119
- version_requirements: !ruby/object:Gem::Requirement
120
- requirements:
121
- - - "~>"
122
- - !ruby/object:Gem::Version
123
- version: 0.51.0
124
26
  description: The PostHog ruby library
125
27
  email: hey@posthog.com
126
28
  executables:
@@ -150,7 +52,8 @@ files:
150
52
  homepage: https://github.com/PostHog/posthog-ruby
151
53
  licenses:
152
54
  - MIT
153
- metadata: {}
55
+ metadata:
56
+ rubygems_mfa_required: 'true'
154
57
  rdoc_options: []
155
58
  require_paths:
156
59
  - lib
@@ -165,7 +68,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
165
68
  - !ruby/object:Gem::Version
166
69
  version: '0'
167
70
  requirements: []
168
- rubygems_version: 3.6.6
71
+ rubygems_version: 3.6.9
169
72
  specification_version: 4
170
73
  summary: PostHog library
171
74
  test_files: []