posthog-rails 3.5.0 → 3.5.2

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.
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PostHog
4
+ module Rails
5
+ class Railtie < ::Rails::Railtie
6
+ # Add PostHog module methods for accessing Rails-specific client
7
+ initializer 'posthog.set_configs' do |_app|
8
+ PostHog.class_eval do
9
+ class << self
10
+ attr_accessor :client
11
+
12
+ # Methods explicitly delegated to the client
13
+ DELEGATED_METHODS = %i[
14
+ capture
15
+ capture_exception
16
+ identify
17
+ alias
18
+ group_identify
19
+ is_feature_enabled
20
+ get_feature_flag
21
+ get_all_flags
22
+ ].freeze
23
+
24
+ # Initialize PostHog client with a block configuration
25
+ def init(options = {})
26
+ # If block given, yield to configuration
27
+ if block_given?
28
+ config = PostHog::Rails::InitConfig.new(options)
29
+ yield config
30
+ options = config.to_client_options
31
+ end
32
+
33
+ # Create the PostHog client
34
+ @client = PostHog::Client.new(options)
35
+ end
36
+
37
+ # Define delegated methods using metaprogramming
38
+ DELEGATED_METHODS.each do |method_name|
39
+ define_method(method_name) do |*args, **kwargs, &block|
40
+ ensure_initialized!
41
+ client.public_send(method_name, *args, **kwargs, &block)
42
+ end
43
+ end
44
+
45
+ def initialized?
46
+ !@client.nil?
47
+ end
48
+
49
+ # Fallback for any client methods not explicitly defined
50
+ # rubocop:disable Lint/RedundantSafeNavigation
51
+ def method_missing(method_name, ...)
52
+ if client&.respond_to?(method_name)
53
+ ensure_initialized!
54
+ client.public_send(method_name, ...)
55
+ else
56
+ super
57
+ end
58
+ end
59
+
60
+ def respond_to_missing?(method_name, include_private = false)
61
+ client&.respond_to?(method_name) || super
62
+ end
63
+ # rubocop:enable Lint/RedundantSafeNavigation
64
+
65
+ private
66
+
67
+ def ensure_initialized!
68
+ return if initialized?
69
+
70
+ raise 'PostHog is not initialized. Call PostHog.init in an initializer.'
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ # Insert middleware for exception capturing
77
+ initializer 'posthog.insert_middlewares' do |app|
78
+ # Insert after DebugExceptions to catch rescued exceptions
79
+ insert_middleware_after(
80
+ app, ActionDispatch::DebugExceptions,
81
+ PostHog::Rails::RescuedExceptionInterceptor
82
+ )
83
+
84
+ # Insert after ShowExceptions to capture all exceptions
85
+ insert_middleware_after(
86
+ app, ActionDispatch::ShowExceptions,
87
+ PostHog::Rails::CaptureExceptions
88
+ )
89
+ end
90
+
91
+ # After initialization, set up remaining integrations
92
+ config.after_initialize do |_app|
93
+ # Hook into ActiveJob only if enabled
94
+ if PostHog::Rails.config&.auto_instrument_active_job
95
+ ActiveSupport.on_load(:active_job) do
96
+ prepend PostHog::Rails::ActiveJobExtensions
97
+ end
98
+ end
99
+
100
+ next unless PostHog.initialized?
101
+
102
+ # Register with Rails error reporter (Rails 7.0+)
103
+ register_error_subscriber if rails_version_above_7?
104
+ end
105
+
106
+ # Ensure PostHog shuts down gracefully (register only once)
107
+ config.after_initialize do
108
+ next if @posthog_at_exit_registered
109
+
110
+ @posthog_at_exit_registered = true
111
+ at_exit { PostHog.client&.shutdown if PostHog.initialized? }
112
+ end
113
+
114
+ def self.insert_middleware_after(app, target, middleware)
115
+ if app.config.middleware.include?(target)
116
+ app.config.middleware.insert_after(target, middleware)
117
+ else
118
+ # Fallback: append to stack if target middleware is missing (e.g., API-only apps)
119
+ app.config.middleware.use(middleware)
120
+ end
121
+ end
122
+
123
+ def self.register_error_subscriber
124
+ return unless PostHog::Rails.config&.auto_capture_exceptions
125
+
126
+ subscriber = PostHog::Rails::ErrorSubscriber.new
127
+ ::Rails.error.subscribe(subscriber)
128
+ rescue StandardError => e
129
+ PostHog::Logging.logger.warn("Failed to register error subscriber: #{e.message}")
130
+ PostHog::Logging.logger.warn("Backtrace: #{e.backtrace&.first(5)&.join("\n")}")
131
+ end
132
+
133
+ def self.rails_version_above_7?
134
+ ::Rails.version.to_f >= 7.0
135
+ end
136
+ end
137
+
138
+ # Configuration wrapper for the init block
139
+ class InitConfig
140
+ def initialize(base_options = {})
141
+ @base_options = base_options
142
+ end
143
+
144
+ # Core PostHog options
145
+ def api_key=(value)
146
+ @base_options[:api_key] = value
147
+ end
148
+
149
+ def personal_api_key=(value)
150
+ @base_options[:personal_api_key] = value
151
+ end
152
+
153
+ def host=(value)
154
+ @base_options[:host] = value
155
+ end
156
+
157
+ def max_queue_size=(value)
158
+ @base_options[:max_queue_size] = value
159
+ end
160
+
161
+ def test_mode=(value)
162
+ @base_options[:test_mode] = value
163
+ end
164
+
165
+ def on_error=(value)
166
+ @base_options[:on_error] = value
167
+ end
168
+
169
+ def feature_flags_polling_interval=(value)
170
+ @base_options[:feature_flags_polling_interval] = value
171
+ end
172
+
173
+ def feature_flag_request_timeout_seconds=(value)
174
+ @base_options[:feature_flag_request_timeout_seconds] = value
175
+ end
176
+
177
+ def before_send=(value)
178
+ @base_options[:before_send] = value
179
+ end
180
+
181
+ def to_client_options
182
+ @base_options
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PostHog
4
+ module Rails
5
+ # Middleware that intercepts exceptions that are rescued by Rails
6
+ # This middleware runs before ShowExceptions and captures the exception
7
+ # so we can report it even if Rails rescues it
8
+ class RescuedExceptionInterceptor
9
+ def initialize(app)
10
+ @app = app
11
+ end
12
+
13
+ def call(env)
14
+ @app.call(env)
15
+ rescue StandardError => e
16
+ # Store the exception so CaptureExceptions middleware can report it
17
+ env['posthog.rescued_exception'] = e if should_intercept?
18
+ raise e
19
+ end
20
+
21
+ private
22
+
23
+ def should_intercept?
24
+ PostHog::Rails.config&.report_rescued_exceptions
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'posthog/rails/configuration'
4
+ require 'posthog/rails/capture_exceptions'
5
+ require 'posthog/rails/rescued_exception_interceptor'
6
+ require 'posthog/rails/active_job'
7
+ require 'posthog/rails/error_subscriber'
8
+ require 'posthog/rails/railtie'
9
+
10
+ module PostHog
11
+ module Rails
12
+ VERSION = PostHog::VERSION
13
+
14
+ # Thread-local key for tracking web request context
15
+ IN_WEB_REQUEST_KEY = :posthog_in_web_request
16
+
17
+ class << self
18
+ def config
19
+ @config ||= Configuration.new
20
+ end
21
+
22
+ attr_writer :config
23
+
24
+ def configure
25
+ yield config if block_given?
26
+ end
27
+
28
+ # Mark that we're in a web request context
29
+ # CaptureExceptions middleware will handle exception capture
30
+ def enter_web_request
31
+ Thread.current[IN_WEB_REQUEST_KEY] = true
32
+ end
33
+
34
+ # Clear web request context (called at end of request)
35
+ def exit_web_request
36
+ Thread.current[IN_WEB_REQUEST_KEY] = false
37
+ end
38
+
39
+ # Check if we're currently in a web request context
40
+ # Used by ErrorSubscriber to avoid duplicate captures
41
+ def in_web_request?
42
+ Thread.current[IN_WEB_REQUEST_KEY] == true
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ unless defined?(Rails)
4
+ raise LoadError, 'posthog-rails requires Rails. Use the posthog-ruby gem directly for non-Rails applications.'
5
+ end
6
+
7
+ # Load core PostHog Ruby SDK
8
+ require 'posthog'
9
+
10
+ # Load Rails integration
11
+ require 'posthog/rails'
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: posthog-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.5.0
4
+ version: 3.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - PostHog
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-02-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: railties
@@ -45,31 +44,21 @@ executables: []
45
44
  extensions: []
46
45
  extra_rdoc_files: []
47
46
  files:
48
- - lib/posthog.rb
49
- - lib/posthog/backoff_policy.rb
50
- - lib/posthog/client.rb
51
- - lib/posthog/defaults.rb
52
- - lib/posthog/exception_capture.rb
53
- - lib/posthog/feature_flag.rb
54
- - lib/posthog/feature_flag_error.rb
55
- - lib/posthog/feature_flag_result.rb
56
- - lib/posthog/feature_flags.rb
57
- - lib/posthog/field_parser.rb
58
- - lib/posthog/logging.rb
59
- - lib/posthog/message_batch.rb
60
- - lib/posthog/noop_worker.rb
61
- - lib/posthog/response.rb
62
- - lib/posthog/send_feature_flags_options.rb
63
- - lib/posthog/send_worker.rb
64
- - lib/posthog/transport.rb
65
- - lib/posthog/utils.rb
66
- - lib/posthog/version.rb
47
+ - lib/generators/posthog/install_generator.rb
48
+ - lib/posthog-rails.rb
49
+ - lib/posthog/rails.rb
50
+ - lib/posthog/rails/active_job.rb
51
+ - lib/posthog/rails/capture_exceptions.rb
52
+ - lib/posthog/rails/configuration.rb
53
+ - lib/posthog/rails/error_subscriber.rb
54
+ - lib/posthog/rails/parameter_filter.rb
55
+ - lib/posthog/rails/railtie.rb
56
+ - lib/posthog/rails/rescued_exception_interceptor.rb
67
57
  homepage: https://github.com/PostHog/posthog-ruby
68
58
  licenses:
69
59
  - MIT
70
60
  metadata:
71
61
  rubygems_mfa_required: 'true'
72
- post_install_message:
73
62
  rdoc_options: []
74
63
  require_paths:
75
64
  - lib
@@ -84,8 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
73
  - !ruby/object:Gem::Version
85
74
  version: '0'
86
75
  requirements: []
87
- rubygems_version: 3.5.10
88
- signing_key:
76
+ rubygems_version: 4.0.3
89
77
  specification_version: 4
90
78
  summary: PostHog integration for Rails
91
79
  test_files: []
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'posthog/defaults'
4
-
5
- module PostHog
6
- class BackoffPolicy
7
- include PostHog::Defaults::BackoffPolicy
8
-
9
- # @param [Hash] opts
10
- # @option opts [Numeric] :min_timeout_ms The minimum backoff timeout
11
- # @option opts [Numeric] :max_timeout_ms The maximum backoff timeout
12
- # @option opts [Numeric] :multiplier The value to multiply the current
13
- # interval with for each retry attempt
14
- # @option opts [Numeric] :randomization_factor The randomization factor
15
- # to use to create a range around the retry interval
16
- def initialize(opts = {})
17
- @min_timeout_ms = opts[:min_timeout_ms] || MIN_TIMEOUT_MS
18
- @max_timeout_ms = opts[:max_timeout_ms] || MAX_TIMEOUT_MS
19
- @multiplier = opts[:multiplier] || MULTIPLIER
20
- @randomization_factor =
21
- opts[:randomization_factor] || RANDOMIZATION_FACTOR
22
-
23
- @attempts = 0
24
- end
25
-
26
- # @return [Numeric] the next backoff interval, in milliseconds.
27
- def next_interval
28
- interval = @min_timeout_ms * (@multiplier**@attempts)
29
- interval = add_jitter(interval, @randomization_factor)
30
-
31
- @attempts += 1
32
-
33
- [interval, @max_timeout_ms].min
34
- end
35
-
36
- private
37
-
38
- def add_jitter(base, randomization_factor)
39
- random_number = rand
40
- max_deviation = base * randomization_factor
41
- deviation = random_number * max_deviation
42
-
43
- random_number < 0.5 ? base - deviation : base + deviation
44
- end
45
- end
46
- end