posthog-ruby 2.7.2 → 2.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7f2eecec568e40b102f14429a40372d2d3072bb4ad327c28c2f31d7197cf14ff
4
- data.tar.gz: 66383a06e85a0e7447e4a46eeebe54e8d897ca5d9d0211cb112653de6e0ec179
3
+ metadata.gz: 403175c4385fd299bfb3288ec55ea218b5108efba7a2096574be5d57db3ad828
4
+ data.tar.gz: 31283de954af3bc6624c90ea5975df2451c901bd8a198f782938d74e73f7d6a5
5
5
  SHA512:
6
- metadata.gz: c84276acbad7b6e0d10ee022bb7eb0b43e70623c0538954a377a6735349c88f56cb3400c04071e59c701685e923430a4175181da8bee09502fc425fcdb5496aa
7
- data.tar.gz: 399eaeaaad63a6a36217e00673c08be3c88cbc2782d19ad1e60339808e1da00ebee2f24351dd15d81c2e3aefb423163b19da83f7b08197b98570422723391c98
6
+ metadata.gz: 9c566381334794edd10f75f20b28fc804cb18f950cb02e8fd381b39f7453b83af372c642e9fea14c17e204c50141ed49cf4a4e3d15057e6878d3460471b7a9ed
7
+ data.tar.gz: 761aba06f50b414406abfd8a40b00403e8002184406b13fbd1c728e03fb3379a995864da24931fd0bd6bb7227be07b1850eb88cce81fd54321d0bb1014d231cc
@@ -0,0 +1,15 @@
1
+ error() {
2
+ echo "$@" >&2
3
+ }
4
+
5
+ fatal() {
6
+ error "$@"
7
+ exit 1
8
+ }
9
+
10
+ set_source_and_root_dir() {
11
+ { set +x; } 2>/dev/null
12
+ source_dir="$( cd -P "$( dirname "$0" )" >/dev/null 2>&1 && pwd )"
13
+ root_dir=$(cd $source_dir && cd ../ && pwd)
14
+ cd $root_dir
15
+ }
data/bin/test ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env bash
2
+ #/ Usage: bin/test [<configuration>]
3
+ #/ Description: Runs all the rspec tests.
4
+ source bin/helpers/_utils.sh
5
+ set_source_and_root_dir
6
+
7
+ bundle exec rspec
@@ -183,7 +183,7 @@ class PostHog
183
183
  # ```
184
184
  def get_feature_flag(key, distinct_id, groups: {}, person_properties: {}, group_properties: {}, only_evaluate_locally: false, send_feature_flag_events: true)
185
185
  person_properties, group_properties = add_local_person_and_group_properties(distinct_id, groups, person_properties, group_properties)
186
- feature_flag_response, flag_was_locally_evaluated = @feature_flags_poller.get_feature_flag(key, distinct_id, groups, person_properties, group_properties, only_evaluate_locally)
186
+ feature_flag_response, flag_was_locally_evaluated, request_id = @feature_flags_poller.get_feature_flag(key, distinct_id, groups, person_properties, group_properties, only_evaluate_locally)
187
187
 
188
188
  feature_flag_reported_key = "#{key}_#{feature_flag_response}"
189
189
  if !@distinct_id_has_sent_flag_calls[distinct_id].include?(feature_flag_reported_key) && send_feature_flag_events
@@ -195,7 +195,7 @@ class PostHog
195
195
  '$feature_flag' => key,
196
196
  '$feature_flag_response' => feature_flag_response,
197
197
  'locally_evaluated' => flag_was_locally_evaluated
198
- },
198
+ }.merge(request_id ? {'$feature_flag_request_id' => request_id} : {}),
199
199
  'groups': groups,
200
200
  }
201
201
  )
@@ -233,6 +233,10 @@ class PostHog
233
233
  end
234
234
 
235
235
  # Returns all flags and payloads for a given user
236
+ #
237
+ # @return [Hash] A hash with the following keys:
238
+ # featureFlags: A hash of feature flags
239
+ # featureFlagPayloads: A hash of feature flag payloads
236
240
  #
237
241
  # @param [String] distinct_id The distinct id of the user
238
242
  # @option [Hash] groups
@@ -242,7 +246,9 @@ class PostHog
242
246
  #
243
247
  def get_all_flags_and_payloads(distinct_id, groups: {}, person_properties: {}, group_properties: {}, only_evaluate_locally: false)
244
248
  person_properties, group_properties = add_local_person_and_group_properties(distinct_id, groups, person_properties, group_properties)
245
- @feature_flags_poller.get_all_flags_and_payloads(distinct_id, groups, person_properties, group_properties, only_evaluate_locally)
249
+ response = @feature_flags_poller.get_all_flags_and_payloads(distinct_id, groups, person_properties, group_properties, only_evaluate_locally)
250
+ response.delete(:requestId) # remove internal information.
251
+ response
246
252
  end
247
253
 
248
254
  def reload_feature_flags
@@ -0,0 +1,61 @@
1
+ # Represents a feature flag returned by /decide v4
2
+ class FeatureFlag
3
+ attr_reader :key, :enabled, :variant, :reason, :metadata
4
+
5
+ def initialize(json)
6
+ json.transform_keys!(&:to_s)
7
+ @key = json["key"]
8
+ @enabled = json["enabled"]
9
+ @variant = json["variant"]
10
+ @reason = json["reason"] ? EvaluationReason.new(json["reason"]) : nil
11
+ @metadata = json["metadata"] ? FeatureFlagMetadata.new(json["metadata"].transform_keys(&:to_s)) : nil
12
+ end
13
+
14
+ def get_value
15
+ @variant || @enabled
16
+ end
17
+
18
+ def payload
19
+ @metadata&.payload
20
+ end
21
+
22
+ def self.from_value_and_payload(key, value, payload)
23
+ new({
24
+ "key" => key,
25
+ "enabled" => value.is_a?(String) ? true : value,
26
+ "variant" => value.is_a?(String) ? value : nil,
27
+ "reason" => nil,
28
+ "metadata" => {
29
+ "id" => nil,
30
+ "version" => nil,
31
+ "payload" => payload,
32
+ "description" => nil
33
+ }
34
+ })
35
+ end
36
+ end
37
+
38
+ # Represents the reason why a flag was enabled/disabled
39
+ class EvaluationReason
40
+ attr_reader :code, :description, :condition_index
41
+
42
+ def initialize(json)
43
+ json.transform_keys!(&:to_s)
44
+ @code = json["code"]
45
+ @description = json["description"]
46
+ @condition_index = json["condition_index"]
47
+ end
48
+ end
49
+
50
+ # Represents metadata about a feature flag
51
+ class FeatureFlagMetadata
52
+ attr_reader :id, :version, :payload, :description
53
+
54
+ def initialize(json)
55
+ json.transform_keys!(&:to_s)
56
+ @id = json["id"]
57
+ @version = json["version"]
58
+ @payload = json["payload"]
59
+ @description = json["description"]
60
+ end
61
+ end
@@ -3,6 +3,7 @@ require 'net/http'
3
3
  require 'json'
4
4
  require 'posthog/version'
5
5
  require 'posthog/logging'
6
+ require 'posthog/feature_flag'
6
7
  require 'digest'
7
8
 
8
9
  class PostHog
@@ -77,7 +78,26 @@ class PostHog
77
78
  "group_properties": group_properties,
78
79
  }
79
80
 
80
- decide_data = _request_feature_flag_evaluation(request_data)
81
+ decide_response = _request_feature_flag_evaluation(request_data)
82
+
83
+ # Only normalize if we have flags in the response
84
+ if decide_response[:flags]
85
+ #v4 format
86
+ flags_hash = decide_response[:flags].transform_values do |flag|
87
+ FeatureFlag.new(flag)
88
+ end
89
+ decide_response[:flags] = flags_hash
90
+ decide_response[:featureFlags] = flags_hash.transform_values(&:get_value).transform_keys(&:to_sym)
91
+ decide_response[:featureFlagPayloads] = flags_hash.transform_values(&:payload).transform_keys(&:to_sym)
92
+ elsif decide_response[:featureFlags]
93
+ #v3 format
94
+ decide_response[:featureFlags] = decide_response[:featureFlags] || {}
95
+ decide_response[:featureFlagPayloads] = decide_response[:featureFlagPayloads] || {}
96
+ decide_response[:flags] = decide_response[:featureFlags].map do |key, value|
97
+ [key, FeatureFlag.from_value_and_payload(key, value, decide_response[:featureFlagPayloads][key])]
98
+ end.to_h
99
+ end
100
+ decide_response
81
101
  end
82
102
 
83
103
  def get_remote_config_payload(flag_key)
@@ -119,9 +139,19 @@ class PostHog
119
139
 
120
140
  flag_was_locally_evaluated = !response.nil?
121
141
 
142
+ request_id = nil
143
+
122
144
  if !flag_was_locally_evaluated && !only_evaluate_locally
123
145
  begin
124
- flags = get_feature_variants(distinct_id, groups, person_properties, group_properties, true)
146
+ decide_data = get_all_flags_and_payloads(distinct_id, groups, person_properties, group_properties, false, true)
147
+ if !decide_data.key?(:featureFlags)
148
+ logger.debug "Missing feature flags key: #{decide_data.to_json}"
149
+ flags = {}
150
+ else
151
+ flags = stringify_keys(decide_data[:featureFlags] || {})
152
+ request_id = decide_data[:requestId]
153
+ end
154
+
125
155
  response = flags[key]
126
156
  if response.nil?
127
157
  response = false
@@ -132,7 +162,7 @@ class PostHog
132
162
  end
133
163
  end
134
164
 
135
- [response, flag_was_locally_evaluated]
165
+ [response, flag_was_locally_evaluated, request_id]
136
166
  end
137
167
 
138
168
  def get_all_flags(distinct_id, groups = {}, person_properties = {}, group_properties = {}, only_evaluate_locally = false)
@@ -142,7 +172,7 @@ class PostHog
142
172
  end
143
173
  # returns a string hash of all flags
144
174
  response = get_all_flags_and_payloads(distinct_id, groups, person_properties, group_properties, only_evaluate_locally)
145
- flags = response[:featureFlags]
175
+ response[:featureFlags]
146
176
  end
147
177
 
148
178
  def get_all_flags_and_payloads(distinct_id, groups = {}, person_properties = {}, group_properties = {}, only_evaluate_locally = false, raise_on_error = false)
@@ -151,6 +181,7 @@ class PostHog
151
181
  flags = {}
152
182
  payloads = {}
153
183
  fallback_to_decide = @feature_flags.empty?
184
+ request_id = nil # Only for /decide requests
154
185
 
155
186
  @feature_flags.each do |flag|
156
187
  begin
@@ -183,13 +214,14 @@ class PostHog
183
214
  else
184
215
  flags = stringify_keys(flags_and_payloads[:featureFlags] || {})
185
216
  payloads = stringify_keys(flags_and_payloads[:featureFlagPayloads] || {})
217
+ request_id = flags_and_payloads[:requestId]
186
218
  end
187
219
  rescue StandardError => e
188
220
  @on_error.call(-1, "Error computing flag remotely: #{e}")
189
221
  raise if raise_on_error
190
222
  end
191
223
  end
192
- {"featureFlags": flags, "featureFlagPayloads": payloads}
224
+ {"featureFlags": flags, "featureFlagPayloads": payloads, "requestId": request_id}
193
225
  end
194
226
 
195
227
  def get_feature_flag_payload(key, distinct_id, match_value = nil, groups = {}, person_properties = {}, group_properties = {}, only_evaluate_locally = false)
@@ -534,7 +566,7 @@ class PostHog
534
566
  end
535
567
 
536
568
  def _request_feature_flag_evaluation(data={})
537
- uri = URI("#{@host}/decide/?v=3")
569
+ uri = URI("#{@host}/decide/?v=4")
538
570
  req = Net::HTTP::Post.new(uri)
539
571
  req['Content-Type'] = 'application/json'
540
572
  data['token'] = @project_api_key
@@ -1,3 +1,3 @@
1
1
  class PostHog
2
- VERSION = '2.7.2'
2
+ VERSION = '2.8.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: posthog-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.2
4
+ version: 2.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-03-14 00:00:00.000000000 Z
10
+ date: 2025-04-07 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: concurrent-ruby
@@ -143,12 +142,15 @@ executables:
143
142
  extensions: []
144
143
  extra_rdoc_files: []
145
144
  files:
145
+ - bin/helpers/_utils.sh
146
146
  - bin/posthog
147
+ - bin/test
147
148
  - lib/posthog-ruby.rb
148
149
  - lib/posthog.rb
149
150
  - lib/posthog/backoff_policy.rb
150
151
  - lib/posthog/client.rb
151
152
  - lib/posthog/defaults.rb
153
+ - lib/posthog/feature_flag.rb
152
154
  - lib/posthog/feature_flags.rb
153
155
  - lib/posthog/field_parser.rb
154
156
  - lib/posthog/logging.rb
@@ -163,7 +165,6 @@ homepage: https://github.com/PostHog/posthog-ruby
163
165
  licenses:
164
166
  - MIT
165
167
  metadata: {}
166
- post_install_message:
167
168
  rdoc_options: []
168
169
  require_paths:
169
170
  - lib
@@ -178,8 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
179
  - !ruby/object:Gem::Version
179
180
  version: '0'
180
181
  requirements: []
181
- rubygems_version: 3.0.3.1
182
- signing_key:
182
+ rubygems_version: 3.6.6
183
183
  specification_version: 4
184
184
  summary: PostHog library
185
185
  test_files: []