posthog-ruby 3.23.5 → 3.23.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e7240af788ef7054780bd976e8d568fc5c5deab52c8e448e18097b3ab2f4206
4
- data.tar.gz: 64fe80390d35ffd7aac25a10ff2c98584a0f74cf353ba4d697503b472e1859ee
3
+ metadata.gz: 67be906751ad3545830e00ac558a3787ba935d08c5414b216b653b63a745a424
4
+ data.tar.gz: 589fa3335b009dc16e22224e4708d55079e9b236c5c6cdfdba6f996fdb191af3
5
5
  SHA512:
6
- metadata.gz: 63adc1ce6c3381d2261076783610e1e71d04cbc4d925548dc777663d8c996228bd61ce80b3813ecec1a5b98c07ad687953b1bee408d30931a8002b1e5175ea3e
7
- data.tar.gz: 24954a02c8857733331f5976718e212786e7ce713ca60b3ea329dcbadc5defcd28d6c5cf02f684cc1f285573d972374b718653885b98daf7e05df4ecb386f6cd
6
+ metadata.gz: 1d7c92afb919cfb24e8e3a9bcfd3605cb4fce07d784019f1dd80fd0c685bc4ddde18d5b0322091fe49ad0e474cedbcae3804baf624697be650eed0418a450cdd
7
+ data.tar.gz: c9bec9d494e68a63b594c6960dc5e9d30d32d918c68df1a7dd83ba3a047179ff87bf5756f5dd0434ffde305b14a392457cbac7762c84e8ea91ed9839fab09763
@@ -141,6 +141,9 @@ module PostHog
141
141
  @api_key = opts[:api_key]
142
142
  @disabled = @api_key.nil? || @api_key.empty?
143
143
  @max_queue_size = opts[:max_queue_size] || Defaults::Queue::MAX_SIZE
144
+ @lib = opts[:_lib] || 'posthog-ruby'
145
+ @lib_version = (opts[:_lib_version] || PostHog::VERSION).to_s
146
+ @headers = Defaults::Request::HEADERS.merge('User-Agent' => "#{@lib}/#{@lib_version}")
144
147
  @worker_mutex = Mutex.new
145
148
  @shutdown_mutex = Mutex.new
146
149
  @shutdown_condition = ConditionVariable.new
@@ -154,11 +157,12 @@ module PostHog
154
157
  elsif @sync_mode
155
158
  nil
156
159
  else
157
- SendWorker.new(@queue, @api_key, opts)
160
+ SendWorker.new(@queue, @api_key, opts.merge(headers: @headers))
158
161
  end
159
162
  if @sync_mode
160
163
  @transport = Transport.new(
161
164
  api_host: opts[:host],
165
+ headers: @headers,
162
166
  skip_ssl_verification: opts[:skip_ssl_verification],
163
167
  retries: opts.key?(:max_retries) ? opts[:max_retries].to_i + 1 : 3,
164
168
  compress_request: opts[:compress_request]
@@ -187,6 +191,12 @@ module PostHog
187
191
  end
188
192
  end
189
193
 
194
+ # Initialize tracking before the poller can load definitions (including async loads).
195
+ @distinct_id_has_sent_flag_calls_mutex = Mutex.new
196
+ @distinct_id_has_sent_flag_calls = SizeLimitedHash.new(Defaults::MAX_HASH_SIZE) do |hash, key|
197
+ hash[key] = SizeLimitedArray.new(Defaults::MAX_HASH_SIZE)
198
+ end
199
+
190
200
  unless @disabled
191
201
  @feature_flags_poller =
192
202
  FeatureFlagsPoller.new(
@@ -198,15 +208,13 @@ module PostHog
198
208
  opts[:on_error],
199
209
  flag_definition_cache_provider: opts[:flag_definition_cache_provider],
200
210
  feature_flag_request_max_retries: opts[:feature_flag_request_max_retries],
201
- async_load: opts[:feature_flags_async_load] == true
211
+ async_load: opts[:feature_flags_async_load] == true,
212
+ user_agent: @headers['User-Agent'],
213
+ flag_definitions_update_mutex: @distinct_id_has_sent_flag_calls_mutex,
214
+ on_flag_definitions_updated: -> { @distinct_id_has_sent_flag_calls.clear }
202
215
  )
203
216
  end
204
217
 
205
- @distinct_id_has_sent_flag_calls_mutex = Mutex.new
206
- @distinct_id_has_sent_flag_calls = SizeLimitedHash.new(Defaults::MAX_HASH_SIZE) do |hash, key|
207
- hash[key] = SizeLimitedArray.new(Defaults::MAX_HASH_SIZE)
208
- end
209
-
210
218
  @before_send = opts[:before_send]
211
219
  @is_server = opts.fetch(:is_server, true) != false
212
220
  @deprecation_emitted_for = Concurrent::Set.new
@@ -362,6 +370,8 @@ module PostHog
362
370
  end
363
371
 
364
372
  attrs[:is_server] = @is_server
373
+ attrs[:lib] = @lib
374
+ attrs[:lib_version] = @lib_version
365
375
  message = FieldParser.parse_for_capture(attrs)
366
376
  # Minimal events are built from the allowlist after full assembly so
367
377
  # context properties and parser-added metadata can never leak in.
@@ -417,6 +427,8 @@ module PostHog
417
427
 
418
428
  symbolize_keys! attrs
419
429
  attrs[:is_server] = @is_server
430
+ attrs[:lib] = @lib
431
+ attrs[:lib_version] = @lib_version
420
432
  enqueue(FieldParser.parse_for_identify(attrs))
421
433
  end
422
434
 
@@ -435,6 +447,8 @@ module PostHog
435
447
 
436
448
  symbolize_keys! attrs
437
449
  attrs[:is_server] = @is_server
450
+ attrs[:lib] = @lib
451
+ attrs[:lib_version] = @lib_version
438
452
  enqueue(FieldParser.parse_for_group_identify(attrs))
439
453
  end
440
454
 
@@ -450,6 +464,8 @@ module PostHog
450
464
 
451
465
  symbolize_keys! attrs
452
466
  attrs[:is_server] = @is_server
467
+ attrs[:lib] = @lib
468
+ attrs[:lib_version] = @lib_version
453
469
  enqueue(FieldParser.parse_for_alias(attrs))
454
470
  end
455
471
 
@@ -44,6 +44,10 @@ module PostHog
44
44
  # @param async_load [Boolean] When true, flag definitions are fetched only on the poller thread: an
45
45
  # immediate first tick at construction, then the regular polling cadence, which keeps retrying until a
46
46
  # load succeeds.
47
+ # @param user_agent [String] User-Agent header sent with feature flag requests.
48
+ # @param flag_definitions_update_mutex [Mutex] Internal lock shared with flag-called deduplication.
49
+ # @param on_flag_definitions_updated [Proc, nil] Internal callback after definitions are applied or discarded,
50
+ # called while holding flag_definitions_update_mutex.
47
51
  def initialize(
48
52
  polling_interval,
49
53
  secret_key,
@@ -53,7 +57,10 @@ module PostHog
53
57
  on_error = nil,
54
58
  flag_definition_cache_provider: nil,
55
59
  feature_flag_request_max_retries: nil,
56
- async_load: false
60
+ async_load: false,
61
+ user_agent: "posthog-ruby/#{PostHog::VERSION}",
62
+ on_flag_definitions_updated: nil,
63
+ flag_definitions_update_mutex: Mutex.new
57
64
  )
58
65
  @polling_interval = polling_interval || Defaults::FeatureFlags::POLLING_INTERVAL_SECONDS
59
66
  @secret_key = secret_key
@@ -72,6 +79,9 @@ module PostHog
72
79
  @flags_etag = Concurrent::AtomicReference.new(nil)
73
80
  @flag_definitions_loaded_at = Concurrent::AtomicReference.new(nil)
74
81
  @async_load = async_load
82
+ @user_agent = user_agent
83
+ @on_flag_definitions_updated = on_flag_definitions_updated
84
+ @flag_definitions_update_mutex = flag_definitions_update_mutex
75
85
  # Server-controlled gate for minimal `$feature_flag_called` events, read
76
86
  # from the top-level `minimal_flag_called_events` key of the local
77
87
  # evaluation definitions payload. false when the server does not send it.
@@ -1218,14 +1228,18 @@ module PostHog
1218
1228
  '[FEATURE FLAGS] Feature flags quota limit exceeded - unsetting all local flags. ' \
1219
1229
  'Learn more about billing limits at https://posthog.com/docs/billing/limits-alerts'
1220
1230
  )
1221
- @feature_flags = Concurrent::Array.new
1222
- @feature_flags_by_key = {}
1223
- @group_type_mapping = Concurrent::Hash.new
1224
- @cohorts = Concurrent::Hash.new
1225
- @flag_definitions_loaded_at.value = nil
1226
- @minimal_flag_called_events = false
1227
- @loaded_flags_successfully_once.make_false
1228
- @quota_limited.make_true
1231
+ @flag_definitions_update_mutex.synchronize do
1232
+ definitions_were_loaded = definitions_loaded?
1233
+ @feature_flags = Concurrent::Array.new
1234
+ @feature_flags_by_key = {}
1235
+ @group_type_mapping = Concurrent::Hash.new
1236
+ @cohorts = Concurrent::Hash.new
1237
+ @flag_definitions_loaded_at.value = nil
1238
+ @minimal_flag_called_events = false
1239
+ @loaded_flags_successfully_once.make_false
1240
+ @quota_limited.make_true
1241
+ @on_flag_definitions_updated&.call if definitions_were_loaded
1242
+ end
1229
1243
  return
1230
1244
  end
1231
1245
 
@@ -1262,21 +1276,26 @@ module PostHog
1262
1276
  cohorts = get_by_symbol_or_string_key(data, 'cohorts') || {}
1263
1277
  minimal_flag_called_events = get_by_symbol_or_string_key(data, 'minimal_flag_called_events')
1264
1278
 
1265
- @feature_flags = Concurrent::Array.new(flags.map { |f| deep_symbolize_keys(f) })
1266
-
1279
+ new_flags = Concurrent::Array.new(flags.map { |f| deep_symbolize_keys(f) })
1267
1280
  new_by_key = {}
1268
- @feature_flags.each do |flag|
1281
+ new_flags.each do |flag|
1269
1282
  new_by_key[flag[:key]] = flag unless flag[:key].nil?
1270
1283
  end
1271
- @feature_flags_by_key = new_by_key
1272
-
1273
- @group_type_mapping = Concurrent::Hash[deep_symbolize_keys(group_type_mapping)]
1274
- @cohorts = Concurrent::Hash[deep_symbolize_keys(cohorts)]
1275
- @minimal_flag_called_events = minimal_flag_called_events == true
1276
-
1277
- logger.debug "Loaded #{@feature_flags.length} feature flags and #{@cohorts.length} cohorts"
1278
- @flag_definitions_loaded_at.value = (Time.now.to_f * 1000).to_i
1279
- @loaded_flags_successfully_once.make_true if @loaded_flags_successfully_once.false?
1284
+ new_group_type_mapping = Concurrent::Hash[deep_symbolize_keys(group_type_mapping)]
1285
+ new_cohorts = Concurrent::Hash[deep_symbolize_keys(cohorts)]
1286
+
1287
+ # A read of the new definitions must not record its event before the tracker reset.
1288
+ @flag_definitions_update_mutex.synchronize do
1289
+ @feature_flags = new_flags
1290
+ @feature_flags_by_key = new_by_key
1291
+ @group_type_mapping = new_group_type_mapping
1292
+ @cohorts = new_cohorts
1293
+ @minimal_flag_called_events = minimal_flag_called_events == true
1294
+ @flag_definitions_loaded_at.value = (Time.now.to_f * 1000).to_i
1295
+ @loaded_flags_successfully_once.make_true if @loaded_flags_successfully_once.false?
1296
+ @on_flag_definitions_updated&.call
1297
+ end
1298
+ logger.debug "Loaded #{new_flags.length} feature flags and #{new_cohorts.length} cohorts"
1280
1299
  end
1281
1300
 
1282
1301
  def _request_feature_flag_definitions(etag: nil)
@@ -1326,7 +1345,7 @@ module PostHog
1326
1345
  RETRYABLE_FLAGS_REQUEST_STATUS_CODES = [502, 504].freeze
1327
1346
 
1328
1347
  def _request(uri, request_object, timeout = nil, include_etag: false, retry_status_codes: [])
1329
- request_object['User-Agent'] = "posthog-ruby/#{PostHog::VERSION}"
1348
+ request_object['User-Agent'] = @user_agent
1330
1349
  request_timeout = timeout || 10
1331
1350
  backoff_policy = nil
1332
1351
  attempts = 0
@@ -144,8 +144,8 @@ module PostHog
144
144
  is_server = fields.fetch(:is_server, true) != false
145
145
 
146
146
  properties = {
147
- '$lib' => 'posthog-ruby',
148
- '$lib_version' => PostHog::VERSION.to_s
147
+ '$lib' => fields.fetch(:lib, 'posthog-ruby'),
148
+ '$lib_version' => fields.fetch(:lib_version, PostHog::VERSION.to_s)
149
149
  }
150
150
  properties['$is_server'] = true if is_server
151
151
 
@@ -45,6 +45,7 @@ module PostHog
45
45
  @pid = Process.pid
46
46
  @transport_options = {
47
47
  api_host: options[:host],
48
+ headers: options[:headers],
48
49
  skip_ssl_verification: options[:skip_ssl_verification],
49
50
  compress_request: options[:compress_request]
50
51
  }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PostHog
4
- VERSION = '3.23.5'
4
+ VERSION = '3.23.7'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: posthog-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.23.5
4
+ version: 3.23.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''