posthog-ruby 3.5.5 → 3.6.1
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 +99 -1
- data/lib/posthog/feature_flags.rb +83 -11
- data/lib/posthog/flag_definition_cache.rb +78 -0
- data/lib/posthog/noop_worker.rb +4 -0
- data/lib/posthog/send_worker.rb +4 -0
- data/lib/posthog/utils.rb +15 -0
- data/lib/posthog/version.rb +1 -1
- data/lib/posthog.rb +1 -0
- metadata +4 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dc278624783a997ea124cc47e7d6724c2160b10509dde00293725e66ca4edceb
|
|
4
|
+
data.tar.gz: db194b891449029e91c8f20d82b873fe8aebe69182db77053ce966366af94ea9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 294eacce50eef253e5d6890c2c972c300e8caab9462889648f89cb2449a06357343515d54935b69b83ae0707e2c77adc2010bad8efc14f7e393c8427744422b3
|
|
7
|
+
data.tar.gz: ffa9da1ee5e32b462e83bc3a4f362bf855dd62931d3351dd41850909300c85921cd2e6b56851fd752f165c9a429cb02b28336a69155187451b4326ca65d6798d
|
data/lib/posthog/client.rb
CHANGED
|
@@ -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,11 @@ 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+.
|
|
71
|
+
# @option opts [Object] :flag_definition_cache_provider An object implementing the
|
|
72
|
+
# {FlagDefinitionCacheProvider} interface for distributed flag definition caching.
|
|
73
|
+
# EXPERIMENTAL: This API may change in future minor version bumps.
|
|
35
74
|
def initialize(opts = {})
|
|
36
75
|
symbolize_keys!(opts)
|
|
37
76
|
|
|
@@ -41,17 +80,42 @@ module PostHog
|
|
|
41
80
|
@api_key = opts[:api_key]
|
|
42
81
|
@max_queue_size = opts[:max_queue_size] || Defaults::Queue::MAX_SIZE
|
|
43
82
|
@worker_mutex = Mutex.new
|
|
83
|
+
@sync_mode = opts[:sync_mode] == true && !opts[:test_mode]
|
|
84
|
+
@on_error = opts[:on_error] || proc { |status, error| }
|
|
44
85
|
@worker = if opts[:test_mode]
|
|
45
86
|
NoopWorker.new(@queue)
|
|
87
|
+
elsif @sync_mode
|
|
88
|
+
nil
|
|
46
89
|
else
|
|
47
90
|
SendWorker.new(@queue, @api_key, opts)
|
|
48
91
|
end
|
|
92
|
+
if @sync_mode
|
|
93
|
+
@transport = Transport.new(
|
|
94
|
+
api_host: opts[:host],
|
|
95
|
+
skip_ssl_verification: opts[:skip_ssl_verification],
|
|
96
|
+
retries: 3
|
|
97
|
+
)
|
|
98
|
+
@sync_lock = Mutex.new
|
|
99
|
+
end
|
|
49
100
|
@worker_thread = nil
|
|
50
101
|
@feature_flags_poller = nil
|
|
51
102
|
@personal_api_key = opts[:personal_api_key]
|
|
52
103
|
|
|
53
104
|
check_api_key!
|
|
54
105
|
|
|
106
|
+
# Warn when multiple clients are created with the same API key (can cause dropped events)
|
|
107
|
+
unless opts[:test_mode] || opts[:disable_singleton_warning]
|
|
108
|
+
previous_count = self.class._increment_instance_count(@api_key)
|
|
109
|
+
if previous_count >= 1
|
|
110
|
+
logger.warn(
|
|
111
|
+
'Multiple PostHog client instances detected for the same API key. ' \
|
|
112
|
+
'This can cause dropped events and inconsistent behavior. ' \
|
|
113
|
+
'Use a singleton pattern: instantiate once and reuse the client. ' \
|
|
114
|
+
'See https://posthog.com/docs/libraries/ruby'
|
|
115
|
+
)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
55
119
|
@feature_flags_poller =
|
|
56
120
|
FeatureFlagsPoller.new(
|
|
57
121
|
opts[:feature_flags_polling_interval],
|
|
@@ -59,7 +123,8 @@ module PostHog
|
|
|
59
123
|
@api_key,
|
|
60
124
|
opts[:host],
|
|
61
125
|
opts[:feature_flag_request_timeout_seconds] || Defaults::FeatureFlags::FLAG_REQUEST_TIMEOUT_SECONDS,
|
|
62
|
-
opts[:on_error]
|
|
126
|
+
opts[:on_error],
|
|
127
|
+
flag_definition_cache_provider: opts[:flag_definition_cache_provider]
|
|
63
128
|
)
|
|
64
129
|
|
|
65
130
|
@distinct_id_has_sent_flag_calls = SizeLimitedHash.new(Defaults::MAX_HASH_SIZE) do |hash, key|
|
|
@@ -74,6 +139,12 @@ module PostHog
|
|
|
74
139
|
# Use only for scripts which are not long-running, and will specifically
|
|
75
140
|
# exit
|
|
76
141
|
def flush
|
|
142
|
+
if @sync_mode
|
|
143
|
+
# Wait for any in-flight sync send to complete
|
|
144
|
+
@sync_lock.synchronize {} # rubocop:disable Lint/EmptyBlock
|
|
145
|
+
return
|
|
146
|
+
end
|
|
147
|
+
|
|
77
148
|
while !@queue.empty? || @worker.is_requesting?
|
|
78
149
|
ensure_worker_running
|
|
79
150
|
sleep(0.1)
|
|
@@ -444,8 +515,14 @@ module PostHog
|
|
|
444
515
|
end
|
|
445
516
|
|
|
446
517
|
def shutdown
|
|
518
|
+
self.class._decrement_instance_count(@api_key) if @api_key
|
|
447
519
|
@feature_flags_poller.shutdown_poller
|
|
448
520
|
flush
|
|
521
|
+
if @sync_mode
|
|
522
|
+
@sync_lock.synchronize { @transport&.shutdown }
|
|
523
|
+
else
|
|
524
|
+
@worker&.shutdown
|
|
525
|
+
end
|
|
449
526
|
end
|
|
450
527
|
|
|
451
528
|
private
|
|
@@ -483,6 +560,11 @@ module PostHog
|
|
|
483
560
|
# add our request id for tracing purposes
|
|
484
561
|
action[:messageId] ||= uid
|
|
485
562
|
|
|
563
|
+
if @sync_mode
|
|
564
|
+
send_sync(action)
|
|
565
|
+
return true
|
|
566
|
+
end
|
|
567
|
+
|
|
486
568
|
if @queue.length < @max_queue_size
|
|
487
569
|
@queue << action
|
|
488
570
|
ensure_worker_running
|
|
@@ -513,6 +595,22 @@ module PostHog
|
|
|
513
595
|
end
|
|
514
596
|
end
|
|
515
597
|
|
|
598
|
+
def send_sync(action)
|
|
599
|
+
batch = MessageBatch.new(1)
|
|
600
|
+
begin
|
|
601
|
+
batch << action
|
|
602
|
+
rescue MessageBatch::JSONGenerationError => e
|
|
603
|
+
@on_error.call(-1, e.to_s)
|
|
604
|
+
return
|
|
605
|
+
end
|
|
606
|
+
return if batch.empty?
|
|
607
|
+
|
|
608
|
+
@sync_lock.synchronize do
|
|
609
|
+
res = @transport.send(@api_key, batch)
|
|
610
|
+
@on_error.call(res.status, res.error) unless res.status == 200
|
|
611
|
+
end
|
|
612
|
+
end
|
|
613
|
+
|
|
516
614
|
def worker_running?
|
|
517
615
|
@worker_thread&.alive?
|
|
518
616
|
end
|
|
@@ -6,6 +6,7 @@ require 'json'
|
|
|
6
6
|
require 'posthog/version'
|
|
7
7
|
require 'posthog/logging'
|
|
8
8
|
require 'posthog/feature_flag'
|
|
9
|
+
require 'posthog/flag_definition_cache'
|
|
9
10
|
require 'digest'
|
|
10
11
|
|
|
11
12
|
module PostHog
|
|
@@ -29,7 +30,8 @@ module PostHog
|
|
|
29
30
|
project_api_key,
|
|
30
31
|
host,
|
|
31
32
|
feature_flag_request_timeout_seconds,
|
|
32
|
-
on_error = nil
|
|
33
|
+
on_error = nil,
|
|
34
|
+
flag_definition_cache_provider: nil
|
|
33
35
|
)
|
|
34
36
|
@polling_interval = polling_interval || 30
|
|
35
37
|
@personal_api_key = personal_api_key
|
|
@@ -44,6 +46,10 @@ module PostHog
|
|
|
44
46
|
@on_error = on_error || proc { |status, error| }
|
|
45
47
|
@quota_limited = Concurrent::AtomicBoolean.new(false)
|
|
46
48
|
@flags_etag = Concurrent::AtomicReference.new(nil)
|
|
49
|
+
|
|
50
|
+
@flag_definition_cache_provider = flag_definition_cache_provider
|
|
51
|
+
FlagDefinitionCacheProvider.validate!(@flag_definition_cache_provider) if @flag_definition_cache_provider
|
|
52
|
+
|
|
47
53
|
@task =
|
|
48
54
|
Concurrent::TimerTask.new(
|
|
49
55
|
execution_interval: polling_interval
|
|
@@ -372,6 +378,13 @@ module PostHog
|
|
|
372
378
|
|
|
373
379
|
def shutdown_poller
|
|
374
380
|
@task.shutdown
|
|
381
|
+
return unless @flag_definition_cache_provider
|
|
382
|
+
|
|
383
|
+
begin
|
|
384
|
+
@flag_definition_cache_provider.shutdown
|
|
385
|
+
rescue StandardError => e
|
|
386
|
+
logger.error("[FEATURE FLAGS] Cache provider shutdown error: #{e}")
|
|
387
|
+
end
|
|
375
388
|
end
|
|
376
389
|
|
|
377
390
|
# Class methods
|
|
@@ -1006,6 +1019,38 @@ module PostHog
|
|
|
1006
1019
|
end
|
|
1007
1020
|
|
|
1008
1021
|
def _load_feature_flags
|
|
1022
|
+
should_fetch = true
|
|
1023
|
+
|
|
1024
|
+
if @flag_definition_cache_provider
|
|
1025
|
+
begin
|
|
1026
|
+
should_fetch = @flag_definition_cache_provider.should_fetch_flag_definitions?
|
|
1027
|
+
rescue StandardError => e
|
|
1028
|
+
logger.error("[FEATURE FLAGS] Cache provider should_fetch error: #{e}")
|
|
1029
|
+
should_fetch = true
|
|
1030
|
+
end
|
|
1031
|
+
end
|
|
1032
|
+
|
|
1033
|
+
unless should_fetch
|
|
1034
|
+
begin
|
|
1035
|
+
cached_data = @flag_definition_cache_provider.flag_definitions
|
|
1036
|
+
if cached_data
|
|
1037
|
+
logger.debug '[FEATURE FLAGS] Using cached flag definitions from external cache'
|
|
1038
|
+
_apply_flag_definitions(cached_data)
|
|
1039
|
+
return
|
|
1040
|
+
elsif @feature_flags.empty?
|
|
1041
|
+
# Emergency fallback: cache empty and no flags loaded -> fetch from API
|
|
1042
|
+
should_fetch = true
|
|
1043
|
+
end
|
|
1044
|
+
rescue StandardError => e
|
|
1045
|
+
logger.error("[FEATURE FLAGS] Cache provider get error: #{e}")
|
|
1046
|
+
should_fetch = true
|
|
1047
|
+
end
|
|
1048
|
+
end
|
|
1049
|
+
|
|
1050
|
+
_fetch_and_apply_flag_definitions if should_fetch
|
|
1051
|
+
end
|
|
1052
|
+
|
|
1053
|
+
def _fetch_and_apply_flag_definitions
|
|
1009
1054
|
begin
|
|
1010
1055
|
res = _request_feature_flag_definitions(etag: @flags_etag.value)
|
|
1011
1056
|
rescue StandardError => e
|
|
@@ -1040,21 +1085,48 @@ module PostHog
|
|
|
1040
1085
|
# Only update ETag on successful responses with flag data
|
|
1041
1086
|
@flags_etag.value = res[:etag]
|
|
1042
1087
|
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
@feature_flags.each do |flag|
|
|
1046
|
-
@feature_flags_by_key[flag[:key]] = flag unless flag[:key].nil?
|
|
1047
|
-
end
|
|
1048
|
-
@group_type_mapping = res[:group_type_mapping] || {}
|
|
1049
|
-
@cohorts = res[:cohorts] || {}
|
|
1050
|
-
|
|
1051
|
-
logger.debug "Loaded #{@feature_flags.length} feature flags and #{@cohorts.length} cohorts"
|
|
1052
|
-
@loaded_flags_successfully_once.make_true if @loaded_flags_successfully_once.false?
|
|
1088
|
+
_apply_flag_definitions(res)
|
|
1089
|
+
_store_in_cache_provider
|
|
1053
1090
|
else
|
|
1054
1091
|
logger.debug "Failed to load feature flags: #{res}"
|
|
1055
1092
|
end
|
|
1056
1093
|
end
|
|
1057
1094
|
|
|
1095
|
+
def _store_in_cache_provider
|
|
1096
|
+
return unless @flag_definition_cache_provider
|
|
1097
|
+
|
|
1098
|
+
begin
|
|
1099
|
+
data = {
|
|
1100
|
+
flags: @feature_flags.to_a,
|
|
1101
|
+
group_type_mapping: @group_type_mapping.to_h,
|
|
1102
|
+
cohorts: @cohorts.to_h
|
|
1103
|
+
}
|
|
1104
|
+
@flag_definition_cache_provider.on_flag_definitions_received(data)
|
|
1105
|
+
rescue StandardError => e
|
|
1106
|
+
logger.error("[FEATURE FLAGS] Cache provider store error: #{e}")
|
|
1107
|
+
end
|
|
1108
|
+
end
|
|
1109
|
+
|
|
1110
|
+
def _apply_flag_definitions(data)
|
|
1111
|
+
flags = get_by_symbol_or_string_key(data, 'flags') || []
|
|
1112
|
+
group_type_mapping = get_by_symbol_or_string_key(data, 'group_type_mapping') || {}
|
|
1113
|
+
cohorts = get_by_symbol_or_string_key(data, 'cohorts') || {}
|
|
1114
|
+
|
|
1115
|
+
@feature_flags = Concurrent::Array.new(flags.map { |f| deep_symbolize_keys(f) })
|
|
1116
|
+
|
|
1117
|
+
new_by_key = {}
|
|
1118
|
+
@feature_flags.each do |flag|
|
|
1119
|
+
new_by_key[flag[:key]] = flag unless flag[:key].nil?
|
|
1120
|
+
end
|
|
1121
|
+
@feature_flags_by_key = new_by_key
|
|
1122
|
+
|
|
1123
|
+
@group_type_mapping = Concurrent::Hash[deep_symbolize_keys(group_type_mapping)]
|
|
1124
|
+
@cohorts = Concurrent::Hash[deep_symbolize_keys(cohorts)]
|
|
1125
|
+
|
|
1126
|
+
logger.debug "Loaded #{@feature_flags.length} feature flags and #{@cohorts.length} cohorts"
|
|
1127
|
+
@loaded_flags_successfully_once.make_true if @loaded_flags_successfully_once.false?
|
|
1128
|
+
end
|
|
1129
|
+
|
|
1058
1130
|
def _request_feature_flag_definitions(etag: nil)
|
|
1059
1131
|
uri = URI("#{@host}/api/feature_flag/local_evaluation")
|
|
1060
1132
|
uri.query = URI.encode_www_form([['token', @project_api_key], %w[send_cohorts true]])
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PostHog
|
|
4
|
+
# Interface for external caching of feature flag definitions.
|
|
5
|
+
#
|
|
6
|
+
# EXPERIMENTAL: This API may change in future minor version bumps.
|
|
7
|
+
#
|
|
8
|
+
# Enables multi-worker environments (Kubernetes, load-balanced servers,
|
|
9
|
+
# serverless functions) to share flag definitions via an external cache,
|
|
10
|
+
# reducing redundant API calls.
|
|
11
|
+
#
|
|
12
|
+
# Implement the four required methods on any object and pass it as the
|
|
13
|
+
# +:flag_definition_cache_provider+ option when creating a {Client}.
|
|
14
|
+
#
|
|
15
|
+
# == Required Methods
|
|
16
|
+
#
|
|
17
|
+
# [+flag_definitions+]
|
|
18
|
+
# Retrieve cached flag definitions. Return a Hash with +:flags+,
|
|
19
|
+
# +:group_type_mapping+, and +:cohorts+ keys, or +nil+ if the cache
|
|
20
|
+
# is empty. Returning +nil+ triggers an API fetch when no flags are
|
|
21
|
+
# loaded yet (emergency fallback).
|
|
22
|
+
#
|
|
23
|
+
# [+should_fetch_flag_definitions?+]
|
|
24
|
+
# Return +true+ if this instance should fetch new definitions from the
|
|
25
|
+
# API, +false+ to read from cache instead. Use for distributed lock
|
|
26
|
+
# coordination so only one worker fetches at a time.
|
|
27
|
+
#
|
|
28
|
+
# [+on_flag_definitions_received(data)+]
|
|
29
|
+
# Called after successfully fetching new definitions from the API.
|
|
30
|
+
# +data+ is a Hash with +:flags+, +:group_type_mapping+, and +:cohorts+
|
|
31
|
+
# keys (plain Ruby types, not Concurrent:: wrappers). Store it in your
|
|
32
|
+
# external cache.
|
|
33
|
+
#
|
|
34
|
+
# [+shutdown+]
|
|
35
|
+
# Called when the PostHog client shuts down. Release any distributed
|
|
36
|
+
# locks and clean up resources.
|
|
37
|
+
#
|
|
38
|
+
# == Error Handling
|
|
39
|
+
#
|
|
40
|
+
# All methods are wrapped in +begin/rescue+. Errors are logged but never
|
|
41
|
+
# break flag evaluation:
|
|
42
|
+
# - +should_fetch_flag_definitions?+ errors default to fetching (fail-safe)
|
|
43
|
+
# - +flag_definitions+ errors fall back to API fetch
|
|
44
|
+
# - +on_flag_definitions_received+ errors are logged; flags remain in memory
|
|
45
|
+
# - +shutdown+ errors are logged; shutdown continues
|
|
46
|
+
#
|
|
47
|
+
# == Example
|
|
48
|
+
#
|
|
49
|
+
# cache = RedisFlagCache.new(redis, service_key: 'my-service')
|
|
50
|
+
# client = PostHog::Client.new(
|
|
51
|
+
# api_key: '<project_api_key>',
|
|
52
|
+
# personal_api_key: '<personal_api_key>',
|
|
53
|
+
# flag_definition_cache_provider: cache
|
|
54
|
+
# )
|
|
55
|
+
#
|
|
56
|
+
module FlagDefinitionCacheProvider
|
|
57
|
+
REQUIRED_METHODS = %i[
|
|
58
|
+
flag_definitions
|
|
59
|
+
should_fetch_flag_definitions?
|
|
60
|
+
on_flag_definitions_received
|
|
61
|
+
shutdown
|
|
62
|
+
].freeze
|
|
63
|
+
|
|
64
|
+
# Validates that +provider+ implements all required methods.
|
|
65
|
+
# Raises +ArgumentError+ listing any missing methods.
|
|
66
|
+
#
|
|
67
|
+
# @param provider [Object] the cache provider to validate
|
|
68
|
+
# @raise [ArgumentError] if any required methods are missing
|
|
69
|
+
def self.validate!(provider)
|
|
70
|
+
missing = REQUIRED_METHODS.reject { |m| provider.respond_to?(m) }
|
|
71
|
+
return if missing.empty?
|
|
72
|
+
|
|
73
|
+
raise ArgumentError,
|
|
74
|
+
"Flag definition cache provider is missing required methods: #{missing.join(', ')}. " \
|
|
75
|
+
'See PostHog::FlagDefinitionCacheProvider for the required interface.'
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
data/lib/posthog/noop_worker.rb
CHANGED
data/lib/posthog/send_worker.rb
CHANGED
data/lib/posthog/utils.rb
CHANGED
|
@@ -27,6 +27,21 @@ module PostHog
|
|
|
27
27
|
hash.transform_keys(&:to_s)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
# public: Recursively convert all keys to symbols in a Hash/Array tree
|
|
31
|
+
#
|
|
32
|
+
def deep_symbolize_keys(obj)
|
|
33
|
+
case obj
|
|
34
|
+
when Hash
|
|
35
|
+
obj.each_with_object({}) do |(key, value), result|
|
|
36
|
+
result[key.to_sym] = deep_symbolize_keys(value)
|
|
37
|
+
end
|
|
38
|
+
when Array
|
|
39
|
+
obj.map { |item| deep_symbolize_keys(item) }
|
|
40
|
+
else
|
|
41
|
+
obj
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
30
45
|
# public: Returns a new hash with all the date values in the into iso8601
|
|
31
46
|
# strings
|
|
32
47
|
#
|
data/lib/posthog/version.rb
CHANGED
data/lib/posthog.rb
CHANGED
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.
|
|
4
|
+
version: 3.6.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ''
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: concurrent-ruby
|
|
@@ -45,6 +44,7 @@ files:
|
|
|
45
44
|
- lib/posthog/feature_flag_result.rb
|
|
46
45
|
- lib/posthog/feature_flags.rb
|
|
47
46
|
- lib/posthog/field_parser.rb
|
|
47
|
+
- lib/posthog/flag_definition_cache.rb
|
|
48
48
|
- lib/posthog/logging.rb
|
|
49
49
|
- lib/posthog/message_batch.rb
|
|
50
50
|
- lib/posthog/noop_worker.rb
|
|
@@ -59,7 +59,6 @@ licenses:
|
|
|
59
59
|
- MIT
|
|
60
60
|
metadata:
|
|
61
61
|
rubygems_mfa_required: 'true'
|
|
62
|
-
post_install_message:
|
|
63
62
|
rdoc_options: []
|
|
64
63
|
require_paths:
|
|
65
64
|
- lib
|
|
@@ -74,8 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
74
73
|
- !ruby/object:Gem::Version
|
|
75
74
|
version: '0'
|
|
76
75
|
requirements: []
|
|
77
|
-
rubygems_version:
|
|
78
|
-
signing_key:
|
|
76
|
+
rubygems_version: 4.0.6
|
|
79
77
|
specification_version: 4
|
|
80
78
|
summary: PostHog library
|
|
81
79
|
test_files: []
|