posthog-ruby 3.3.3 → 3.4.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 +21 -14
- data/lib/posthog/feature_flags.rb +44 -9
- data/lib/posthog/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9e99287bd9fe6eb8969c2481c0380c2a6eba12709de5f5189a053cb9ba5ca255
|
|
4
|
+
data.tar.gz: 78b496c5feb9c8cb222393ababa36d0f6fd81c18e603469b0633ac3fcdbfd639
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dc1c52ff0243af441d895c05199067347931f0bf50a21ba897a3fb75640cb954ec66c79dddb29fb92338479014f3a7f42543a1ef651ebdcbd7fe6845ff9e9002
|
|
7
|
+
data.tar.gz: e46932963b504e54fe4590db73e1956611190b2ab16a1f04ffff5877c01b1e71a2dafc5f9c373361b69bb5f63532d1400c244580ac3c229ced006b350880eef5
|
data/lib/posthog/client.rb
CHANGED
|
@@ -285,26 +285,31 @@ module PostHog
|
|
|
285
285
|
person_properties,
|
|
286
286
|
group_properties
|
|
287
287
|
)
|
|
288
|
-
feature_flag_response, flag_was_locally_evaluated, request_id =
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
288
|
+
feature_flag_response, flag_was_locally_evaluated, request_id, evaluated_at =
|
|
289
|
+
@feature_flags_poller.get_feature_flag(
|
|
290
|
+
key,
|
|
291
|
+
distinct_id,
|
|
292
|
+
groups,
|
|
293
|
+
person_properties,
|
|
294
|
+
group_properties,
|
|
295
|
+
only_evaluate_locally
|
|
296
|
+
)
|
|
296
297
|
|
|
297
298
|
feature_flag_reported_key = "#{key}_#{feature_flag_response}"
|
|
298
299
|
if !@distinct_id_has_sent_flag_calls[distinct_id].include?(feature_flag_reported_key) && send_feature_flag_events
|
|
300
|
+
properties = {
|
|
301
|
+
'$feature_flag' => key,
|
|
302
|
+
'$feature_flag_response' => feature_flag_response,
|
|
303
|
+
'locally_evaluated' => flag_was_locally_evaluated
|
|
304
|
+
}
|
|
305
|
+
properties['$feature_flag_request_id'] = request_id if request_id
|
|
306
|
+
properties['$feature_flag_evaluated_at'] = evaluated_at if evaluated_at
|
|
307
|
+
|
|
299
308
|
capture(
|
|
300
309
|
{
|
|
301
310
|
distinct_id: distinct_id,
|
|
302
311
|
event: '$feature_flag_called',
|
|
303
|
-
properties:
|
|
304
|
-
'$feature_flag' => key,
|
|
305
|
-
'$feature_flag_response' => feature_flag_response,
|
|
306
|
-
'locally_evaluated' => flag_was_locally_evaluated
|
|
307
|
-
}.merge(request_id ? { '$feature_flag_request_id' => request_id } : {}),
|
|
312
|
+
properties: properties,
|
|
308
313
|
groups: groups
|
|
309
314
|
}
|
|
310
315
|
)
|
|
@@ -385,7 +390,9 @@ module PostHog
|
|
|
385
390
|
distinct_id, groups, person_properties, group_properties, only_evaluate_locally
|
|
386
391
|
)
|
|
387
392
|
|
|
388
|
-
|
|
393
|
+
# Remove internal information
|
|
394
|
+
response.delete(:requestId)
|
|
395
|
+
response.delete(:evaluatedAt)
|
|
389
396
|
response
|
|
390
397
|
end
|
|
391
398
|
|
|
@@ -43,6 +43,7 @@ module PostHog
|
|
|
43
43
|
@feature_flag_request_timeout_seconds = feature_flag_request_timeout_seconds
|
|
44
44
|
@on_error = on_error || proc { |status, error| }
|
|
45
45
|
@quota_limited = Concurrent::AtomicBoolean.new(false)
|
|
46
|
+
@flags_etag = Concurrent::AtomicReference.new(nil)
|
|
46
47
|
@task =
|
|
47
48
|
Concurrent::TimerTask.new(
|
|
48
49
|
execution_interval: polling_interval
|
|
@@ -140,6 +141,7 @@ module PostHog
|
|
|
140
141
|
[key, FeatureFlag.from_value_and_payload(key, value, flags_response[:featureFlagPayloads][key])]
|
|
141
142
|
end
|
|
142
143
|
end
|
|
144
|
+
|
|
143
145
|
flags_response
|
|
144
146
|
end
|
|
145
147
|
|
|
@@ -190,6 +192,7 @@ module PostHog
|
|
|
190
192
|
flag_was_locally_evaluated = !response.nil?
|
|
191
193
|
|
|
192
194
|
request_id = nil
|
|
195
|
+
evaluated_at = nil
|
|
193
196
|
|
|
194
197
|
if !flag_was_locally_evaluated && !only_evaluate_locally
|
|
195
198
|
begin
|
|
@@ -198,6 +201,7 @@ module PostHog
|
|
|
198
201
|
if flags_data.key?(:featureFlags)
|
|
199
202
|
flags = stringify_keys(flags_data[:featureFlags] || {})
|
|
200
203
|
request_id = flags_data[:requestId]
|
|
204
|
+
evaluated_at = flags_data[:evaluatedAt]
|
|
201
205
|
else
|
|
202
206
|
logger.debug "Missing feature flags key: #{flags_data.to_json}"
|
|
203
207
|
flags = {}
|
|
@@ -211,7 +215,7 @@ module PostHog
|
|
|
211
215
|
end
|
|
212
216
|
end
|
|
213
217
|
|
|
214
|
-
[response, flag_was_locally_evaluated, request_id]
|
|
218
|
+
[response, flag_was_locally_evaluated, request_id, evaluated_at]
|
|
215
219
|
end
|
|
216
220
|
|
|
217
221
|
def get_all_flags(
|
|
@@ -252,6 +256,7 @@ module PostHog
|
|
|
252
256
|
payloads = {}
|
|
253
257
|
fallback_to_server = @feature_flags.empty?
|
|
254
258
|
request_id = nil # Only for /flags requests
|
|
259
|
+
evaluated_at = nil # Only for /flags requests
|
|
255
260
|
|
|
256
261
|
@feature_flags.each do |flag|
|
|
257
262
|
match_value = _compute_flag_locally(flag, distinct_id, groups, person_properties, group_properties)
|
|
@@ -282,6 +287,7 @@ module PostHog
|
|
|
282
287
|
flags = stringify_keys(flags_and_payloads[:featureFlags] || {})
|
|
283
288
|
payloads = stringify_keys(flags_and_payloads[:featureFlagPayloads] || {})
|
|
284
289
|
request_id = flags_and_payloads[:requestId]
|
|
290
|
+
evaluated_at = flags_and_payloads[:evaluatedAt]
|
|
285
291
|
end
|
|
286
292
|
rescue StandardError => e
|
|
287
293
|
@on_error.call(-1, "Error computing flag remotely: #{e}")
|
|
@@ -292,7 +298,8 @@ module PostHog
|
|
|
292
298
|
{
|
|
293
299
|
featureFlags: flags,
|
|
294
300
|
featureFlagPayloads: payloads,
|
|
295
|
-
requestId: request_id
|
|
301
|
+
requestId: request_id,
|
|
302
|
+
evaluatedAt: evaluated_at
|
|
296
303
|
}
|
|
297
304
|
end
|
|
298
305
|
|
|
@@ -834,12 +841,20 @@ module PostHog
|
|
|
834
841
|
|
|
835
842
|
def _load_feature_flags
|
|
836
843
|
begin
|
|
837
|
-
res = _request_feature_flag_definitions
|
|
844
|
+
res = _request_feature_flag_definitions(etag: @flags_etag.value)
|
|
838
845
|
rescue StandardError => e
|
|
839
846
|
@on_error.call(-1, e.to_s)
|
|
840
847
|
return
|
|
841
848
|
end
|
|
842
849
|
|
|
850
|
+
# Handle 304 Not Modified - flags haven't changed, skip processing
|
|
851
|
+
# Only update ETag if the 304 response includes one
|
|
852
|
+
if res[:not_modified]
|
|
853
|
+
@flags_etag.value = res[:etag] if res[:etag]
|
|
854
|
+
logger.debug '[FEATURE FLAGS] Flags not modified (304), using cached data'
|
|
855
|
+
return
|
|
856
|
+
end
|
|
857
|
+
|
|
843
858
|
# Handle quota limits with 402 status
|
|
844
859
|
if res.is_a?(Hash) && res[:status] == 402
|
|
845
860
|
logger.warn(
|
|
@@ -856,6 +871,9 @@ module PostHog
|
|
|
856
871
|
end
|
|
857
872
|
|
|
858
873
|
if res.key?(:flags)
|
|
874
|
+
# Only update ETag on successful responses with flag data
|
|
875
|
+
@flags_etag.value = res[:etag]
|
|
876
|
+
|
|
859
877
|
@feature_flags = res[:flags] || []
|
|
860
878
|
@feature_flags_by_key = {}
|
|
861
879
|
@feature_flags.each do |flag|
|
|
@@ -871,13 +889,14 @@ module PostHog
|
|
|
871
889
|
end
|
|
872
890
|
end
|
|
873
891
|
|
|
874
|
-
def _request_feature_flag_definitions
|
|
892
|
+
def _request_feature_flag_definitions(etag: nil)
|
|
875
893
|
uri = URI("#{@host}/api/feature_flag/local_evaluation")
|
|
876
894
|
uri.query = URI.encode_www_form([['token', @project_api_key], %w[send_cohorts true]])
|
|
877
895
|
req = Net::HTTP::Get.new(uri)
|
|
878
896
|
req['Authorization'] = "Bearer #{@personal_api_key}"
|
|
897
|
+
req['If-None-Match'] = etag if etag
|
|
879
898
|
|
|
880
|
-
_request(uri, req)
|
|
899
|
+
_request(uri, req, nil, include_etag: true)
|
|
881
900
|
end
|
|
882
901
|
|
|
883
902
|
def _request_feature_flag_evaluation(data = {})
|
|
@@ -901,7 +920,7 @@ module PostHog
|
|
|
901
920
|
end
|
|
902
921
|
|
|
903
922
|
# rubocop:disable Lint/ShadowedException
|
|
904
|
-
def _request(uri, request_object, timeout = nil)
|
|
923
|
+
def _request(uri, request_object, timeout = nil, include_etag: false)
|
|
905
924
|
request_object['User-Agent'] = "posthog-ruby#{PostHog::VERSION}"
|
|
906
925
|
request_timeout = timeout || 10
|
|
907
926
|
|
|
@@ -913,16 +932,28 @@ module PostHog
|
|
|
913
932
|
read_timeout: request_timeout
|
|
914
933
|
) do |http|
|
|
915
934
|
res = http.request(request_object)
|
|
935
|
+
status_code = res.code.to_i
|
|
936
|
+
etag = include_etag ? res['ETag'] : nil
|
|
937
|
+
|
|
938
|
+
# Handle 304 Not Modified - return special response indicating no change
|
|
939
|
+
if status_code == 304
|
|
940
|
+
logger.debug("#{request_object.method} #{_mask_tokens_in_url(uri.to_s)} returned 304 Not Modified")
|
|
941
|
+
return { not_modified: true, etag: etag, status: status_code }
|
|
942
|
+
end
|
|
916
943
|
|
|
917
944
|
# Parse response body to hash
|
|
918
945
|
begin
|
|
919
946
|
response = JSON.parse(res.body, { symbolize_names: true })
|
|
920
|
-
# Only add status if response is a hash
|
|
921
|
-
|
|
947
|
+
# Only add status (and etag if requested) if response is a hash
|
|
948
|
+
extra_fields = { status: status_code }
|
|
949
|
+
extra_fields[:etag] = etag if include_etag
|
|
950
|
+
response = response.merge(extra_fields) if response.is_a?(Hash)
|
|
922
951
|
return response
|
|
923
952
|
rescue JSON::ParserError
|
|
924
953
|
# Handle case when response isn't valid JSON
|
|
925
|
-
|
|
954
|
+
error_response = { error: 'Invalid JSON response', body: res.body, status: status_code }
|
|
955
|
+
error_response[:etag] = etag if include_etag
|
|
956
|
+
return error_response
|
|
926
957
|
end
|
|
927
958
|
end
|
|
928
959
|
rescue Timeout::Error,
|
|
@@ -939,5 +970,9 @@ module PostHog
|
|
|
939
970
|
end
|
|
940
971
|
end
|
|
941
972
|
# rubocop:enable Lint/ShadowedException
|
|
973
|
+
|
|
974
|
+
def _mask_tokens_in_url(url)
|
|
975
|
+
url.gsub(/token=([^&]{10})[^&]*/, 'token=\1...')
|
|
976
|
+
end
|
|
942
977
|
end
|
|
943
978
|
end
|
data/lib/posthog/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +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.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ''
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2025-
|
|
10
|
+
date: 2025-12-04 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: concurrent-ruby
|