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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57e103be9e2b8f115f96e948f0e82fd138b3c9ec9a7fe6fb1e1cff2e0c29b658
4
- data.tar.gz: c275ab93b048fda6bd2e5fa65c8e1955296536b6a281405974db2460b941a626
3
+ metadata.gz: 987f48b8d0cfa660cf9fc2d53f7da5d7e59150b774fc8403c4b65b94873b577d
4
+ data.tar.gz: 84402895313a99ebbf865e50ab06b5ee9876510e4c621b206a521dde8d371bc7
5
5
  SHA512:
6
- metadata.gz: 05cfc7dc4717b287287a37acd50680db1805ef41e42524feaabfd1b3113d96a1d5a90873b453f257e568ff75ce2d6a0e8db4ee82e26210ac280c628970bbed79
7
- data.tar.gz: 7ca3acb35caa156e76b06bec03d13494b54a06fb6b727a6601c19d6cbb2e04437bb7873cb3e1e9534303042b84a34bbf35deba36e243bc843a7a89baeef278ac
6
+ metadata.gz: e34a38c0b8386e95ab5fde0fad897f4bbccacf727cd72c7a72b3ec832977b460055b2ea35d2ab670e65bc916feb84f7d00e8bfa7eaee6e451ae1e6a156e6471f
7
+ data.tar.gz: d338821ad6066df5a7036421d62b2de806f07fcb266cd695f87f605084b6644e96e954c90ab3395fcc2d246642d6e8055be04c6e6af15b90a730290156733c11
@@ -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.get_feature_variants(attrs[:distinct_id], attrs[:groups])
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
- # make sure they're loaded on first run
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
- response = {}
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
- response[flag[:key]] = _compute_flag_locally(flag, distinct_id, groups, person_properties, group_properties)
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
- flags = get_feature_variants(distinct_id, groups, person_properties, group_properties)
143
- response = {**response, **flags}
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=2")
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
@@ -1,3 +1,3 @@
1
1
  class PostHog
2
- VERSION = '2.2.0'
2
+ VERSION = '2.3.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.2.0
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-02-08 00:00:00.000000000 Z
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.6
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: []