posthog-ruby 3.9.0 → 3.9.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/backoff_policy.rb +4 -1
- data/lib/posthog/client.rb +81 -44
- data/lib/posthog/exception_capture.rb +22 -0
- data/lib/posthog/feature_flag.rb +18 -3
- data/lib/posthog/feature_flag_error.rb +1 -0
- data/lib/posthog/feature_flag_evaluations.rb +41 -2
- data/lib/posthog/feature_flag_result.rb +28 -5
- data/lib/posthog/feature_flags.rb +32 -15
- data/lib/posthog/field_parser.rb +11 -0
- data/lib/posthog/flag_definition_cache.rb +10 -4
- data/lib/posthog/logging.rb +8 -1
- data/lib/posthog/message_batch.rb +8 -1
- data/lib/posthog/noop_worker.rb +7 -1
- data/lib/posthog/response.rb +5 -3
- data/lib/posthog/send_feature_flags_options.rb +17 -2
- data/lib/posthog/send_worker.rb +14 -7
- data/lib/posthog/transport.rb +18 -1
- data/lib/posthog/utils.rb +10 -0
- data/lib/posthog/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1388daa77d143c2822f15642b375b1e833a893d86677337d0d8bfc88875b364c
|
|
4
|
+
data.tar.gz: 2e71adb4fdc958bd8793515dbb3317652d9e811166678e7e38c57e57a85f3a08
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a3ab6ee23fabf6375facb994ed3a16de3ec21ec53c88da0121fe3158d9b6552127d081124bb02870ca0b04d47bd2df4a79b7a95c45d8991095e2a4cbdfc63d09
|
|
7
|
+
data.tar.gz: ae04e209088f84ccb2760f0dc5fc3760cfb25e2ecd452201cf16b5432e4761acd63ef9471fc87cf88e21a1d16ce744191ea7ed6a445b2544fcf3e3a0f2f1040a
|
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
require 'posthog/defaults'
|
|
4
4
|
|
|
5
5
|
module PostHog
|
|
6
|
+
# Retry backoff policy used by the SDK transport.
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
6
9
|
class BackoffPolicy
|
|
7
10
|
include PostHog::Defaults::BackoffPolicy
|
|
8
11
|
|
|
9
|
-
# @param [Hash]
|
|
12
|
+
# @param opts [Hash]
|
|
10
13
|
# @option opts [Numeric] :min_timeout_ms The minimum backoff timeout
|
|
11
14
|
# @option opts [Numeric] :max_timeout_ms The maximum backoff timeout
|
|
12
15
|
# @option opts [Numeric] :multiplier The value to multiply the current
|
data/lib/posthog/client.rb
CHANGED
|
@@ -50,29 +50,28 @@ module PostHog
|
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
# @param [Hash]
|
|
54
|
-
# @option opts [String] :api_key Your project's
|
|
55
|
-
# @option opts [String] :personal_api_key Your personal API key
|
|
56
|
-
# @option opts [
|
|
57
|
-
#
|
|
58
|
-
# @option opts [
|
|
59
|
-
#
|
|
60
|
-
# @option opts [
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
# @option opts [
|
|
64
|
-
#
|
|
65
|
-
# @option opts [Integer] :
|
|
66
|
-
#
|
|
67
|
-
# @option opts [
|
|
68
|
-
#
|
|
69
|
-
# @option opts [
|
|
70
|
-
#
|
|
71
|
-
# @option opts [
|
|
72
|
-
#
|
|
73
|
-
# @option opts [Object] :flag_definition_cache_provider An object implementing the
|
|
74
|
-
#
|
|
75
|
-
# EXPERIMENTAL: This API may change in future minor version bumps.
|
|
53
|
+
# @param opts [Hash] Client configuration.
|
|
54
|
+
# @option opts [String] :api_key Your project's API key. Required.
|
|
55
|
+
# @option opts [String, nil] :personal_api_key Your personal API key. Required for local feature flag evaluation.
|
|
56
|
+
# @option opts [String] :host Fully qualified hostname of the PostHog server. Defaults to `https://us.i.posthog.com`.
|
|
57
|
+
# @option opts [Integer] :max_queue_size Maximum number of calls to remain queued. Defaults to 10_000.
|
|
58
|
+
# @option opts [Integer] :batch_size Maximum number of events to send in one async batch.
|
|
59
|
+
# @option opts [Boolean] :test_mode +true+ if messages should remain queued for testing. Defaults to +false+.
|
|
60
|
+
# @option opts [Boolean] :sync_mode +true+ to send events synchronously on the calling thread. Useful in
|
|
61
|
+
# forking environments like Sidekiq and Resque. Defaults to +false+.
|
|
62
|
+
# @option opts [Proc] :on_error Callback invoked as `on_error.call(status, error)` for API or serialization errors.
|
|
63
|
+
# @option opts [Integer] :feature_flags_polling_interval How often to poll for feature flag definition changes,
|
|
64
|
+
# in seconds. Defaults to 30.
|
|
65
|
+
# @option opts [Integer] :feature_flag_request_timeout_seconds How long to wait for feature flag evaluation,
|
|
66
|
+
# in seconds. Defaults to 3.
|
|
67
|
+
# @option opts [Proc] :before_send A callback that receives the event hash and should return either a modified
|
|
68
|
+
# hash to be sent to PostHog or nil to prevent the event from being sent. e.g. `before_send: ->(event) { event }`.
|
|
69
|
+
# @option opts [Boolean] :disable_singleton_warning +true+ to suppress the warning when multiple clients share
|
|
70
|
+
# the same API key. Use only when you intentionally need multiple clients. Defaults to +false+.
|
|
71
|
+
# @option opts [Boolean] :skip_ssl_verification +true+ to disable SSL certificate verification for requests.
|
|
72
|
+
# Intended only for local development or custom deployments.
|
|
73
|
+
# @option opts [Object] :flag_definition_cache_provider An object implementing the {FlagDefinitionCacheProvider}
|
|
74
|
+
# interface for distributed flag definition caching. EXPERIMENTAL: This API may change in future minor versions.
|
|
76
75
|
def initialize(opts = {})
|
|
77
76
|
symbolize_keys!(opts)
|
|
78
77
|
|
|
@@ -143,7 +142,9 @@ module PostHog
|
|
|
143
142
|
# Synchronously waits until the worker has cleared the queue.
|
|
144
143
|
#
|
|
145
144
|
# Use only for scripts which are not long-running, and will specifically
|
|
146
|
-
# exit
|
|
145
|
+
# exit.
|
|
146
|
+
#
|
|
147
|
+
# @return [void]
|
|
147
148
|
def flush
|
|
148
149
|
if @sync_mode
|
|
149
150
|
# Wait for any in-flight sync send to complete
|
|
@@ -159,7 +160,9 @@ module PostHog
|
|
|
159
160
|
|
|
160
161
|
# Clears the queue without waiting.
|
|
161
162
|
#
|
|
162
|
-
# Use only in test mode
|
|
163
|
+
# Use only in test mode.
|
|
164
|
+
#
|
|
165
|
+
# @return [void]
|
|
163
166
|
def clear
|
|
164
167
|
@queue.clear
|
|
165
168
|
end
|
|
@@ -176,8 +179,10 @@ module PostHog
|
|
|
176
179
|
#
|
|
177
180
|
# @option attrs [String] :event Event name
|
|
178
181
|
# @option attrs [Hash] :properties Event properties (optional)
|
|
179
|
-
# @option attrs [
|
|
180
|
-
#
|
|
182
|
+
# @option attrs [Hash] :groups Group analytics mapping from group type to group key (optional)
|
|
183
|
+
# @option attrs [Boolean, Hash, SendFeatureFlagsOptions] :send_feature_flags
|
|
184
|
+
# Deprecated. Whether to send feature flags with this event, or configuration for feature flag evaluation
|
|
185
|
+
# (optional)
|
|
181
186
|
# @option attrs [PostHog::FeatureFlagEvaluations] :flags A snapshot returned by
|
|
182
187
|
# {#evaluate_flags}. When present, `$feature/<key>` and `$active_feature_flags` are
|
|
183
188
|
# attached from the snapshot without making an additional /flags request, and this
|
|
@@ -189,6 +194,7 @@ module PostHog
|
|
|
189
194
|
# @note If `:distinct_id` is omitted, request/context distinct_id is used when
|
|
190
195
|
# available; otherwise a UUID is generated and the event is marked personless
|
|
191
196
|
# with `$process_person_profile: false`.
|
|
197
|
+
# @return [Boolean] Whether the event was queued or sent.
|
|
192
198
|
# @macro common_attrs
|
|
193
199
|
def capture(attrs)
|
|
194
200
|
symbolize_keys! attrs
|
|
@@ -268,9 +274,10 @@ module PostHog
|
|
|
268
274
|
# @param [String] distinct_id The ID for the user (optional, defaults to request/context distinct_id
|
|
269
275
|
# or a generated UUID)
|
|
270
276
|
# @param [Hash] additional_properties Additional properties to include with the exception event (optional)
|
|
271
|
-
# @param [PostHog::FeatureFlagEvaluations]
|
|
277
|
+
# @param flags [PostHog::FeatureFlagEvaluations, nil] A snapshot returned by {#evaluate_flags}.
|
|
272
278
|
# Forwarded to the inner {#capture} call so the captured `$exception` event carries the
|
|
273
279
|
# same `$feature/<key>` and `$active_feature_flags` properties as the snapshot.
|
|
280
|
+
# @return [Boolean, nil] Whether the exception event was queued or sent, or nil if the input could not be parsed.
|
|
274
281
|
def capture_exception(exception, distinct_id = nil, additional_properties = {}, flags: nil)
|
|
275
282
|
exception_info = ExceptionCapture.build_parsed_exception(exception)
|
|
276
283
|
|
|
@@ -295,6 +302,7 @@ module PostHog
|
|
|
295
302
|
# @param [Hash] attrs
|
|
296
303
|
#
|
|
297
304
|
# @option attrs [Hash] :properties User properties (optional)
|
|
305
|
+
# @return [Boolean] Whether the identify event was queued or sent.
|
|
298
306
|
# @macro common_attrs
|
|
299
307
|
def identify(attrs)
|
|
300
308
|
symbolize_keys! attrs
|
|
@@ -309,6 +317,7 @@ module PostHog
|
|
|
309
317
|
# @option attrs [String] :group_key Group key
|
|
310
318
|
# @option attrs [Hash] :properties Group properties (optional)
|
|
311
319
|
# @option attrs [String] :distinct_id Distinct ID (optional)
|
|
320
|
+
# @return [Boolean] Whether the group identify event was queued or sent.
|
|
312
321
|
# @macro common_attrs
|
|
313
322
|
def group_identify(attrs)
|
|
314
323
|
symbolize_keys! attrs
|
|
@@ -320,23 +329,32 @@ module PostHog
|
|
|
320
329
|
# @param [Hash] attrs
|
|
321
330
|
#
|
|
322
331
|
# @option attrs [String] :alias The alias to give the distinct id
|
|
332
|
+
# @return [Boolean] Whether the alias event was queued or sent.
|
|
323
333
|
# @macro common_attrs
|
|
324
334
|
def alias(attrs)
|
|
325
335
|
symbolize_keys! attrs
|
|
326
336
|
enqueue(FieldParser.parse_for_alias(attrs))
|
|
327
337
|
end
|
|
328
338
|
|
|
329
|
-
# @return [Hash]
|
|
339
|
+
# @return [Hash] Pops the last message from the queue. Intended for test mode.
|
|
330
340
|
def dequeue_last_message
|
|
331
341
|
@queue.pop
|
|
332
342
|
end
|
|
333
343
|
|
|
334
|
-
# @return [
|
|
344
|
+
# @return [Integer] Number of messages in the queue. Intended for test mode.
|
|
335
345
|
def queued_messages
|
|
336
346
|
@queue.length
|
|
337
347
|
end
|
|
338
348
|
|
|
339
|
-
# @deprecated Use {#evaluate_flags} and {FeatureFlagEvaluations#
|
|
349
|
+
# @deprecated Use {#evaluate_flags} and {FeatureFlagEvaluations#enabled?} instead.
|
|
350
|
+
# @param flag_key [String, Symbol] The unique key of the feature flag.
|
|
351
|
+
# @param distinct_id [String] The distinct id of the user.
|
|
352
|
+
# @param groups [Hash] Group analytics mapping from group type to group key.
|
|
353
|
+
# @param person_properties [Hash] Properties to use when evaluating the user locally or remotely.
|
|
354
|
+
# @param group_properties [Hash] Properties to use when evaluating groups locally or remotely.
|
|
355
|
+
# @param only_evaluate_locally [Boolean] Skip the remote /flags call.
|
|
356
|
+
# @param send_feature_flag_events [Boolean] Whether to capture `$feature_flag_called` for this access.
|
|
357
|
+
# @return [Boolean, nil] Whether the flag is enabled, or nil when the flag could not be evaluated.
|
|
340
358
|
# TODO: In future version, rename to `feature_flag_enabled?`
|
|
341
359
|
def is_feature_enabled( # rubocop:disable Naming/PredicateName
|
|
342
360
|
flag_key,
|
|
@@ -366,8 +384,8 @@ module PostHog
|
|
|
366
384
|
!!response
|
|
367
385
|
end
|
|
368
386
|
|
|
369
|
-
# @param [String, Symbol]
|
|
370
|
-
# @return [
|
|
387
|
+
# @param flag_key [String, Symbol] The unique flag key of the remote config feature flag.
|
|
388
|
+
# @return [Hash] The parsed remote config payload response.
|
|
371
389
|
def get_remote_config_payload(flag_key)
|
|
372
390
|
@feature_flags_poller.get_remote_config_payload(flag_key.to_s)
|
|
373
391
|
end
|
|
@@ -379,8 +397,10 @@ module PostHog
|
|
|
379
397
|
# @param [Hash] groups
|
|
380
398
|
# @param [Hash] person_properties key-value pairs of properties to associate with the user.
|
|
381
399
|
# @param [Hash] group_properties
|
|
400
|
+
# @param only_evaluate_locally [Boolean] Skip the remote /flags call.
|
|
401
|
+
# @param send_feature_flag_events [Boolean] Whether to capture `$feature_flag_called` for this access.
|
|
382
402
|
#
|
|
383
|
-
# @return [String, nil] The value of the feature flag
|
|
403
|
+
# @return [String, Boolean, nil] The value of the feature flag
|
|
384
404
|
#
|
|
385
405
|
# The provided properties are used to calculate feature flags locally, if possible.
|
|
386
406
|
#
|
|
@@ -420,6 +440,14 @@ module PostHog
|
|
|
420
440
|
|
|
421
441
|
# @deprecated Use {#evaluate_flags} and {FeatureFlagEvaluations#get_flag} /
|
|
422
442
|
# {FeatureFlagEvaluations#get_flag_payload} instead.
|
|
443
|
+
# @param key [String, Symbol] The unique key of the feature flag.
|
|
444
|
+
# @param distinct_id [String] The distinct id of the user.
|
|
445
|
+
# @param groups [Hash] Group analytics mapping from group type to group key.
|
|
446
|
+
# @param person_properties [Hash] Properties to use when evaluating the user locally or remotely.
|
|
447
|
+
# @param group_properties [Hash] Properties to use when evaluating groups locally or remotely.
|
|
448
|
+
# @param only_evaluate_locally [Boolean] Skip the remote /flags call.
|
|
449
|
+
# @param send_feature_flag_events [Boolean] Whether to capture `$feature_flag_called` for this access.
|
|
450
|
+
# @return [PostHog::FeatureFlagResult, nil]
|
|
423
451
|
def get_feature_flag_result(
|
|
424
452
|
key,
|
|
425
453
|
distinct_id,
|
|
@@ -456,7 +484,8 @@ module PostHog
|
|
|
456
484
|
# @param [Hash] person_properties key-value pairs of properties to associate with the user
|
|
457
485
|
# @param [Hash] group_properties
|
|
458
486
|
# @param [Boolean] only_evaluate_locally Skip the remote /flags call entirely
|
|
459
|
-
# @param [Boolean] disable_geoip
|
|
487
|
+
# @param [Boolean, nil] disable_geoip When true, disables GeoIP lookup for remote evaluation and stamps captured
|
|
488
|
+
# access events.
|
|
460
489
|
# @param [Array<String, Symbol>] flag_keys When set, scopes the underlying /flags
|
|
461
490
|
# request to only these flag keys (sent as `flag_keys_to_evaluate`).
|
|
462
491
|
# Distinct from {FeatureFlagEvaluations#only}, which filters the
|
|
@@ -580,6 +609,7 @@ module PostHog
|
|
|
580
609
|
# @param [Hash] groups
|
|
581
610
|
# @param [Hash] person_properties key-value pairs of properties to associate with the user.
|
|
582
611
|
# @param [Hash] group_properties
|
|
612
|
+
# @param only_evaluate_locally [Boolean] Skip the remote /flags call.
|
|
583
613
|
#
|
|
584
614
|
# @return [Hash] String (not symbol) key value pairs of flag and their values
|
|
585
615
|
def get_all_flags(
|
|
@@ -602,11 +632,12 @@ module PostHog
|
|
|
602
632
|
#
|
|
603
633
|
# @param [String, Symbol] key The key of the feature flag
|
|
604
634
|
# @param [String] distinct_id The distinct id of the user
|
|
605
|
-
# @
|
|
606
|
-
# @
|
|
607
|
-
# @
|
|
608
|
-
# @
|
|
609
|
-
# @
|
|
635
|
+
# @param match_value [String, Boolean, nil] The value of the feature flag to be matched
|
|
636
|
+
# @param groups [Hash]
|
|
637
|
+
# @param person_properties [Hash] key-value pairs of properties to associate with the user.
|
|
638
|
+
# @param group_properties [Hash]
|
|
639
|
+
# @param only_evaluate_locally [Boolean]
|
|
640
|
+
# @return [Object, nil] The parsed payload for the matched flag value.
|
|
610
641
|
#
|
|
611
642
|
# @deprecated Use {#evaluate_flags} and {FeatureFlagEvaluations#get_flag_payload} instead.
|
|
612
643
|
def get_feature_flag_payload(
|
|
@@ -639,10 +670,10 @@ module PostHog
|
|
|
639
670
|
# featureFlagPayloads: A hash of feature flag payloads
|
|
640
671
|
#
|
|
641
672
|
# @param [String] distinct_id The distinct id of the user
|
|
642
|
-
# @
|
|
643
|
-
# @
|
|
644
|
-
# @
|
|
645
|
-
# @
|
|
673
|
+
# @param groups [Hash]
|
|
674
|
+
# @param person_properties [Hash] key-value pairs of properties to associate with the user.
|
|
675
|
+
# @param group_properties [Hash]
|
|
676
|
+
# @param only_evaluate_locally [Boolean] Skip the remote /flags call.
|
|
646
677
|
#
|
|
647
678
|
def get_all_flags_and_payloads(
|
|
648
679
|
distinct_id,
|
|
@@ -664,6 +695,9 @@ module PostHog
|
|
|
664
695
|
response
|
|
665
696
|
end
|
|
666
697
|
|
|
698
|
+
# Reload locally cached feature flag definitions.
|
|
699
|
+
#
|
|
700
|
+
# @return [void]
|
|
667
701
|
def reload_feature_flags
|
|
668
702
|
unless @personal_api_key
|
|
669
703
|
logger.error(
|
|
@@ -674,6 +708,9 @@ module PostHog
|
|
|
674
708
|
@feature_flags_poller.load_feature_flags(true)
|
|
675
709
|
end
|
|
676
710
|
|
|
711
|
+
# Flush pending events and stop background resources.
|
|
712
|
+
#
|
|
713
|
+
# @return [void]
|
|
677
714
|
def shutdown
|
|
678
715
|
self.class._decrement_instance_count(@api_key) if @api_key
|
|
679
716
|
@feature_flags_poller.shutdown_poller
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
# 💖 open source (under MIT License)
|
|
12
12
|
|
|
13
13
|
module PostHog
|
|
14
|
+
# Builds PostHog exception payloads from Ruby exception objects.
|
|
15
|
+
#
|
|
16
|
+
# @api private
|
|
14
17
|
module ExceptionCapture
|
|
15
18
|
RUBY_INPUT_FORMAT = /
|
|
16
19
|
^ \s* (?: [a-zA-Z]: | uri:classloader: )? ([^:]+ | <.*>):
|
|
@@ -18,6 +21,8 @@ module PostHog
|
|
|
18
21
|
(?: :in\s('|`)(?:([\w:]+)\#)?([^']+)')?$
|
|
19
22
|
/x
|
|
20
23
|
|
|
24
|
+
# @param value [Exception, String, Object] Exception input to parse.
|
|
25
|
+
# @return [Hash, nil] Parsed exception payload, or nil when the input is unsupported.
|
|
21
26
|
def self.build_parsed_exception(value)
|
|
22
27
|
title, message, backtrace = coerce_exception_input(value)
|
|
23
28
|
return nil if title.nil?
|
|
@@ -25,6 +30,10 @@ module PostHog
|
|
|
25
30
|
build_single_exception_from_data(title, message, backtrace)
|
|
26
31
|
end
|
|
27
32
|
|
|
33
|
+
# @param title [String]
|
|
34
|
+
# @param message [String, nil]
|
|
35
|
+
# @param backtrace [Array<String>, nil]
|
|
36
|
+
# @return [Hash]
|
|
28
37
|
def self.build_single_exception_from_data(title, message, backtrace)
|
|
29
38
|
{
|
|
30
39
|
'type' => title,
|
|
@@ -37,6 +46,8 @@ module PostHog
|
|
|
37
46
|
}
|
|
38
47
|
end
|
|
39
48
|
|
|
49
|
+
# @param backtrace [Array<String>, nil]
|
|
50
|
+
# @return [Hash, nil]
|
|
40
51
|
def self.build_stacktrace(backtrace)
|
|
41
52
|
return nil unless backtrace && !backtrace.empty?
|
|
42
53
|
|
|
@@ -50,6 +61,8 @@ module PostHog
|
|
|
50
61
|
}
|
|
51
62
|
end
|
|
52
63
|
|
|
64
|
+
# @param line [String]
|
|
65
|
+
# @return [Hash, nil]
|
|
53
66
|
def self.parse_backtrace_line(line)
|
|
54
67
|
match = line.match(RUBY_INPUT_FORMAT)
|
|
55
68
|
return nil unless match
|
|
@@ -72,6 +85,8 @@ module PostHog
|
|
|
72
85
|
frame
|
|
73
86
|
end
|
|
74
87
|
|
|
88
|
+
# @param path [String]
|
|
89
|
+
# @return [Boolean]
|
|
75
90
|
def self.gem_path?(path)
|
|
76
91
|
path.include?('/gems/') ||
|
|
77
92
|
path.include?('/ruby/') ||
|
|
@@ -79,6 +94,11 @@ module PostHog
|
|
|
79
94
|
path.include?('/.rvm/')
|
|
80
95
|
end
|
|
81
96
|
|
|
97
|
+
# @param frame [Hash]
|
|
98
|
+
# @param file_path [String]
|
|
99
|
+
# @param lineno [Integer]
|
|
100
|
+
# @param context_size [Integer]
|
|
101
|
+
# @return [void]
|
|
82
102
|
def self.add_context_lines(frame, file_path, lineno, context_size = 5)
|
|
83
103
|
lines = File.readlines(file_path)
|
|
84
104
|
return if lines.empty?
|
|
@@ -97,6 +117,8 @@ module PostHog
|
|
|
97
117
|
# Silently ignore file read errors
|
|
98
118
|
end
|
|
99
119
|
|
|
120
|
+
# @param value [Exception, String, Object]
|
|
121
|
+
# @return [Array] Three-item array of title, message, and backtrace.
|
|
100
122
|
def self.coerce_exception_input(value)
|
|
101
123
|
if value.is_a?(String)
|
|
102
124
|
title = 'Error'
|
data/lib/posthog/feature_flag.rb
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Represents a feature flag returned by /flags v2
|
|
4
3
|
module PostHog
|
|
4
|
+
# Represents a feature flag returned by /flags v2.
|
|
5
|
+
#
|
|
6
|
+
# @api private
|
|
5
7
|
class FeatureFlag
|
|
6
8
|
attr_reader :key, :enabled, :variant, :reason, :metadata, :failed
|
|
7
9
|
|
|
10
|
+
# @param json [Hash] Raw feature flag data returned by /flags.
|
|
8
11
|
def initialize(json)
|
|
9
12
|
json.transform_keys!(&:to_s)
|
|
10
13
|
@key = json['key']
|
|
@@ -15,15 +18,21 @@ module PostHog
|
|
|
15
18
|
@failed = json['failed']
|
|
16
19
|
end
|
|
17
20
|
|
|
21
|
+
# @return [String, Boolean] The variant value when present, otherwise the enabled status.
|
|
18
22
|
# TODO: Rename to `value` in future version
|
|
19
23
|
def get_value # rubocop:disable Naming/AccessorMethodName
|
|
20
24
|
@variant || @enabled
|
|
21
25
|
end
|
|
22
26
|
|
|
27
|
+
# @return [Object, nil] The flag payload from metadata.
|
|
23
28
|
def payload
|
|
24
29
|
@metadata&.payload
|
|
25
30
|
end
|
|
26
31
|
|
|
32
|
+
# @param key [String, Symbol] The feature flag key.
|
|
33
|
+
# @param value [String, Boolean] The feature flag value.
|
|
34
|
+
# @param payload [Object, nil] The feature flag payload.
|
|
35
|
+
# @return [PostHog::FeatureFlag]
|
|
27
36
|
def self.from_value_and_payload(key, value, payload)
|
|
28
37
|
new({
|
|
29
38
|
'key' => key,
|
|
@@ -40,10 +49,13 @@ module PostHog
|
|
|
40
49
|
end
|
|
41
50
|
end
|
|
42
51
|
|
|
43
|
-
# Represents the reason why a flag was enabled/disabled
|
|
52
|
+
# Represents the reason why a flag was enabled/disabled.
|
|
53
|
+
#
|
|
54
|
+
# @api private
|
|
44
55
|
class EvaluationReason
|
|
45
56
|
attr_reader :code, :description, :condition_index
|
|
46
57
|
|
|
58
|
+
# @param json [Hash] Raw reason data returned by /flags.
|
|
47
59
|
def initialize(json)
|
|
48
60
|
json.transform_keys!(&:to_s)
|
|
49
61
|
@code = json['code']
|
|
@@ -52,10 +64,13 @@ module PostHog
|
|
|
52
64
|
end
|
|
53
65
|
end
|
|
54
66
|
|
|
55
|
-
# Represents metadata about a feature flag
|
|
67
|
+
# Represents metadata about a feature flag.
|
|
68
|
+
#
|
|
69
|
+
# @api private
|
|
56
70
|
class FeatureFlagMetadata
|
|
57
71
|
attr_reader :id, :version, :payload, :description
|
|
58
72
|
|
|
73
|
+
# @param json [Hash] Raw metadata returned by /flags.
|
|
59
74
|
def initialize(json)
|
|
60
75
|
json.transform_keys!(&:to_s)
|
|
61
76
|
@id = json['id']
|
|
@@ -17,6 +17,7 @@ module PostHog
|
|
|
17
17
|
#
|
|
18
18
|
# For API errors with status codes, use the api_error() method which returns
|
|
19
19
|
# a string like "api_error_500".
|
|
20
|
+
# @api private
|
|
20
21
|
class FeatureFlagError
|
|
21
22
|
ERRORS_WHILE_COMPUTING = 'errors_while_computing_flags'
|
|
22
23
|
FLAG_MISSING = 'flag_missing'
|
|
@@ -4,7 +4,7 @@ require 'set'
|
|
|
4
4
|
|
|
5
5
|
module PostHog
|
|
6
6
|
# A snapshot of feature flag evaluations for one distinct_id, returned by
|
|
7
|
-
# PostHog::Client#evaluate_flags. Calls to {#
|
|
7
|
+
# PostHog::Client#evaluate_flags. Calls to {#enabled?} / {#get_flag} fire the
|
|
8
8
|
# `$feature_flag_called` event (deduped through the existing per-distinct_id
|
|
9
9
|
# cache); {#get_flag_payload} does not. Pass the snapshot to `capture(flags:)`
|
|
10
10
|
# to attach `$feature/<key>` and `$active_feature_flags` without a second
|
|
@@ -19,8 +19,32 @@ module PostHog
|
|
|
19
19
|
|
|
20
20
|
Host = Struct.new(:capture_flag_called_event_if_needed, :log_warning, keyword_init: true)
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
# @return [String] The distinct id these evaluations belong to.
|
|
23
|
+
attr_reader :distinct_id
|
|
23
24
|
|
|
25
|
+
# @return [Hash, nil] Group analytics mapping used for evaluation.
|
|
26
|
+
attr_reader :groups
|
|
27
|
+
|
|
28
|
+
# @return [String, nil] Request id returned by the /flags endpoint.
|
|
29
|
+
attr_reader :request_id
|
|
30
|
+
|
|
31
|
+
# @return [String, nil] Evaluation timestamp returned by the /flags endpoint.
|
|
32
|
+
attr_reader :evaluated_at
|
|
33
|
+
|
|
34
|
+
# @return [Time, nil] When local flag definitions were loaded.
|
|
35
|
+
attr_reader :flag_definitions_loaded_at
|
|
36
|
+
|
|
37
|
+
# @param host [Host, nil] Internal host callbacks used to record flag access events.
|
|
38
|
+
# @param distinct_id [String, nil] The distinct id these evaluations belong to.
|
|
39
|
+
# @param flags [Hash] Evaluated flags keyed by flag key.
|
|
40
|
+
# @param groups [Hash, nil] Group analytics mapping from group type to group key.
|
|
41
|
+
# @param disable_geoip [Boolean, nil] Whether GeoIP was disabled during evaluation.
|
|
42
|
+
# @param request_id [String, nil] The request id returned by the /flags endpoint.
|
|
43
|
+
# @param evaluated_at [String, nil] The evaluation timestamp returned by the /flags endpoint.
|
|
44
|
+
# @param flag_definitions_loaded_at [Time, nil] When local flag definitions were loaded.
|
|
45
|
+
# @param errors_while_computing [Boolean] Whether the server reported errors while computing flags.
|
|
46
|
+
# @param quota_limited [Boolean] Whether feature flag evaluation was quota limited.
|
|
47
|
+
# @param accessed [Array<String>, Set<String>, nil] Flag keys already accessed by this snapshot.
|
|
24
48
|
def initialize(
|
|
25
49
|
host: nil,
|
|
26
50
|
distinct_id: nil,
|
|
@@ -47,10 +71,13 @@ module PostHog
|
|
|
47
71
|
@accessed = Set.new(accessed || [])
|
|
48
72
|
end
|
|
49
73
|
|
|
74
|
+
# @return [Array<String>] The evaluated flag keys in this snapshot.
|
|
50
75
|
def keys
|
|
51
76
|
@flags.keys
|
|
52
77
|
end
|
|
53
78
|
|
|
79
|
+
# @param key [String, Symbol] The feature flag key.
|
|
80
|
+
# @return [Boolean] true when the flag is enabled, false when disabled or missing.
|
|
54
81
|
def enabled?(key)
|
|
55
82
|
key = key.to_s
|
|
56
83
|
flag = @flags[key]
|
|
@@ -58,6 +85,9 @@ module PostHog
|
|
|
58
85
|
flag&.enabled ? true : false
|
|
59
86
|
end
|
|
60
87
|
|
|
88
|
+
# @param key [String, Symbol] The feature flag key.
|
|
89
|
+
# @return [String, Boolean, nil] Variant string for multivariate flags, true/false for boolean flags,
|
|
90
|
+
# or nil when the flag was not returned by the evaluation.
|
|
61
91
|
def get_flag(key)
|
|
62
92
|
key = key.to_s
|
|
63
93
|
flag = @flags[key]
|
|
@@ -65,6 +95,8 @@ module PostHog
|
|
|
65
95
|
_flag_value(flag)
|
|
66
96
|
end
|
|
67
97
|
|
|
98
|
+
# @param key [String, Symbol] The feature flag key.
|
|
99
|
+
# @return [Object, nil] The parsed payload for the flag, if any.
|
|
68
100
|
def get_flag_payload(key)
|
|
69
101
|
flag = @flags[key.to_s]
|
|
70
102
|
flag&.payload
|
|
@@ -73,10 +105,14 @@ module PostHog
|
|
|
73
105
|
# Order-dependent: if nothing has been accessed yet, the returned snapshot is
|
|
74
106
|
# empty. The method honors its name — pre-access flags before calling this if
|
|
75
107
|
# you want a populated result.
|
|
108
|
+
# @return [PostHog::FeatureFlagEvaluations] A snapshot containing only flags already accessed with
|
|
109
|
+
# {#enabled?} or {#get_flag}.
|
|
76
110
|
def only_accessed
|
|
77
111
|
_clone_with(@flags.slice(*@accessed))
|
|
78
112
|
end
|
|
79
113
|
|
|
114
|
+
# @param keys [Array<String, Symbol>, String, Symbol] Flag keys to keep in the returned snapshot.
|
|
115
|
+
# @return [PostHog::FeatureFlagEvaluations] A snapshot containing only the requested keys that exist.
|
|
80
116
|
def only(keys)
|
|
81
117
|
keys = Array(keys).map(&:to_s)
|
|
82
118
|
missing = keys.reject { |k| @flags.key?(k) }
|
|
@@ -92,6 +128,9 @@ module PostHog
|
|
|
92
128
|
|
|
93
129
|
# Builds the `$feature/<key>` and `$active_feature_flags` properties for a
|
|
94
130
|
# captured event. Called from PostHog::Client#capture when `flags:` is set.
|
|
131
|
+
#
|
|
132
|
+
# @api private
|
|
133
|
+
# @return [Hash]
|
|
95
134
|
def _get_event_properties
|
|
96
135
|
properties = {}
|
|
97
136
|
active = []
|
|
@@ -6,8 +6,19 @@ module PostHog
|
|
|
6
6
|
# Represents the result of a feature flag evaluation
|
|
7
7
|
# containing both the flag value and payload
|
|
8
8
|
class FeatureFlagResult
|
|
9
|
-
|
|
9
|
+
# @return [String, Symbol] The feature flag key.
|
|
10
|
+
attr_reader :key
|
|
10
11
|
|
|
12
|
+
# @return [String, nil] The variant key for multivariate flags.
|
|
13
|
+
attr_reader :variant
|
|
14
|
+
|
|
15
|
+
# @return [Object, nil] The parsed feature flag payload.
|
|
16
|
+
attr_reader :payload
|
|
17
|
+
|
|
18
|
+
# @param key [String, Symbol] The feature flag key.
|
|
19
|
+
# @param enabled [Boolean] Whether the feature flag is enabled.
|
|
20
|
+
# @param variant [String, nil] The variant key for multivariate flags.
|
|
21
|
+
# @param payload [Object, nil] The parsed feature flag payload.
|
|
11
22
|
def initialize(key:, enabled:, variant: nil, payload: nil)
|
|
12
23
|
@key = key
|
|
13
24
|
@enabled = enabled
|
|
@@ -15,18 +26,27 @@ module PostHog
|
|
|
15
26
|
@payload = payload
|
|
16
27
|
end
|
|
17
28
|
|
|
18
|
-
# Returns the effective value of the feature flag
|
|
19
|
-
#
|
|
29
|
+
# Returns the effective value of the feature flag: variant if present,
|
|
30
|
+
# otherwise enabled status.
|
|
31
|
+
#
|
|
32
|
+
# @return [String, Boolean]
|
|
20
33
|
def value
|
|
21
34
|
@variant || @enabled
|
|
22
35
|
end
|
|
23
36
|
|
|
24
|
-
# Returns whether or not the feature flag evaluated as enabled
|
|
37
|
+
# Returns whether or not the feature flag evaluated as enabled.
|
|
38
|
+
#
|
|
39
|
+
# @return [Boolean]
|
|
25
40
|
def enabled?
|
|
26
41
|
@enabled
|
|
27
42
|
end
|
|
28
43
|
|
|
29
|
-
# Factory method to create from flag value and payload
|
|
44
|
+
# Factory method to create from flag value and payload.
|
|
45
|
+
#
|
|
46
|
+
# @param key [String, Symbol] The feature flag key.
|
|
47
|
+
# @param value [String, Boolean, nil] The raw feature flag value.
|
|
48
|
+
# @param payload [Object, String, nil] The raw or JSON-encoded feature flag payload.
|
|
49
|
+
# @return [PostHog::FeatureFlagResult, nil]
|
|
30
50
|
def self.from_value_and_payload(key, value, payload)
|
|
31
51
|
return nil if value.nil?
|
|
32
52
|
|
|
@@ -43,6 +63,9 @@ module PostHog
|
|
|
43
63
|
# returned when the body is not valid JSON); already-deserialized values
|
|
44
64
|
# pass through. Public so {FeatureFlagEvaluations} can normalize payloads
|
|
45
65
|
# the same way {FeatureFlagResult} does.
|
|
66
|
+
#
|
|
67
|
+
# @param payload [Object, String, nil] The raw payload value.
|
|
68
|
+
# @return [Object, nil] The parsed payload.
|
|
46
69
|
def self.parse_payload(payload)
|
|
47
70
|
return nil if payload.nil?
|
|
48
71
|
return payload unless payload.is_a?(String)
|
|
@@ -20,10 +20,20 @@ module PostHog
|
|
|
20
20
|
class RequiresServerEvaluation < StandardError
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
# Polls and evaluates feature flag definitions for {PostHog::Client}.
|
|
24
|
+
#
|
|
25
|
+
# @api private
|
|
23
26
|
class FeatureFlagsPoller
|
|
24
27
|
include PostHog::Logging
|
|
25
28
|
include PostHog::Utils
|
|
26
29
|
|
|
30
|
+
# @param polling_interval [Integer, nil] Seconds between local feature flag definition polls.
|
|
31
|
+
# @param personal_api_key [String, nil] Personal API key used to fetch local evaluation definitions.
|
|
32
|
+
# @param project_api_key [String] Project API key.
|
|
33
|
+
# @param host [String] PostHog API host URL.
|
|
34
|
+
# @param feature_flag_request_timeout_seconds [Integer] Timeout for feature flag requests.
|
|
35
|
+
# @param on_error [Proc, nil] Callback invoked as `on_error.call(status, error)`.
|
|
36
|
+
# @param flag_definition_cache_provider [Object, nil] Optional {FlagDefinitionCacheProvider} implementation.
|
|
27
37
|
def initialize(
|
|
28
38
|
polling_interval,
|
|
29
39
|
personal_api_key,
|
|
@@ -446,6 +456,16 @@ module PostHog
|
|
|
446
456
|
parsed_dt
|
|
447
457
|
end
|
|
448
458
|
|
|
459
|
+
# Parse a single semver numeric identifier, rejecting empty, non-digit, or
|
|
460
|
+
# leading-zero values per semver 2.0.0 §2.
|
|
461
|
+
def self.parse_semver_numeric(part)
|
|
462
|
+
raise InconclusiveMatchError, 'Invalid semver format' if part.nil? || part.empty? || part !~ /^\d+$/
|
|
463
|
+
# Semver 2.0.0 §2: numeric identifiers MUST NOT include leading zeros.
|
|
464
|
+
raise InconclusiveMatchError, 'Invalid semver format' if part.length > 1 && part[0] == '0'
|
|
465
|
+
|
|
466
|
+
part.to_i
|
|
467
|
+
end
|
|
468
|
+
|
|
449
469
|
# Parse a semver string into a comparable [major, minor, patch] integer array.
|
|
450
470
|
# Handles v-prefix, whitespace, pre-release suffixes. Defaults missing components to 0.
|
|
451
471
|
def self.parse_semver(value)
|
|
@@ -461,14 +481,9 @@ module PostHog
|
|
|
461
481
|
|
|
462
482
|
raise InconclusiveMatchError, 'Invalid semver format' if parts.empty? || parts[0].to_s.empty?
|
|
463
483
|
|
|
464
|
-
|
|
465
|
-
parts.
|
|
466
|
-
|
|
467
|
-
end
|
|
468
|
-
|
|
469
|
-
major = parts[0].to_i
|
|
470
|
-
minor = parts.length > 1 ? parts[1].to_i : 0
|
|
471
|
-
patch = parts.length > 2 ? parts[2].to_i : 0
|
|
484
|
+
major = parse_semver_numeric(parts[0])
|
|
485
|
+
minor = parts.length > 1 ? parse_semver_numeric(parts[1]) : 0
|
|
486
|
+
patch = parts.length > 2 ? parse_semver_numeric(parts[2]) : 0
|
|
472
487
|
|
|
473
488
|
[major, minor, patch]
|
|
474
489
|
end
|
|
@@ -525,20 +540,22 @@ module PostHog
|
|
|
525
540
|
|
|
526
541
|
raise InconclusiveMatchError, 'Invalid semver wildcard format' if parts.empty?
|
|
527
542
|
|
|
528
|
-
parts.
|
|
529
|
-
|
|
543
|
+
numeric = parts.map do |part|
|
|
544
|
+
parse_semver_numeric(part)
|
|
545
|
+
rescue InconclusiveMatchError
|
|
546
|
+
raise InconclusiveMatchError, 'Invalid semver wildcard format'
|
|
530
547
|
end
|
|
531
548
|
|
|
532
|
-
major =
|
|
533
|
-
case
|
|
549
|
+
major = numeric[0]
|
|
550
|
+
case numeric.length
|
|
534
551
|
when 1
|
|
535
552
|
[[major, 0, 0], [major + 1, 0, 0]]
|
|
536
553
|
when 2
|
|
537
|
-
minor =
|
|
554
|
+
minor = numeric[1]
|
|
538
555
|
[[major, minor, 0], [major, minor + 1, 0]]
|
|
539
556
|
else
|
|
540
|
-
minor =
|
|
541
|
-
patch =
|
|
557
|
+
minor = numeric[1]
|
|
558
|
+
patch = numeric[2]
|
|
542
559
|
[[major, minor, patch], [major, minor, patch + 1]]
|
|
543
560
|
end
|
|
544
561
|
end
|
data/lib/posthog/field_parser.rb
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
require 'posthog/logging'
|
|
4
4
|
|
|
5
5
|
module PostHog
|
|
6
|
+
# Converts public SDK method arguments into PostHog API event payloads.
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
6
9
|
class FieldParser
|
|
7
10
|
class << self
|
|
8
11
|
include PostHog::Utils
|
|
@@ -14,6 +17,8 @@ module PostHog
|
|
|
14
17
|
# - "properties"
|
|
15
18
|
# - "groups"
|
|
16
19
|
# - "uuid"
|
|
20
|
+
# @param fields [Hash]
|
|
21
|
+
# @return [Hash]
|
|
17
22
|
def parse_for_capture(fields)
|
|
18
23
|
common = parse_common_fields(fields)
|
|
19
24
|
|
|
@@ -45,6 +50,8 @@ module PostHog
|
|
|
45
50
|
# In addition to the common fields, identify accepts:
|
|
46
51
|
#
|
|
47
52
|
# - "properties"
|
|
53
|
+
# @param fields [Hash]
|
|
54
|
+
# @return [Hash]
|
|
48
55
|
def parse_for_identify(fields)
|
|
49
56
|
common = parse_common_fields(fields)
|
|
50
57
|
|
|
@@ -63,6 +70,8 @@ module PostHog
|
|
|
63
70
|
)
|
|
64
71
|
end
|
|
65
72
|
|
|
73
|
+
# @param fields [Hash]
|
|
74
|
+
# @return [Hash]
|
|
66
75
|
def parse_for_group_identify(fields)
|
|
67
76
|
properties = fields[:properties] || {}
|
|
68
77
|
group_type = fields[:group_type]
|
|
@@ -92,6 +101,8 @@ module PostHog
|
|
|
92
101
|
# In addition to the common fields, alias accepts:
|
|
93
102
|
#
|
|
94
103
|
# - "alias"
|
|
104
|
+
# @param fields [Hash]
|
|
105
|
+
# @return [Hash]
|
|
95
106
|
def parse_for_alias(fields)
|
|
96
107
|
common = parse_common_fields(fields)
|
|
97
108
|
|
|
@@ -14,26 +14,31 @@ module PostHog
|
|
|
14
14
|
#
|
|
15
15
|
# == Required Methods
|
|
16
16
|
#
|
|
17
|
-
#
|
|
17
|
+
# @!method flag_definitions
|
|
18
18
|
# Retrieve cached flag definitions. Return a Hash with +:flags+,
|
|
19
19
|
# +:group_type_mapping+, and +:cohorts+ keys, or +nil+ if the cache
|
|
20
20
|
# is empty. Returning +nil+ triggers an API fetch when no flags are
|
|
21
21
|
# loaded yet (emergency fallback).
|
|
22
|
+
# @return [Hash, nil]
|
|
22
23
|
#
|
|
23
|
-
#
|
|
24
|
+
# @!method should_fetch_flag_definitions?
|
|
24
25
|
# Return +true+ if this instance should fetch new definitions from the
|
|
25
26
|
# API, +false+ to read from cache instead. Use for distributed lock
|
|
26
27
|
# coordination so only one worker fetches at a time.
|
|
28
|
+
# @return [Boolean]
|
|
27
29
|
#
|
|
28
|
-
#
|
|
30
|
+
# @!method on_flag_definitions_received(data)
|
|
29
31
|
# Called after successfully fetching new definitions from the API.
|
|
30
32
|
# +data+ is a Hash with +:flags+, +:group_type_mapping+, and +:cohorts+
|
|
31
33
|
# keys (plain Ruby types, not Concurrent:: wrappers). Store it in your
|
|
32
34
|
# external cache.
|
|
35
|
+
# @param data [Hash]
|
|
36
|
+
# @return [void]
|
|
33
37
|
#
|
|
34
|
-
#
|
|
38
|
+
# @!method shutdown
|
|
35
39
|
# Called when the PostHog client shuts down. Release any distributed
|
|
36
40
|
# locks and clean up resources.
|
|
41
|
+
# @return [void]
|
|
37
42
|
#
|
|
38
43
|
# == Error Handling
|
|
39
44
|
#
|
|
@@ -66,6 +71,7 @@ module PostHog
|
|
|
66
71
|
#
|
|
67
72
|
# @param provider [Object] the cache provider to validate
|
|
68
73
|
# @raise [ArgumentError] if any required methods are missing
|
|
74
|
+
# @return [void]
|
|
69
75
|
def self.validate!(provider)
|
|
70
76
|
missing = REQUIRED_METHODS.reject { |m| provider.respond_to?(m) }
|
|
71
77
|
return if missing.empty?
|
data/lib/posthog/logging.rb
CHANGED
|
@@ -3,8 +3,12 @@
|
|
|
3
3
|
require 'logger'
|
|
4
4
|
|
|
5
5
|
module PostHog
|
|
6
|
-
# Wraps an existing logger and adds a prefix to all messages
|
|
6
|
+
# Wraps an existing logger and adds a prefix to all messages.
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
7
9
|
class PrefixedLogger
|
|
10
|
+
# @param logger [Logger, #debug, #info, #warn, #error]
|
|
11
|
+
# @param prefix [String]
|
|
8
12
|
def initialize(logger, prefix)
|
|
9
13
|
@logger = logger
|
|
10
14
|
@prefix = prefix
|
|
@@ -37,6 +41,7 @@ module PostHog
|
|
|
37
41
|
|
|
38
42
|
module Logging
|
|
39
43
|
class << self
|
|
44
|
+
# @return [Logger, PostHog::PrefixedLogger] The logger used by the SDK.
|
|
40
45
|
def logger
|
|
41
46
|
return @logger if @logger
|
|
42
47
|
|
|
@@ -52,6 +57,7 @@ module PostHog
|
|
|
52
57
|
@logger = PrefixedLogger.new(base_logger, '[posthog-ruby]')
|
|
53
58
|
end
|
|
54
59
|
|
|
60
|
+
# @param logger [Logger, #debug, #info, #warn, #error] Custom logger used by the SDK.
|
|
55
61
|
attr_writer :logger
|
|
56
62
|
end
|
|
57
63
|
|
|
@@ -63,6 +69,7 @@ module PostHog
|
|
|
63
69
|
end
|
|
64
70
|
end
|
|
65
71
|
|
|
72
|
+
# @return [Logger, PostHog::PrefixedLogger] The logger used by the SDK.
|
|
66
73
|
def logger
|
|
67
74
|
Logging.logger
|
|
68
75
|
end
|
|
@@ -4,7 +4,9 @@ require 'forwardable'
|
|
|
4
4
|
require 'posthog/logging'
|
|
5
5
|
|
|
6
6
|
module PostHog
|
|
7
|
-
# A batch of
|
|
7
|
+
# A batch of messages to be sent to the API.
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
8
10
|
class MessageBatch
|
|
9
11
|
class JSONGenerationError < StandardError
|
|
10
12
|
end
|
|
@@ -13,12 +15,15 @@ module PostHog
|
|
|
13
15
|
include PostHog::Logging
|
|
14
16
|
include PostHog::Defaults::MessageBatch
|
|
15
17
|
|
|
18
|
+
# @param max_message_count [Integer] Maximum number of messages in the batch.
|
|
16
19
|
def initialize(max_message_count)
|
|
17
20
|
@messages = []
|
|
18
21
|
@max_message_count = max_message_count
|
|
19
22
|
@json_size = 0
|
|
20
23
|
end
|
|
21
24
|
|
|
25
|
+
# @param message [Hash] Message to add to the batch.
|
|
26
|
+
# @return [Array<Hash>, nil]
|
|
22
27
|
def <<(message)
|
|
23
28
|
begin
|
|
24
29
|
message_json = message.to_json
|
|
@@ -35,10 +40,12 @@ module PostHog
|
|
|
35
40
|
end
|
|
36
41
|
end
|
|
37
42
|
|
|
43
|
+
# @return [Boolean] Whether the batch is full.
|
|
38
44
|
def full?
|
|
39
45
|
item_count_exhausted? || size_exhausted?
|
|
40
46
|
end
|
|
41
47
|
|
|
48
|
+
# @return [void]
|
|
42
49
|
def clear
|
|
43
50
|
@messages.clear
|
|
44
51
|
@json_size = 0
|
data/lib/posthog/noop_worker.rb
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# A worker that doesn't consume jobs
|
|
4
3
|
module PostHog
|
|
4
|
+
# A worker that doesn't consume jobs.
|
|
5
|
+
#
|
|
6
|
+
# @api private
|
|
5
7
|
class NoopWorker
|
|
8
|
+
# @param queue [Queue]
|
|
6
9
|
def initialize(queue)
|
|
7
10
|
@queue = queue
|
|
8
11
|
end
|
|
9
12
|
|
|
13
|
+
# @return [void]
|
|
10
14
|
def run
|
|
11
15
|
# Does nothing
|
|
12
16
|
end
|
|
13
17
|
|
|
18
|
+
# @return [Boolean]
|
|
14
19
|
# TODO: Rename to `requesting?` in future version
|
|
15
20
|
def is_requesting? # rubocop:disable Naming/PredicateName
|
|
16
21
|
false
|
|
17
22
|
end
|
|
18
23
|
|
|
24
|
+
# @return [void]
|
|
19
25
|
def shutdown
|
|
20
26
|
# Does nothing
|
|
21
27
|
end
|
data/lib/posthog/response.rb
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module PostHog
|
|
4
|
+
# API response wrapper returned by the SDK transport.
|
|
5
|
+
#
|
|
6
|
+
# @api private
|
|
4
7
|
class Response
|
|
5
8
|
attr_reader :status, :error
|
|
6
9
|
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
+
# @param status [Integer] HTTP status code, or -1 for SDK/transport errors.
|
|
11
|
+
# @param error [String, nil] Error message returned by the API or SDK.
|
|
10
12
|
def initialize(status = 200, error = nil)
|
|
11
13
|
@status = status
|
|
12
14
|
@error = error
|
|
@@ -3,16 +3,29 @@
|
|
|
3
3
|
require 'posthog/utils'
|
|
4
4
|
|
|
5
5
|
module PostHog
|
|
6
|
-
# Options for configuring feature flag behavior in capture calls
|
|
6
|
+
# Options for configuring deprecated feature flag behavior in capture calls.
|
|
7
|
+
#
|
|
8
|
+
# @deprecated Prefer passing a {PostHog::FeatureFlagEvaluations} snapshot to `capture(flags:)`.
|
|
7
9
|
class SendFeatureFlagsOptions
|
|
8
|
-
|
|
10
|
+
# @return [Boolean, nil] Whether remote feature flag evaluation should be skipped.
|
|
11
|
+
attr_reader :only_evaluate_locally
|
|
9
12
|
|
|
13
|
+
# @return [Hash] Person properties to use for feature flag evaluation.
|
|
14
|
+
attr_reader :person_properties
|
|
15
|
+
|
|
16
|
+
# @return [Hash] Group properties to use for feature flag evaluation.
|
|
17
|
+
attr_reader :group_properties
|
|
18
|
+
|
|
19
|
+
# @param only_evaluate_locally [Boolean, nil] Skip remote feature flag evaluation.
|
|
20
|
+
# @param person_properties [Hash, nil] Person properties to use for feature flag evaluation.
|
|
21
|
+
# @param group_properties [Hash, nil] Group properties to use for feature flag evaluation.
|
|
10
22
|
def initialize(only_evaluate_locally: nil, person_properties: nil, group_properties: nil)
|
|
11
23
|
@only_evaluate_locally = only_evaluate_locally
|
|
12
24
|
@person_properties = person_properties || {}
|
|
13
25
|
@group_properties = group_properties || {}
|
|
14
26
|
end
|
|
15
27
|
|
|
28
|
+
# @return [Hash] A hash representation suitable for `capture(send_feature_flags:)`.
|
|
16
29
|
def to_h
|
|
17
30
|
{
|
|
18
31
|
only_evaluate_locally: @only_evaluate_locally,
|
|
@@ -21,6 +34,8 @@ module PostHog
|
|
|
21
34
|
}
|
|
22
35
|
end
|
|
23
36
|
|
|
37
|
+
# @param hash [Hash]
|
|
38
|
+
# @return [PostHog::SendFeatureFlagsOptions, nil]
|
|
24
39
|
def self.from_hash(hash)
|
|
25
40
|
return nil unless hash.is_a?(Hash)
|
|
26
41
|
|
data/lib/posthog/send_worker.rb
CHANGED
|
@@ -6,6 +6,9 @@ require 'posthog/transport'
|
|
|
6
6
|
require 'posthog/utils'
|
|
7
7
|
|
|
8
8
|
module PostHog
|
|
9
|
+
# Background worker that batches and sends queued events.
|
|
10
|
+
#
|
|
11
|
+
# @api private
|
|
9
12
|
class SendWorker
|
|
10
13
|
include PostHog::Utils
|
|
11
14
|
include PostHog::Defaults
|
|
@@ -16,12 +19,13 @@ module PostHog
|
|
|
16
19
|
# The worker continuously takes messages off the queue
|
|
17
20
|
# and makes requests to the posthog.com api
|
|
18
21
|
#
|
|
19
|
-
# queue
|
|
20
|
-
# api_key
|
|
21
|
-
# options
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
22
|
+
# @param queue [Queue] Queue synchronized between client and worker.
|
|
23
|
+
# @param api_key [String] Project API key.
|
|
24
|
+
# @param options [Hash] Worker options.
|
|
25
|
+
# @option options [Integer] :batch_size How many items to send in a batch.
|
|
26
|
+
# @option options [Proc] :on_error Callback invoked as `on_error.call(status, error)`.
|
|
27
|
+
# @option options [String] :host PostHog API host URL.
|
|
28
|
+
# @option options [Boolean] :skip_ssl_verification Disable SSL certificate verification.
|
|
25
29
|
def initialize(queue, api_key, options = {})
|
|
26
30
|
symbolize_keys! options
|
|
27
31
|
@queue = queue
|
|
@@ -33,8 +37,9 @@ module PostHog
|
|
|
33
37
|
@transport = Transport.new api_host: options[:host], skip_ssl_verification: options[:skip_ssl_verification]
|
|
34
38
|
end
|
|
35
39
|
|
|
36
|
-
#
|
|
40
|
+
# Continuously runs the loop to check for new events.
|
|
37
41
|
#
|
|
42
|
+
# @return [void]
|
|
38
43
|
def run
|
|
39
44
|
until Thread.current[:should_exit]
|
|
40
45
|
return if @queue.empty?
|
|
@@ -54,12 +59,14 @@ module PostHog
|
|
|
54
59
|
@transport.shutdown
|
|
55
60
|
end
|
|
56
61
|
|
|
62
|
+
# @return [void]
|
|
57
63
|
def shutdown
|
|
58
64
|
@transport.shutdown
|
|
59
65
|
end
|
|
60
66
|
|
|
61
67
|
# public: Check whether we have outstanding requests.
|
|
62
68
|
#
|
|
69
|
+
# @return [Boolean] Whether the worker has outstanding requests.
|
|
63
70
|
# TODO: Rename to `requesting?` in future version
|
|
64
71
|
def is_requesting? # rubocop:disable Naming/PredicateName
|
|
65
72
|
@lock.synchronize { !@batch.empty? }
|
data/lib/posthog/transport.rb
CHANGED
|
@@ -10,11 +10,24 @@ require 'net/https'
|
|
|
10
10
|
require 'json'
|
|
11
11
|
|
|
12
12
|
module PostHog
|
|
13
|
+
# HTTP transport used by the SDK workers.
|
|
14
|
+
#
|
|
15
|
+
# @api private
|
|
13
16
|
class Transport
|
|
14
17
|
include PostHog::Defaults::Request
|
|
15
18
|
include PostHog::Utils
|
|
16
19
|
include PostHog::Logging
|
|
17
20
|
|
|
21
|
+
# @param options [Hash] Transport configuration.
|
|
22
|
+
# @option options [String] :api_host Full PostHog API host URL.
|
|
23
|
+
# @option options [String] :host Hostname to connect to.
|
|
24
|
+
# @option options [Integer] :port Port to connect to.
|
|
25
|
+
# @option options [Boolean] :ssl Whether to use HTTPS.
|
|
26
|
+
# @option options [Hash] :headers HTTP headers for batch requests.
|
|
27
|
+
# @option options [String] :path HTTP path for batch requests.
|
|
28
|
+
# @option options [Integer] :retries Number of retry attempts for retryable failures.
|
|
29
|
+
# @option options [PostHog::BackoffPolicy] :backoff_policy Backoff policy used between retries.
|
|
30
|
+
# @option options [Boolean] :skip_ssl_verification Disable SSL certificate verification.
|
|
18
31
|
def initialize(options = {})
|
|
19
32
|
if options[:api_host]
|
|
20
33
|
uri = URI.parse(options[:api_host])
|
|
@@ -43,6 +56,8 @@ module PostHog
|
|
|
43
56
|
|
|
44
57
|
# Sends a batch of messages to the API
|
|
45
58
|
#
|
|
59
|
+
# @param api_key [String] Project API key.
|
|
60
|
+
# @param batch [PostHog::MessageBatch, Array<Hash>] Batch of messages to send.
|
|
46
61
|
# @return [Response] API response
|
|
47
62
|
def send(api_key, batch)
|
|
48
63
|
logger.debug("Sending request for #{batch.length} items")
|
|
@@ -72,7 +87,9 @@ module PostHog
|
|
|
72
87
|
end
|
|
73
88
|
end
|
|
74
89
|
|
|
75
|
-
# Closes a persistent connection if it exists
|
|
90
|
+
# Closes a persistent connection if it exists.
|
|
91
|
+
#
|
|
92
|
+
# @return [void]
|
|
76
93
|
def shutdown
|
|
77
94
|
@http.finish if @http.started?
|
|
78
95
|
end
|
data/lib/posthog/utils.rb
CHANGED
|
@@ -6,6 +6,9 @@ module PostHog
|
|
|
6
6
|
class InconclusiveMatchError < StandardError
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
# Utility helpers used internally by the SDK.
|
|
10
|
+
#
|
|
11
|
+
# @api private
|
|
9
12
|
module Utils
|
|
10
13
|
module_function
|
|
11
14
|
|
|
@@ -145,12 +148,19 @@ module PostHog
|
|
|
145
148
|
end
|
|
146
149
|
end
|
|
147
150
|
|
|
151
|
+
# Hash that clears itself when it reaches a maximum length.
|
|
152
|
+
#
|
|
153
|
+
# @api private
|
|
148
154
|
class SizeLimitedHash < Hash
|
|
155
|
+
# @param max_length [Integer]
|
|
149
156
|
def initialize(max_length, ...)
|
|
150
157
|
super(...)
|
|
151
158
|
@max_length = max_length
|
|
152
159
|
end
|
|
153
160
|
|
|
161
|
+
# @param key [Object]
|
|
162
|
+
# @param value [Object]
|
|
163
|
+
# @return [Object]
|
|
154
164
|
def []=(key, value)
|
|
155
165
|
clear if length >= @max_length
|
|
156
166
|
super
|
data/lib/posthog/version.rb
CHANGED