posthog-ruby 2.2.0 → 2.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/posthog/client.rb +29 -1
- data/lib/posthog/feature_flags.rb +85 -18
- data/lib/posthog/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 987f48b8d0cfa660cf9fc2d53f7da5d7e59150b774fc8403c4b65b94873b577d
|
4
|
+
data.tar.gz: 84402895313a99ebbf865e50ab06b5ee9876510e4c621b206a521dde8d371bc7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e34a38c0b8386e95ab5fde0fad897f4bbccacf727cd72c7a72b3ec832977b460055b2ea35d2ab670e65bc916feb84f7d00e8bfa7eaee6e451ae1e6a156e6471f
|
7
|
+
data.tar.gz: d338821ad6066df5a7036421d62b2de806f07fcb266cd695f87f605084b6644e96e954c90ab3395fcc2d246642d6e8055be04c6e6af15b90a730290156733c11
|
data/lib/posthog/client.rb
CHANGED
@@ -15,6 +15,7 @@ class PostHog
|
|
15
15
|
|
16
16
|
# @param [Hash] opts
|
17
17
|
# @option opts [String] :api_key Your project's api_key
|
18
|
+
# @option opts [String] :personal_api_key Your personal API key
|
18
19
|
# @option opts [FixNum] :max_queue_size Maximum number of calls to be
|
19
20
|
# remain queued. Defaults to 10_000.
|
20
21
|
# @option opts [Bool] :test_mode +true+ if messages should remain
|
@@ -91,7 +92,8 @@ class PostHog
|
|
91
92
|
symbolize_keys! attrs
|
92
93
|
|
93
94
|
if attrs[:send_feature_flags]
|
94
|
-
feature_variants = @feature_flags_poller.
|
95
|
+
feature_variants = @feature_flags_poller._get_active_feature_variants(attrs[:distinct_id], attrs[:groups])
|
96
|
+
|
95
97
|
attrs[:feature_variants] = feature_variants
|
96
98
|
end
|
97
99
|
|
@@ -205,6 +207,32 @@ class PostHog
|
|
205
207
|
return @feature_flags_poller.get_all_flags(distinct_id, groups, person_properties, group_properties, only_evaluate_locally)
|
206
208
|
end
|
207
209
|
|
210
|
+
# Returns payload for a given feature flag
|
211
|
+
#
|
212
|
+
# @param [String] key The key of the feature flag
|
213
|
+
# @param [String] distinct_id The distinct id of the user
|
214
|
+
# @option [String or boolean] match_value The value of the feature flag to be matched
|
215
|
+
# @option [Hash] groups
|
216
|
+
# @option [Hash] person_properties key-value pairs of properties to associate with the user.
|
217
|
+
# @option [Hash] group_properties
|
218
|
+
# @option [Boolean] only_evaluate_locally
|
219
|
+
#
|
220
|
+
def get_feature_flag_payload(key, distinct_id, match_value: nil, groups: {}, person_properties: {}, group_properties: {}, only_evaluate_locally: false)
|
221
|
+
@feature_flags_poller.get_feature_flag_payload(key, distinct_id, match_value, groups, person_properties, group_properties, only_evaluate_locally)
|
222
|
+
end
|
223
|
+
|
224
|
+
# Returns all flags and payloads for a given user
|
225
|
+
#
|
226
|
+
# @param [String] distinct_id The distinct id of the user
|
227
|
+
# @option [Hash] groups
|
228
|
+
# @option [Hash] person_properties key-value pairs of properties to associate with the user.
|
229
|
+
# @option [Hash] group_properties
|
230
|
+
# @option [Boolean] only_evaluate_locally
|
231
|
+
#
|
232
|
+
def get_all_flags_and_payloads(distinct_id, groups: {}, person_properties: {}, group_properties: {}, only_evaluate_locally: false)
|
233
|
+
@feature_flags_poller.get_all_flags_and_payloads(distinct_id, groups, person_properties, group_properties, only_evaluate_locally)
|
234
|
+
end
|
235
|
+
|
208
236
|
def reload_feature_flags
|
209
237
|
unless @personal_api_key
|
210
238
|
logger.error(
|
@@ -10,9 +10,6 @@ class PostHog
|
|
10
10
|
class InconclusiveMatchError < StandardError
|
11
11
|
end
|
12
12
|
|
13
|
-
class DecideAPIError < StandardError
|
14
|
-
end
|
15
|
-
|
16
13
|
class FeatureFlagsPoller
|
17
14
|
include PostHog::Logging
|
18
15
|
include PostHog::Utils
|
@@ -25,6 +22,7 @@ class PostHog
|
|
25
22
|
@feature_flags = Concurrent::Array.new
|
26
23
|
@group_type_mapping = Concurrent::Hash.new
|
27
24
|
@loaded_flags_successfully_once = Concurrent::AtomicBoolean.new
|
25
|
+
@feature_flags_by_key = nil
|
28
26
|
|
29
27
|
@task =
|
30
28
|
Concurrent::TimerTask.new(
|
@@ -49,7 +47,35 @@ class PostHog
|
|
49
47
|
end
|
50
48
|
|
51
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
|
+
if !decide_data.key?(:featureFlags)
|
52
|
+
logger.error "Missing feature flags key: #{decide_data.to_json}"
|
53
|
+
else
|
54
|
+
stringify_keys(decide_data[:featureFlags] || {})
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
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
|
+
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)
|
71
|
+
if !decide_data.key?(:featureFlagPayloads)
|
72
|
+
logger.error "Missing feature flag payloads key: #{decide_data.to_json}"
|
73
|
+
else
|
74
|
+
stringify_keys(decide_data[:featureFlagPayloads] || {})
|
75
|
+
end
|
76
|
+
end
|
52
77
|
|
78
|
+
def get_decide(distinct_id, groups={}, person_properties={}, group_properties={})
|
53
79
|
request_data = {
|
54
80
|
"distinct_id": distinct_id,
|
55
81
|
"groups": groups,
|
@@ -58,12 +84,6 @@ class PostHog
|
|
58
84
|
}
|
59
85
|
|
60
86
|
decide_data = _request_feature_flag_evaluation(request_data)
|
61
|
-
|
62
|
-
if !decide_data.key?(:featureFlags)
|
63
|
-
raise DecideAPIError.new(decide_data.to_json)
|
64
|
-
else
|
65
|
-
stringify_keys(decide_data[:featureFlags] || {})
|
66
|
-
end
|
67
87
|
end
|
68
88
|
|
69
89
|
def get_feature_flag(key, distinct_id, groups = {}, person_properties = {}, group_properties = {}, only_evaluate_locally = false)
|
@@ -119,16 +139,25 @@ class PostHog
|
|
119
139
|
|
120
140
|
def get_all_flags(distinct_id, groups = {}, person_properties = {}, group_properties = {}, only_evaluate_locally = false)
|
121
141
|
# returns a string hash of all flags
|
142
|
+
response = get_all_flags_and_payloads(distinct_id, groups, person_properties, group_properties, only_evaluate_locally)
|
143
|
+
flags = response[:featureFlags]
|
144
|
+
end
|
122
145
|
|
123
|
-
|
146
|
+
def get_all_flags_and_payloads(distinct_id, groups = {}, person_properties = {}, group_properties = {}, only_evaluate_locally = false)
|
124
147
|
load_feature_flags
|
125
148
|
|
126
|
-
|
149
|
+
flags = {}
|
150
|
+
payloads = {}
|
127
151
|
fallback_to_decide = @feature_flags.empty?
|
128
152
|
|
129
153
|
@feature_flags.each do |flag|
|
130
154
|
begin
|
131
|
-
|
155
|
+
match_value = _compute_flag_locally(flag, distinct_id, groups, person_properties, group_properties)
|
156
|
+
flags[flag[:key]] = match_value
|
157
|
+
match_payload = _compute_flag_payload_locally(flag[:key], match_value)
|
158
|
+
if match_payload
|
159
|
+
payloads[flag[:key]] = match_payload
|
160
|
+
end
|
132
161
|
rescue InconclusiveMatchError => e
|
133
162
|
fallback_to_decide = true
|
134
163
|
rescue StandardError => e
|
@@ -136,15 +165,37 @@ class PostHog
|
|
136
165
|
fallback_to_decide = true
|
137
166
|
end
|
138
167
|
end
|
139
|
-
|
140
168
|
if fallback_to_decide && !only_evaluate_locally
|
141
169
|
begin
|
142
|
-
|
143
|
-
|
170
|
+
flags_and_payloads = get_decide(distinct_id, groups, person_properties, group_properties)
|
171
|
+
flags = stringify_keys(flags_and_payloads[:featureFlags] || {})
|
172
|
+
payloads = stringify_keys(flags_and_payloads[:featureFlagPayloads] || {})
|
144
173
|
rescue StandardError => e
|
145
174
|
logger.error "Error computing flag remotely: #{e}"
|
146
175
|
end
|
147
176
|
end
|
177
|
+
{"featureFlags": flags, "featureFlagPayloads": payloads}
|
178
|
+
end
|
179
|
+
|
180
|
+
def get_feature_flag_payload(key, distinct_id, match_value = nil, groups = {}, person_properties = {}, group_properties = {}, only_evaluate_locally = false)
|
181
|
+
if match_value == nil
|
182
|
+
match_value = get_feature_flag(
|
183
|
+
key,
|
184
|
+
distinct_id,
|
185
|
+
groups,
|
186
|
+
person_properties,
|
187
|
+
group_properties,
|
188
|
+
true,
|
189
|
+
)[0]
|
190
|
+
end
|
191
|
+
response = nil
|
192
|
+
if match_value != nil
|
193
|
+
response = _compute_flag_payload_locally(key, match_value)
|
194
|
+
end
|
195
|
+
if response == nil and !only_evaluate_locally
|
196
|
+
decide_payloads = get_feature_payloads(distinct_id, groups, person_properties, group_properties)
|
197
|
+
response = decide_payloads[key.downcase] || nil
|
198
|
+
end
|
148
199
|
response
|
149
200
|
end
|
150
201
|
|
@@ -248,6 +299,17 @@ class PostHog
|
|
248
299
|
|
249
300
|
end
|
250
301
|
|
302
|
+
def _compute_flag_payload_locally(key, match_value)
|
303
|
+
response = nil
|
304
|
+
|
305
|
+
if [true, false].include? match_value
|
306
|
+
response = @feature_flags_by_key.dig(key, :filters, :payloads, match_value.to_s.to_sym)
|
307
|
+
elsif match_value.is_a? String
|
308
|
+
response = @feature_flags_by_key.dig(key, :filters, :payloads, match_value.to_sym)
|
309
|
+
end
|
310
|
+
response
|
311
|
+
end
|
312
|
+
|
251
313
|
def match_feature_flag_properties(flag, distinct_id, properties)
|
252
314
|
flag_filters = flag[:filters] || {}
|
253
315
|
|
@@ -341,12 +403,17 @@ class PostHog
|
|
341
403
|
|
342
404
|
def _load_feature_flags()
|
343
405
|
res = _request_feature_flag_definitions
|
344
|
-
@feature_flags.clear
|
345
406
|
|
346
407
|
if !res.key?(:flags)
|
347
408
|
logger.error "Failed to load feature flags: #{res}"
|
348
409
|
else
|
349
|
-
@feature_flags = res[:flags] || []
|
410
|
+
@feature_flags = res[:flags] || []
|
411
|
+
@feature_flags_by_key = {}
|
412
|
+
@feature_flags.each do |flag|
|
413
|
+
if flag[:key] != nil
|
414
|
+
@feature_flags_by_key[flag[:key]] = flag
|
415
|
+
end
|
416
|
+
end
|
350
417
|
@group_type_mapping = res[:group_type_mapping] || {}
|
351
418
|
|
352
419
|
logger.debug "Loaded #{@feature_flags.length} feature flags"
|
@@ -365,7 +432,7 @@ class PostHog
|
|
365
432
|
end
|
366
433
|
|
367
434
|
def _request_feature_flag_evaluation(data={})
|
368
|
-
uri = URI("#{@host}/decide/?v=
|
435
|
+
uri = URI("#{@host}/decide/?v=3")
|
369
436
|
req = Net::HTTP::Post.new(uri)
|
370
437
|
req['Content-Type'] = 'application/json'
|
371
438
|
data['token'] = @project_api_key
|
data/lib/posthog/version.rb
CHANGED
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
|
+
version: 2.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ''
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-08-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -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.
|
182
|
-
signing_key:
|
181
|
+
rubygems_version: 3.1.2
|
182
|
+
signing_key:
|
183
183
|
specification_version: 4
|
184
184
|
summary: PostHog library
|
185
185
|
test_files: []
|