Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/safe-otters-noop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'posthog-ruby': patch
---

No-op when the SDK is disabled or the Rails facade is used before initialization.
12 changes: 11 additions & 1 deletion lib/posthog/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ def initialize(opts = {})
@feature_flags_poller = nil
@personal_api_key = opts[:personal_api_key]

logger.error('api_key is missing or empty after trimming whitespace; check your project API key') if @disabled
if @disabled && !opts[:silence_disabled_client_error]
logger.error('api_key is missing or empty after trimming whitespace; check your project API key')
end

# Warn when multiple clients are created with the same API key (can cause dropped events)
unless @disabled || opts[:test_mode] || opts[:disable_singleton_warning]
Expand Down Expand Up @@ -284,6 +286,8 @@ def capture(attrs)
# same `$feature/<key>` and `$active_feature_flags` properties as the snapshot.
# @return [Boolean, nil] Whether the exception event was queued or sent, or nil if the input could not be parsed.
def capture_exception(exception, distinct_id = nil, additional_properties = {}, flags: nil)
return false if @disabled

exception_info = ExceptionCapture.build_parsed_exception(exception)

return if exception_info.nil?
Expand All @@ -310,6 +314,8 @@ def capture_exception(exception, distinct_id = nil, additional_properties = {},
# @return [Boolean] Whether the identify event was queued or sent.
# @macro common_attrs
def identify(attrs)
return false if @disabled

symbolize_keys! attrs
enqueue(FieldParser.parse_for_identify(attrs))
end
Expand All @@ -325,6 +331,8 @@ def identify(attrs)
# @return [Boolean] Whether the group identify event was queued or sent.
# @macro common_attrs
def group_identify(attrs)
return false if @disabled

symbolize_keys! attrs
enqueue(FieldParser.parse_for_group_identify(attrs))
end
Expand All @@ -337,6 +345,8 @@ def group_identify(attrs)
# @return [Boolean] Whether the alias event was queued or sent.
# @macro common_attrs
def alias(attrs)
return false if @disabled

symbolize_keys! attrs
enqueue(FieldParser.parse_for_alias(attrs))
end
Expand Down
12 changes: 6 additions & 6 deletions posthog-rails/lib/posthog/rails/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ def initialized?
# Fallback for any client methods not explicitly defined.
#
# @api private
# rubocop:disable Lint/RedundantSafeNavigation
def method_missing(method_name, ...)
if client&.respond_to?(method_name)
ensure_initialized!
ensure_initialized!

if client.respond_to?(method_name)
client.public_send(method_name, ...)
else
super
Expand All @@ -66,16 +66,16 @@ def method_missing(method_name, ...)

# @api private
def respond_to_missing?(method_name, include_private = false)
client&.respond_to?(method_name) || super
ensure_initialized!
client.respond_to?(method_name, include_private) || super
end
# rubocop:enable Lint/RedundantSafeNavigation

private

def ensure_initialized!
return if initialized?

raise 'PostHog is not initialized. Call PostHog.init in an initializer.'
@client = PostHog::Client.new(api_key: nil, silence_disabled_client_error: true)
end
Comment thread
marandaneto marked this conversation as resolved.
end
end
Expand Down
17 changes: 17 additions & 0 deletions spec/posthog/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,23 @@ module PostHog
.with(include('api_key is missing or empty after trimming whitespace'))
.once
end

it 'no-ops event methods before validating payloads' do
client = Client.new(api_key: api_key)
event_methods = [
[:identify, [{}]],
[:group_identify, [{}]],
[:alias, [{}]],
[:capture_exception, [StandardError.new('boom'), nil, 'not a hash']]
]

event_methods.each do |method_name, args|
aggregate_failures(method_name) do
expect { client.public_send(method_name, *args) }.not_to raise_error
expect(client.public_send(method_name, *args)).to eq(false)
end
end
end
Comment thread
marandaneto marked this conversation as resolved.
end

context 'when api_key is nil' do
Expand Down
29 changes: 29 additions & 0 deletions spec/posthog/rails/railtie_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

# Minimal requires for testing the Railtie in isolation
require 'logger'
require 'posthog'
require 'rails/railtie'

Expand All @@ -15,6 +16,34 @@
require 'posthog/rails/railtie'

RSpec.describe PostHog::Rails::Railtie do
describe 'posthog.set_configs initializer' do
before do
initializer = PostHog::Rails::Railtie.initializers.find { |i| i.name == 'posthog.set_configs' }
PostHog::Rails::Railtie.instance.instance_exec(double('app'), &initializer.block)
PostHog::Logging.logger = Logger.new(File::NULL)
PostHog.client = nil
end

after do
PostHog.client = nil
end

it 'no-ops delegated calls before explicit init without logging a missing api_key error' do
logger = instance_spy(Logger)
PostHog::Logging.logger = logger

expect { PostHog.capture(event: 'event', distinct_id: 'user') }.not_to raise_error
expect(PostHog.capture(event: 'event', distinct_id: 'user')).to eq(false)
expect(PostHog.identify(distinct_id: 'user')).to eq(false)
expect(PostHog.alias(alias: 'anon', distinct_id: 'user')).to eq(false)
expect(PostHog.group_identify(group_type: 'organization', group_key: 'id:5')).to eq(false)
expect(PostHog.get_feature_flag('flag', 'user')).to be_nil
expect(PostHog.get_all_flags('user')).to eq({})
expect(PostHog.evaluate_flags('user').keys).to eq([])
expect(logger).not_to have_received(:error)
end
end

describe 'posthog.insert_middlewares initializer' do
it 'has insert_middleware_after accessible from initializer context' do
# Rails initializer blocks are executed via instance_exec on the Railtie
Expand Down
Loading