posthog-ruby 2.4.3 → 2.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4045560a50a4339e398f41b14198c8f7e8a89b03e9c67c4051f9c3e8a3ddc023
4
- data.tar.gz: f5db284d85ca9b085205e435c236a0b74da89c141c2c25254307811308ed038f
3
+ metadata.gz: 78a70730ff0365203bb5af28ae5fadbb938950bb8980d048e3167878a1dc6645
4
+ data.tar.gz: e0a73f36a251b3280a21950c960c0a3f89172511a03f1c076898b769eb91ebbf
5
5
  SHA512:
6
- metadata.gz: b841977f8e744a5420d9fdec891e98d82b486c558dcbbf33c1ecf2441031b454bd5ea62323904e5600458a31fb11263513d49944edfd0c916359fecd1f4881a6
7
- data.tar.gz: 2f959dc54d97743c25abe0e3d3ae385f781a05fcdd2c8bc97d84d0909475ba1b183d546086b4cc943cc416c2c2f642810e838b869608779ae518b3482b85a77b
6
+ metadata.gz: 6f1b69643da1ed3dccfb38de8979436f9f0236676a2858931bea97215e617e6e97282e1b719bf1c811453731fe8e4873e7ac5a0fa021f9785899712b4ef43828
7
+ data.tar.gz: 7f016c7d172072f837f0780994d8018352356e19ae872578fd5befe2f657aab417c2aec350c357162d44fab52e59a3883236888e4a28a004b35e44b0e0a7bcf7
@@ -23,6 +23,7 @@ class PostHog
23
23
  # @option opts [Proc] :on_error Handles error calls from the API.
24
24
  # @option opts [String] :host Fully qualified hostname of the PostHog server. Defaults to `https://app.posthog.com`
25
25
  # @option opts [Integer] :feature_flags_polling_interval How often to poll for feature flag definition changes. Measured in seconds, defaults to 30.
26
+ # @option opts [Integer] :feature_flag_request_timeout_seconds How long to wait for feature flag evaluation. Measured in seconds, defaults to 3.
26
27
  def initialize(opts = {})
27
28
  symbolize_keys!(opts)
28
29
 
@@ -48,7 +49,9 @@ class PostHog
48
49
  opts[:feature_flags_polling_interval],
49
50
  opts[:personal_api_key],
50
51
  @api_key,
51
- opts[:host]
52
+ opts[:host],
53
+ opts[:feature_flag_request_timeout_seconds] || Defaults::FeatureFlags::FLAG_REQUEST_TIMEOUT_SECONDS,
54
+ opts[:on_error]
52
55
  )
53
56
 
54
57
  @distinct_id_has_sent_flag_calls = SizeLimitedHash.new(Defaults::MAX_HASH_SIZE) { |hash, key| hash[key] = Array.new }
@@ -90,7 +93,7 @@ class PostHog
90
93
  symbolize_keys! attrs
91
94
 
92
95
  if attrs[:send_feature_flags]
93
- feature_variants = @feature_flags_poller._get_active_feature_variants(attrs[:distinct_id], attrs[:groups])
96
+ feature_variants = @feature_flags_poller.get_feature_variants(attrs[:distinct_id], attrs[:groups] || {})
94
97
 
95
98
  attrs[:feature_variants] = feature_variants
96
99
  end
@@ -116,6 +119,7 @@ class PostHog
116
119
  # @option attrs [String] :group_type Group type
117
120
  # @option attrs [String] :group_key Group key
118
121
  # @option attrs [Hash] :properties Group properties (optional)
122
+ # @option attrs [String] :distinct_id Distinct ID (optional)
119
123
  # @macro common_attrs
120
124
  def group_identify(attrs)
121
125
  symbolize_keys! attrs
@@ -16,6 +16,10 @@ class PostHog
16
16
  RETRIES = 10
17
17
  end
18
18
 
19
+ module FeatureFlags
20
+ FLAG_REQUEST_TIMEOUT_SECONDS = 3
21
+ end
22
+
19
23
  module Queue
20
24
  MAX_SIZE = 10_000
21
25
  end
@@ -14,7 +14,7 @@ class PostHog
14
14
  include PostHog::Logging
15
15
  include PostHog::Utils
16
16
 
17
- def initialize(polling_interval, personal_api_key, project_api_key, host)
17
+ def initialize(polling_interval, personal_api_key, project_api_key, host, feature_flag_request_timeout_seconds, on_error = nil)
18
18
  @polling_interval = polling_interval || 30
19
19
  @personal_api_key = personal_api_key
20
20
  @project_api_key = project_api_key
@@ -23,6 +23,8 @@ class PostHog
23
23
  @group_type_mapping = Concurrent::Hash.new
24
24
  @loaded_flags_successfully_once = Concurrent::AtomicBoolean.new
25
25
  @feature_flags_by_key = nil
26
+ @feature_flag_request_timeout_seconds = feature_flag_request_timeout_seconds
27
+ @on_error = on_error || proc { |status, error| }
26
28
 
27
29
  @task =
28
30
  Concurrent::TimerTask.new(
@@ -46,30 +48,22 @@ class PostHog
46
48
  end
47
49
  end
48
50
 
49
- def get_feature_variants(distinct_id, groups={}, person_properties={}, group_properties={})
50
- decide_data = get_decide(distinct_id, groups, person_properties, group_properties)
51
+ def get_feature_variants(distinct_id, groups={}, person_properties={}, group_properties={}, raise_on_error=false)
52
+ # TODO: Convert to options hash for easier argument passing
53
+ decide_data = get_all_flags_and_payloads(distinct_id, groups, person_properties, group_properties, false, raise_on_error)
51
54
  if !decide_data.key?(:featureFlags)
52
- logger.error "Missing feature flags key: #{decide_data.to_json}"
55
+ logger.debug "Missing feature flags key: #{decide_data.to_json}"
56
+ {}
53
57
  else
54
58
  stringify_keys(decide_data[:featureFlags] || {})
55
59
  end
56
60
  end
57
61
 
58
- def _get_active_feature_variants(distinct_id, groups={}, person_properties={}, group_properties={})
59
- feature_variants = get_feature_variants(distinct_id, groups, person_properties, group_properties)
60
- active_feature_variants = {}
61
- feature_variants.each do |key, value|
62
- if value != false
63
- active_feature_variants[key] = value
64
- end
65
- end
66
- active_feature_variants
67
- end
68
-
69
62
  def get_feature_payloads(distinct_id, groups = {}, person_properties = {}, group_properties = {}, only_evaluate_locally = false)
70
- decide_data = get_decide(distinct_id, groups, person_properties, group_properties)
63
+ decide_data = get_all_flags_and_payloads(distinct_id, groups, person_properties, group_properties)
71
64
  if !decide_data.key?(:featureFlagPayloads)
72
- logger.error "Missing feature flag payloads key: #{decide_data.to_json}"
65
+ logger.debug "Missing feature flag payloads key: #{decide_data.to_json}"
66
+ return {}
73
67
  else
74
68
  stringify_keys(decide_data[:featureFlagPayloads] || {})
75
69
  end
@@ -115,7 +109,7 @@ class PostHog
115
109
  rescue InconclusiveMatchError => e
116
110
  logger.debug "Failed to compute flag #{key} locally: #{e}"
117
111
  rescue StandardError => e
118
- logger.error "Error computing flag locally: #{e}. #{e.backtrace.join("\n")}"
112
+ @on_error.call(-1, "Error computing flag locally: #{e}. #{e.backtrace.join("\n")}")
119
113
  end
120
114
  end
121
115
 
@@ -123,14 +117,14 @@ class PostHog
123
117
 
124
118
  if !flag_was_locally_evaluated && !only_evaluate_locally
125
119
  begin
126
- flags = get_feature_variants(distinct_id, groups, person_properties, group_properties)
120
+ flags = get_feature_variants(distinct_id, groups, person_properties, group_properties, true)
127
121
  response = flags[key]
128
122
  if response.nil?
129
123
  response = false
130
124
  end
131
125
  logger.debug "Successfully computed flag remotely: #{key} -> #{response}"
132
126
  rescue StandardError => e
133
- logger.error "Error computing flag remotely: #{e}. #{e.backtrace.join("\n")}"
127
+ @on_error.call(-1, "Error computing flag remotely: #{e}. #{e.backtrace.join("\n")}")
134
128
  end
135
129
  end
136
130
 
@@ -143,7 +137,7 @@ class PostHog
143
137
  flags = response[:featureFlags]
144
138
  end
145
139
 
146
- def get_all_flags_and_payloads(distinct_id, groups = {}, person_properties = {}, group_properties = {}, only_evaluate_locally = false)
140
+ def get_all_flags_and_payloads(distinct_id, groups = {}, person_properties = {}, group_properties = {}, only_evaluate_locally = false, raise_on_error = false)
147
141
  load_feature_flags
148
142
 
149
143
  flags = {}
@@ -161,17 +155,22 @@ class PostHog
161
155
  rescue InconclusiveMatchError => e
162
156
  fallback_to_decide = true
163
157
  rescue StandardError => e
164
- logger.error "Error computing flag locally: #{e}."
158
+ @on_error.call(-1, "Error computing flag locally: #{e}. #{e.backtrace.join("\n")} ")
165
159
  fallback_to_decide = true
166
160
  end
167
161
  end
168
162
  if fallback_to_decide && !only_evaluate_locally
169
163
  begin
170
164
  flags_and_payloads = get_decide(distinct_id, groups, person_properties, group_properties)
165
+
166
+ unless flags_and_payloads.key?(:featureFlags)
167
+ raise StandardError.new("Error flags response: #{flags_and_payloads}")
168
+ end
171
169
  flags = stringify_keys(flags_and_payloads[:featureFlags] || {})
172
170
  payloads = stringify_keys(flags_and_payloads[:featureFlagPayloads] || {})
173
171
  rescue StandardError => e
174
- logger.error "Error computing flag remotely: #{e}"
172
+ @on_error.call(-1, "Error computing flag remotely: #{e}")
173
+ raise if raise_on_error
175
174
  end
176
175
  end
177
176
  {"featureFlags": flags, "featureFlagPayloads": payloads}
@@ -370,8 +369,9 @@ class PostHog
370
369
  end
371
370
 
372
371
  def _compute_flag_payload_locally(key, match_value)
373
- response = nil
372
+ return nil if @feature_flags_by_key.nil?
374
373
 
374
+ response = nil
375
375
  if [true, false].include? match_value
376
376
  response = @feature_flags_by_key.dig(key, :filters, :payloads, match_value.to_s.to_sym)
377
377
  elsif match_value.is_a? String
@@ -472,10 +472,15 @@ class PostHog
472
472
  end
473
473
 
474
474
  def _load_feature_flags()
475
- res = _request_feature_flag_definitions
475
+ begin
476
+ res = _request_feature_flag_definitions
477
+ rescue StandardError => e
478
+ @on_error.call(-1, e.to_s)
479
+ return
480
+ end
476
481
 
477
482
  if !res.key?(:flags)
478
- logger.error "Failed to load feature flags: #{res}"
483
+ logger.debug "Failed to load feature flags: #{res}"
479
484
  else
480
485
  @feature_flags = res[:flags] || []
481
486
  @feature_flags_by_key = {}
@@ -508,16 +513,18 @@ class PostHog
508
513
  data['token'] = @project_api_key
509
514
  req.body = data.to_json
510
515
 
511
- _request(uri, req)
516
+ _request(uri, req, @feature_flag_request_timeout_seconds)
512
517
  end
513
518
 
514
- def _request(uri, request_object)
519
+ def _request(uri, request_object, timeout = nil)
515
520
 
516
521
  request_object['User-Agent'] = "posthog-ruby#{PostHog::VERSION}"
517
522
 
523
+ request_timeout = timeout || 10
524
+
518
525
  begin
519
526
  res_body = nil
520
- Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
527
+ Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https', :read_timeout => request_timeout) do |http|
521
528
  res = http.request(request_object)
522
529
  JSON.parse(res.body, {symbolize_names: true})
523
530
  end
@@ -527,9 +534,11 @@ class PostHog
527
534
  EOFError,
528
535
  Net::HTTPBadResponse,
529
536
  Net::HTTPHeaderSyntaxError,
537
+ Net::ReadTimeout,
538
+ Net::WriteTimeout,
530
539
  Net::ProtocolError => e
531
540
  logger.debug("Unable to complete request to #{uri}")
532
- throw e
541
+ raise
533
542
  end
534
543
  end
535
544
  end
@@ -64,8 +64,7 @@ class PostHog
64
64
  check_presence!(group_key, 'group_key')
65
65
  check_is_hash!(properties, 'properties')
66
66
 
67
- distinct_id = "$#{group_type}_#{group_key}"
68
- fields[:distinct_id] = distinct_id
67
+ fields[:distinct_id] ||= "$#{group_type}_#{group_key}"
69
68
  common = parse_common_fields(fields)
70
69
 
71
70
  isoify_dates! properties
@@ -137,10 +136,14 @@ class PostHog
137
136
 
138
137
  if send_feature_flags
139
138
  feature_variants = fields[:feature_variants]
139
+ active_feature_variants = {}
140
140
  feature_variants.each do |key, value|
141
141
  parsed[:properties]["$feature/#{key}"] = value
142
+ if value != false
143
+ active_feature_variants[key] = value
144
+ end
142
145
  end
143
- parsed[:properties]["$active_feature_flags"] = feature_variants.keys
146
+ parsed[:properties]["$active_feature_flags"] = active_feature_variants.keys
144
147
  end
145
148
  parsed
146
149
  end
@@ -1,3 +1,3 @@
1
1
  class PostHog
2
- VERSION = '2.4.3'
2
+ VERSION = '2.5.1'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: posthog-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.3
4
+ version: 2.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-29 00:00:00.000000000 Z
11
+ date: 2024-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -128,14 +128,14 @@ dependencies:
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: 0.1.4
131
+ version: 0.6.0
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: 0.1.4
138
+ version: 0.6.0
139
139
  description: The PostHog ruby library
140
140
  email: hey@posthog.com
141
141
  executables:
@@ -163,7 +163,7 @@ homepage: https://github.com/PostHog/posthog-ruby
163
163
  licenses:
164
164
  - MIT
165
165
  metadata: {}
166
- post_install_message:
166
+ post_install_message:
167
167
  rdoc_options: []
168
168
  require_paths:
169
169
  - lib
@@ -178,8 +178,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
178
  - !ruby/object:Gem::Version
179
179
  version: '0'
180
180
  requirements: []
181
- rubygems_version: 3.1.2
182
- signing_key:
181
+ rubygems_version: 3.4.10
182
+ signing_key:
183
183
  specification_version: 4
184
184
  summary: PostHog library
185
185
  test_files: []