posthog-ruby 3.8.1 → 3.9.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 +4 -4
- data/lib/posthog/client.rb +40 -5
- data/lib/posthog/internal/context.rb +119 -0
- data/lib/posthog/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3f033107e265e546dca78bfbf8f6adb3552e5803ac7dec0bf822afb814e7658d
|
|
4
|
+
data.tar.gz: efe43054d73bfce034973fc12a01301f3b935cfacb1e7e86358332a9879f8911
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e1e3e4e0e5712ab73c8c0c11ce5a5869df0dc95f293b151f81d7fc715844e36867d3aeed393427494789bf8b35f2d96f42c703085d6be405fe1168feaa7daa82
|
|
7
|
+
data.tar.gz: 1347919dcaeda0b7ad237964ae00acc34d4b6e4b9273d3335ce6e4666f09471d8d8fefb53c13d51e345cf3b7c8e467b813b14cbbdad74062dbabbc12fbeadc26
|
data/lib/posthog/client.rb
CHANGED
|
@@ -14,6 +14,7 @@ require 'posthog/feature_flags'
|
|
|
14
14
|
require 'posthog/feature_flag_evaluations'
|
|
15
15
|
require 'posthog/send_feature_flags_options'
|
|
16
16
|
require 'posthog/exception_capture'
|
|
17
|
+
require 'posthog/internal/context'
|
|
17
18
|
|
|
18
19
|
module PostHog
|
|
19
20
|
class Client
|
|
@@ -185,9 +186,13 @@ module PostHog
|
|
|
185
186
|
# events in PostHog are deduplicated by the
|
|
186
187
|
# combination of teamId, timestamp date,
|
|
187
188
|
# event name, distinct id, and UUID
|
|
189
|
+
# @note If `:distinct_id` is omitted, request/context distinct_id is used when
|
|
190
|
+
# available; otherwise a UUID is generated and the event is marked personless
|
|
191
|
+
# with `$process_person_profile: false`.
|
|
188
192
|
# @macro common_attrs
|
|
189
193
|
def capture(attrs)
|
|
190
194
|
symbolize_keys! attrs
|
|
195
|
+
enrich_capture_attrs_with_context(attrs)
|
|
191
196
|
|
|
192
197
|
# Precedence: an explicit `flags` snapshot always wins, regardless of
|
|
193
198
|
# `send_feature_flags`. The snapshot guarantees the event carries the same
|
|
@@ -260,7 +265,8 @@ module PostHog
|
|
|
260
265
|
# Captures an exception as an event
|
|
261
266
|
#
|
|
262
267
|
# @param [Exception, String, Object] exception The exception to capture, a string message, or exception-like object
|
|
263
|
-
# @param [String] distinct_id The ID for the user (optional, defaults to
|
|
268
|
+
# @param [String] distinct_id The ID for the user (optional, defaults to request/context distinct_id
|
|
269
|
+
# or a generated UUID)
|
|
264
270
|
# @param [Hash] additional_properties Additional properties to include with the exception event (optional)
|
|
265
271
|
# @param [PostHog::FeatureFlagEvaluations] flags A snapshot returned by {#evaluate_flags}.
|
|
266
272
|
# Forwarded to the inner {#capture} call so the captured `$exception` event carries the
|
|
@@ -270,12 +276,8 @@ module PostHog
|
|
|
270
276
|
|
|
271
277
|
return if exception_info.nil?
|
|
272
278
|
|
|
273
|
-
no_distinct_id_was_provided = distinct_id.nil?
|
|
274
|
-
distinct_id ||= SecureRandom.uuid
|
|
275
|
-
|
|
276
279
|
properties = { '$exception_list' => [exception_info] }
|
|
277
280
|
properties.merge!(additional_properties) if additional_properties && !additional_properties.empty?
|
|
278
|
-
properties['$process_person_profile'] = false if no_distinct_id_was_provided
|
|
279
281
|
|
|
280
282
|
event_data = {
|
|
281
283
|
distinct_id: distinct_id,
|
|
@@ -685,6 +687,39 @@ module PostHog
|
|
|
685
687
|
|
|
686
688
|
private
|
|
687
689
|
|
|
690
|
+
def enrich_capture_attrs_with_context(attrs)
|
|
691
|
+
context = Internal::Context.current
|
|
692
|
+
explicit_properties = attrs[:properties]
|
|
693
|
+
properties_are_hash = explicit_properties.nil? || explicit_properties.is_a?(Hash)
|
|
694
|
+
context_properties = context&.properties || {}
|
|
695
|
+
if properties_are_hash
|
|
696
|
+
attrs[:properties] = Internal::Context.merge_properties(context_properties, explicit_properties || {})
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
return if present_id?(attrs[:distinct_id])
|
|
700
|
+
|
|
701
|
+
if present_id?(context&.distinct_id)
|
|
702
|
+
attrs[:distinct_id] = context.distinct_id
|
|
703
|
+
return
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
attrs[:distinct_id] = SecureRandom.uuid
|
|
707
|
+
return unless properties_are_hash
|
|
708
|
+
return if property_key?(explicit_properties, '$process_person_profile')
|
|
709
|
+
|
|
710
|
+
attrs[:properties]['$process_person_profile'] = false
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
def present_id?(value)
|
|
714
|
+
!(value.nil? || (value.is_a?(String) && value.empty?))
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
def property_key?(properties, key)
|
|
718
|
+
return false unless properties.is_a?(Hash)
|
|
719
|
+
|
|
720
|
+
properties.key?(key) || properties.key?(key.to_sym)
|
|
721
|
+
end
|
|
722
|
+
|
|
688
723
|
# Shared by the legacy single-flag path ({#get_feature_flag_result}) and the
|
|
689
724
|
# snapshot's access-recording. Owns dedup-key construction, the
|
|
690
725
|
# per-distinct_id sent-flags cache, and the `$feature_flag_called` capture call.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PostHog
|
|
4
|
+
module Internal
|
|
5
|
+
# Internal request/fiber-local context applied to capture calls.
|
|
6
|
+
# Uses Rails' isolated execution state when available, otherwise falls back
|
|
7
|
+
# to thread-local storage in the core SDK.
|
|
8
|
+
#
|
|
9
|
+
# This is intentionally not exposed as a public SDK API in Ruby yet. It exists
|
|
10
|
+
# to let framework integrations such as posthog-rails propagate request-scoped
|
|
11
|
+
# tracing headers to regular capture and exception events without making the
|
|
12
|
+
# server-side SDK globally stateful per user.
|
|
13
|
+
class Context
|
|
14
|
+
STORAGE_KEY = :posthog_context
|
|
15
|
+
|
|
16
|
+
attr_reader :distinct_id, :session_id, :properties
|
|
17
|
+
|
|
18
|
+
def initialize(distinct_id: nil, session_id: nil, properties: {})
|
|
19
|
+
@distinct_id = distinct_id
|
|
20
|
+
@session_id = session_id
|
|
21
|
+
@properties = properties ? properties.dup : {}
|
|
22
|
+
apply_session_property!
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.current
|
|
26
|
+
if defined?(ActiveSupport::IsolatedExecutionState)
|
|
27
|
+
ActiveSupport::IsolatedExecutionState[STORAGE_KEY]
|
|
28
|
+
else
|
|
29
|
+
Thread.current[STORAGE_KEY]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.current=(context)
|
|
34
|
+
if defined?(ActiveSupport::IsolatedExecutionState)
|
|
35
|
+
ActiveSupport::IsolatedExecutionState[STORAGE_KEY] = context
|
|
36
|
+
else
|
|
37
|
+
Thread.current[STORAGE_KEY] = context
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.with_context(data = nil, fresh: false, **kwargs)
|
|
42
|
+
previous_context = current
|
|
43
|
+
raise ArgumentError, 'with_context requires a block' unless block_given?
|
|
44
|
+
|
|
45
|
+
self.current = resolve(merge_data_and_kwargs(data, kwargs), previous_context, fresh: fresh)
|
|
46
|
+
yield
|
|
47
|
+
ensure
|
|
48
|
+
self.current = previous_context
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.resolve(data, parent, fresh: false)
|
|
52
|
+
data = normalize_data(data)
|
|
53
|
+
|
|
54
|
+
parent_properties = fresh || parent.nil? ? {} : parent.properties
|
|
55
|
+
properties = merge_properties(parent_properties, data[:properties] || {})
|
|
56
|
+
if data[:session_id] && !session_property_key?(data[:properties])
|
|
57
|
+
properties.delete('$session_id')
|
|
58
|
+
properties.delete(:$session_id)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
new(
|
|
62
|
+
distinct_id: data[:distinct_id] || (fresh || parent.nil? ? nil : parent.distinct_id),
|
|
63
|
+
session_id: data[:session_id] || (fresh || parent.nil? ? nil : parent.session_id),
|
|
64
|
+
properties: properties
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
private_class_method :resolve
|
|
68
|
+
|
|
69
|
+
def self.merge_data_and_kwargs(data, kwargs)
|
|
70
|
+
data ||= {}
|
|
71
|
+
raise ArgumentError, 'context data must be a Hash' unless data.is_a?(Hash)
|
|
72
|
+
|
|
73
|
+
data.merge(kwargs)
|
|
74
|
+
end
|
|
75
|
+
private_class_method :merge_data_and_kwargs
|
|
76
|
+
|
|
77
|
+
def self.merge_properties(base, overrides)
|
|
78
|
+
merged = (base || {}).dup
|
|
79
|
+
(overrides || {}).each do |key, value|
|
|
80
|
+
merged.delete(key.to_s) if key.is_a?(Symbol)
|
|
81
|
+
merged.delete(key.to_sym) if key.is_a?(String)
|
|
82
|
+
merged[key] = value
|
|
83
|
+
end
|
|
84
|
+
merged
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def self.normalize_data(data)
|
|
88
|
+
data ||= {}
|
|
89
|
+
raise ArgumentError, 'context data must be a Hash' unless data.is_a?(Hash)
|
|
90
|
+
|
|
91
|
+
properties = data[:properties] || data['properties'] || {}
|
|
92
|
+
raise ArgumentError, 'context properties must be a Hash' unless properties.is_a?(Hash)
|
|
93
|
+
|
|
94
|
+
{
|
|
95
|
+
distinct_id: data[:distinct_id] || data['distinct_id'] || data[:distinctId] || data['distinctId'],
|
|
96
|
+
session_id: data[:session_id] || data['session_id'] || data[:sessionId] || data['sessionId'],
|
|
97
|
+
properties: properties
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
private_class_method :normalize_data
|
|
101
|
+
|
|
102
|
+
def self.session_property_key?(properties)
|
|
103
|
+
return false unless properties.is_a?(Hash)
|
|
104
|
+
|
|
105
|
+
properties.key?('$session_id') || properties.key?(:$session_id)
|
|
106
|
+
end
|
|
107
|
+
private_class_method :session_property_key?
|
|
108
|
+
|
|
109
|
+
def apply_session_property!
|
|
110
|
+
return if session_id.nil? || properties.key?('$session_id') || properties.key?(:$session_id)
|
|
111
|
+
|
|
112
|
+
properties['$session_id'] = session_id
|
|
113
|
+
end
|
|
114
|
+
private :apply_session_property!
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
private_constant :Internal
|
|
119
|
+
end
|
data/lib/posthog/version.rb
CHANGED
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.
|
|
4
|
+
version: 3.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ''
|
|
@@ -46,6 +46,7 @@ files:
|
|
|
46
46
|
- lib/posthog/feature_flags.rb
|
|
47
47
|
- lib/posthog/field_parser.rb
|
|
48
48
|
- lib/posthog/flag_definition_cache.rb
|
|
49
|
+
- lib/posthog/internal/context.rb
|
|
49
50
|
- lib/posthog/logging.rb
|
|
50
51
|
- lib/posthog/message_batch.rb
|
|
51
52
|
- lib/posthog/noop_worker.rb
|