posthog-ruby 3.5.5 → 3.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a707a12f37f1d97a91f179b0b6fa6695b6c6342957de137b38f66ebc0e008a5d
4
- data.tar.gz: ecace6615c9121841987baaed7178327e1a50db76f73b5b890f6e42da7c30656
3
+ metadata.gz: f9cb5856b81b9b86c58818eb7063f4e8ca950fb1e9f9b23d998b16a43c21f9dc
4
+ data.tar.gz: e0d5f9a42f919ec8924b096f2c0bb3f32a5052f6b09d868ccf194dab6d5f4980
5
5
  SHA512:
6
- metadata.gz: 3f584a56131c572124ab4c46b452ca96538b94c386934ee99b768de5dd84c5a34236e65edfefde9bed167fde431b311f70f08fd36786dd586b4173556029b6a2
7
- data.tar.gz: ddd803d8420443ea7ee1fb015a46dfad302a11eab98c02ef70a16820e5e8bea73a82bea644bfb6d1109b11e744739be82810ac4bdaf8375cd9cb2847d79df4eb
6
+ metadata.gz: b2736eedb29171b83370b7be1eea115d015a907f2c27ed656a4ab8c708050867464a82608b1520e84d2bf045031625ae0645191395bb9992622e24ddc0dbb5e4
7
+ data.tar.gz: 89f33534310162d51b7edd63f99f87ef41e2b803fad8cbec9930e7c382143ae63297ac273425468b91bfbfbe863a68a06a8659364ca881ba48245a907d97bd2e
@@ -8,6 +8,8 @@ require 'posthog/logging'
8
8
  require 'posthog/utils'
9
9
  require 'posthog/send_worker'
10
10
  require 'posthog/noop_worker'
11
+ require 'posthog/message_batch'
12
+ require 'posthog/transport'
11
13
  require 'posthog/feature_flags'
12
14
  require 'posthog/send_feature_flags_options'
13
15
  require 'posthog/exception_capture'
@@ -17,6 +19,35 @@ module PostHog
17
19
  include PostHog::Utils
18
20
  include PostHog::Logging
19
21
 
22
+ # Thread-safe tracking of client instances per API key for singleton warnings
23
+ @instances_by_api_key = {}
24
+ @instances_mutex = Mutex.new
25
+
26
+ class << self
27
+ # Resets instance tracking. Used primarily for testing.
28
+ # In production, instance counts persist for the lifetime of the process.
29
+ def reset_instance_tracking!
30
+ @instances_mutex.synchronize do
31
+ @instances_by_api_key = {}
32
+ end
33
+ end
34
+
35
+ def _increment_instance_count(api_key)
36
+ @instances_mutex.synchronize do
37
+ count = @instances_by_api_key[api_key] || 0
38
+ @instances_by_api_key[api_key] = count + 1
39
+ count
40
+ end
41
+ end
42
+
43
+ def _decrement_instance_count(api_key)
44
+ @instances_mutex.synchronize do
45
+ count = (@instances_by_api_key[api_key] || 1) - 1
46
+ @instances_by_api_key[api_key] = [count, 0].max
47
+ end
48
+ end
49
+ end
50
+
20
51
  # @param [Hash] opts
21
52
  # @option opts [String] :api_key Your project's api_key
22
53
  # @option opts [String] :personal_api_key Your personal API key
@@ -24,6 +55,9 @@ module PostHog
24
55
  # remain queued. Defaults to 10_000.
25
56
  # @option opts [Bool] :test_mode +true+ if messages should remain
26
57
  # queued for testing. Defaults to +false+.
58
+ # @option opts [Bool] :sync_mode +true+ to send events synchronously
59
+ # on the calling thread. Useful in forking environments like Sidekiq
60
+ # and Resque. Defaults to +false+.
27
61
  # @option opts [Proc] :on_error Handles error calls from the API.
28
62
  # @option opts [String] :host Fully qualified hostname of the PostHog server. Defaults to `https://app.posthog.com`
29
63
  # @option opts [Integer] :feature_flags_polling_interval How often to poll for feature flag definition changes.
@@ -32,6 +66,8 @@ module PostHog
32
66
  # Measured in seconds, defaults to 3.
33
67
  # @option opts [Proc] :before_send A block that receives the event hash and should return either a modified hash
34
68
  # to be sent to PostHog or nil to prevent the event from being sent. e.g. `before_send: ->(event) { event }`
69
+ # @option opts [Bool] :disable_singleton_warning +true+ to suppress the warning when multiple clients
70
+ # share the same API key. Use only when you intentionally need multiple clients. Defaults to +false+.
35
71
  def initialize(opts = {})
36
72
  symbolize_keys!(opts)
37
73
 
@@ -41,17 +77,42 @@ module PostHog
41
77
  @api_key = opts[:api_key]
42
78
  @max_queue_size = opts[:max_queue_size] || Defaults::Queue::MAX_SIZE
43
79
  @worker_mutex = Mutex.new
80
+ @sync_mode = opts[:sync_mode] == true && !opts[:test_mode]
81
+ @on_error = opts[:on_error] || proc { |status, error| }
44
82
  @worker = if opts[:test_mode]
45
83
  NoopWorker.new(@queue)
84
+ elsif @sync_mode
85
+ nil
46
86
  else
47
87
  SendWorker.new(@queue, @api_key, opts)
48
88
  end
89
+ if @sync_mode
90
+ @transport = Transport.new(
91
+ api_host: opts[:host],
92
+ skip_ssl_verification: opts[:skip_ssl_verification],
93
+ retries: 3
94
+ )
95
+ @sync_lock = Mutex.new
96
+ end
49
97
  @worker_thread = nil
50
98
  @feature_flags_poller = nil
51
99
  @personal_api_key = opts[:personal_api_key]
52
100
 
53
101
  check_api_key!
54
102
 
103
+ # Warn when multiple clients are created with the same API key (can cause dropped events)
104
+ unless opts[:test_mode] || opts[:disable_singleton_warning]
105
+ previous_count = self.class._increment_instance_count(@api_key)
106
+ if previous_count >= 1
107
+ logger.warn(
108
+ 'Multiple PostHog client instances detected for the same API key. ' \
109
+ 'This can cause dropped events and inconsistent behavior. ' \
110
+ 'Use a singleton pattern: instantiate once and reuse the client. ' \
111
+ 'See https://posthog.com/docs/libraries/ruby'
112
+ )
113
+ end
114
+ end
115
+
55
116
  @feature_flags_poller =
56
117
  FeatureFlagsPoller.new(
57
118
  opts[:feature_flags_polling_interval],
@@ -74,6 +135,12 @@ module PostHog
74
135
  # Use only for scripts which are not long-running, and will specifically
75
136
  # exit
76
137
  def flush
138
+ if @sync_mode
139
+ # Wait for any in-flight sync send to complete
140
+ @sync_lock.synchronize {} # rubocop:disable Lint/EmptyBlock
141
+ return
142
+ end
143
+
77
144
  while !@queue.empty? || @worker.is_requesting?
78
145
  ensure_worker_running
79
146
  sleep(0.1)
@@ -444,8 +511,14 @@ module PostHog
444
511
  end
445
512
 
446
513
  def shutdown
514
+ self.class._decrement_instance_count(@api_key) if @api_key
447
515
  @feature_flags_poller.shutdown_poller
448
516
  flush
517
+ if @sync_mode
518
+ @sync_lock.synchronize { @transport&.shutdown }
519
+ else
520
+ @worker&.shutdown
521
+ end
449
522
  end
450
523
 
451
524
  private
@@ -483,6 +556,11 @@ module PostHog
483
556
  # add our request id for tracing purposes
484
557
  action[:messageId] ||= uid
485
558
 
559
+ if @sync_mode
560
+ send_sync(action)
561
+ return true
562
+ end
563
+
486
564
  if @queue.length < @max_queue_size
487
565
  @queue << action
488
566
  ensure_worker_running
@@ -513,6 +591,22 @@ module PostHog
513
591
  end
514
592
  end
515
593
 
594
+ def send_sync(action)
595
+ batch = MessageBatch.new(1)
596
+ begin
597
+ batch << action
598
+ rescue MessageBatch::JSONGenerationError => e
599
+ @on_error.call(-1, e.to_s)
600
+ return
601
+ end
602
+ return if batch.empty?
603
+
604
+ @sync_lock.synchronize do
605
+ res = @transport.send(@api_key, batch)
606
+ @on_error.call(res.status, res.error) unless res.status == 200
607
+ end
608
+ end
609
+
516
610
  def worker_running?
517
611
  @worker_thread&.alive?
518
612
  end
@@ -15,5 +15,9 @@ module PostHog
15
15
  def is_requesting? # rubocop:disable Naming/PredicateName
16
16
  false
17
17
  end
18
+
19
+ def shutdown
20
+ # Does nothing
21
+ end
18
22
  end
19
23
  end
@@ -54,6 +54,10 @@ module PostHog
54
54
  @transport.shutdown
55
55
  end
56
56
 
57
+ def shutdown
58
+ @transport.shutdown
59
+ end
60
+
57
61
  # public: Check whether we have outstanding requests.
58
62
  #
59
63
  # TODO: Rename to `requesting?` in future version
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PostHog
4
- VERSION = '3.5.5'
4
+ VERSION = '3.6.0'
5
5
  end
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: 3.5.5
4
+ version: 3.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-03-05 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: concurrent-ruby
@@ -59,7 +58,6 @@ licenses:
59
58
  - MIT
60
59
  metadata:
61
60
  rubygems_mfa_required: 'true'
62
- post_install_message:
63
61
  rdoc_options: []
64
62
  require_paths:
65
63
  - lib
@@ -74,8 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
74
72
  - !ruby/object:Gem::Version
75
73
  version: '0'
76
74
  requirements: []
77
- rubygems_version: 3.5.10
78
- signing_key:
75
+ rubygems_version: 4.0.6
79
76
  specification_version: 4
80
77
  summary: PostHog library
81
78
  test_files: []