From 0d0d596dbb6e8ebbb30a8ff5c45fde1399e0904a Mon Sep 17 00:00:00 2001 From: Phil Haack Date: Wed, 15 Jul 2026 09:16:47 -0700 Subject: [PATCH 1/2] Add $feature_flag_has_experiment to $feature_flag_called events Parse the server-reported has_experiment field from /flags v2 metadata and /flags/definitions flag definitions, defaulting to false when the server does not send it (older deployments), and include it as a boolean property on every $feature_flag_called event across the single-flag, snapshot (evaluate_flags), local, and remote evaluation paths. --- .changeset/feature-flag-has-experiment.md | 5 ++ lib/posthog/client.rb | 13 ++- lib/posthog/feature_flag.rb | 5 +- lib/posthog/feature_flag_evaluations.rb | 3 +- lib/posthog/feature_flags.rb | 13 ++- public_api_snapshot.txt | 2 + spec/posthog/client_spec.rb | 10 ++- spec/posthog/feature_flag_evaluations_spec.rb | 71 +++++++++++++++- spec/posthog/feature_flag_spec.rb | 81 +++++++++++++++++++ spec/posthog/flags_spec.rb | 19 ++++- 10 files changed, 210 insertions(+), 12 deletions(-) create mode 100644 .changeset/feature-flag-has-experiment.md diff --git a/.changeset/feature-flag-has-experiment.md b/.changeset/feature-flag-has-experiment.md new file mode 100644 index 00000000..a203814d --- /dev/null +++ b/.changeset/feature-flag-has-experiment.md @@ -0,0 +1,5 @@ +--- +"posthog-ruby": minor +--- + +feat: add a `$feature_flag_has_experiment` boolean property to every `$feature_flag_called` event, sourced from the server-reported `has_experiment` field (defaults to false when the server does not report it) diff --git a/lib/posthog/client.rb b/lib/posthog/client.rb index e84755b7..2e04aab9 100644 --- a/lib/posthog/client.rb +++ b/lib/posthog/client.rb @@ -609,7 +609,8 @@ def evaluate_flags( id: definition[:id], version: nil, reason: FeatureFlagEvaluations::EVALUATED_LOCALLY_REASON, - locally_evaluated: true + locally_evaluated: true, + has_experiment: definition[:has_experiment] ? true : false ) locally_evaluated_keys << key.to_s end @@ -649,7 +650,8 @@ def evaluate_flags( id: metadata ? metadata.id : nil, version: metadata ? metadata.version : nil, reason: reason ? (reason.description || reason.code) : nil, - locally_evaluated: false + locally_evaluated: false, + has_experiment: metadata ? metadata.has_experiment : false ) end rescue StandardError => e @@ -766,6 +768,7 @@ def get_all_flags_and_payloads( # Remove internal information response.delete(:requestId) response.delete(:evaluatedAt) + response.delete(:flagDetails) response end @@ -921,7 +924,8 @@ def _get_feature_flag_result( person_properties, group_properties = add_local_person_and_group_properties( groups, person_properties, group_properties ) - feature_flag_response, flag_was_locally_evaluated, request_id, evaluated_at, feature_flag_error, payload = + feature_flag_response, flag_was_locally_evaluated, request_id, evaluated_at, feature_flag_error, payload, + has_experiment = @feature_flags_poller.get_feature_flag( key, distinct_id, groups, person_properties, group_properties, only_evaluate_locally ) @@ -929,7 +933,8 @@ def _get_feature_flag_result( properties = { '$feature_flag' => key, '$feature_flag_response' => feature_flag_response, - 'locally_evaluated' => flag_was_locally_evaluated + 'locally_evaluated' => flag_was_locally_evaluated, + '$feature_flag_has_experiment' => has_experiment ? true : false } properties['$feature_flag_request_id'] = request_id if request_id properties['$feature_flag_evaluated_at'] = evaluated_at if evaluated_at diff --git a/lib/posthog/feature_flag.rb b/lib/posthog/feature_flag.rb index 952158c8..0c68bc53 100644 --- a/lib/posthog/feature_flag.rb +++ b/lib/posthog/feature_flag.rb @@ -68,7 +68,7 @@ def initialize(json) # # @api private class FeatureFlagMetadata - attr_reader :id, :version, :payload, :description + attr_reader :id, :version, :payload, :description, :has_experiment # @param json [Hash] Raw metadata returned by /flags. def initialize(json) @@ -77,6 +77,9 @@ def initialize(json) @version = json['version'] @payload = json['payload'] @description = json['description'] + # Whether the flag is linked to an experiment. Defaults to false when the + # server (an older deployment) does not report the field. + @has_experiment = json['has_experiment'] ? true : false end end end diff --git a/lib/posthog/feature_flag_evaluations.rb b/lib/posthog/feature_flag_evaluations.rb index e4756773..b188811f 100644 --- a/lib/posthog/feature_flag_evaluations.rb +++ b/lib/posthog/feature_flag_evaluations.rb @@ -13,7 +13,7 @@ class FeatureFlagEvaluations EVALUATED_LOCALLY_REASON = 'Evaluated locally' EvaluatedFlagRecord = Struct.new( - :key, :enabled, :variant, :payload, :id, :version, :reason, :locally_evaluated, + :key, :enabled, :variant, :payload, :id, :version, :reason, :locally_evaluated, :has_experiment, keyword_init: true ) @@ -165,6 +165,7 @@ def _record_access(key, flag) '$feature_flag' => key, '$feature_flag_response' => response, 'locally_evaluated' => flag&.locally_evaluated ? true : false, + '$feature_flag_has_experiment' => flag&.has_experiment ? true : false, "$feature/#{key}" => response } diff --git a/lib/posthog/feature_flags.rb b/lib/posthog/feature_flags.rb index fb342121..a5e4d3bc 100644 --- a/lib/posthog/feature_flags.rb +++ b/lib/posthog/feature_flags.rb @@ -221,6 +221,11 @@ def get_feature_flag( end flag_was_locally_evaluated = !response.nil? + # Whether the flag is linked to an experiment, as reported by the server. + # Locally-evaluated flags carry it in the stored definition; remotely + # evaluated flags carry it in the response metadata. Defaults to false + # when the server (an older deployment) does not report it. + has_experiment = flag_was_locally_evaluated && feature_flag[:has_experiment] ? true : false request_id = nil evaluated_at = nil @@ -253,6 +258,9 @@ def get_feature_flag( payload = payloads[key] feature_flag_error = errors.join(',') unless errors.empty? + flag_detail = flags_data[:flagDetails]&.[](key.to_sym) + has_experiment = flag_detail&.metadata&.has_experiment ? true : false + logger.debug "Successfully computed flag remotely: #{key} -> #{response}" rescue Timeout::Error => e @on_error.call(-1, "Timeout while fetching flags remotely: #{e}") @@ -266,7 +274,7 @@ def get_feature_flag( end end - [response, flag_was_locally_evaluated, request_id, evaluated_at, feature_flag_error, payload] + [response, flag_was_locally_evaluated, request_id, evaluated_at, feature_flag_error, payload, has_experiment] end def get_all_flags( @@ -324,10 +332,12 @@ def get_all_flags_and_payloads( errors_while_computing = false quota_limited = nil status_code = nil + flag_details = nil if fallback_to_server && !only_evaluate_locally begin flags_and_payloads = get_flags(distinct_id, groups, person_properties, group_properties) + flag_details = flags_and_payloads[:flags] errors_while_computing = flags_and_payloads[:errorsWhileComputingFlags] || false quota_limited = flags_and_payloads[:quotaLimited] status_code = flags_and_payloads[:status] @@ -368,6 +378,7 @@ def get_all_flags_and_payloads( { featureFlags: flags, featureFlagPayloads: payloads, + flagDetails: flag_details, requestId: request_id, evaluatedAt: evaluated_at, errorsWhileComputingFlags: errors_while_computing, diff --git a/public_api_snapshot.txt b/public_api_snapshot.txt index d8e599dd..fb26974b 100644 --- a/public_api_snapshot.txt +++ b/public_api_snapshot.txt @@ -68,6 +68,8 @@ constant PostHog::FeatureFlagEvaluations::EVALUATED_LOCALLY_REASON: String class PostHog::FeatureFlagEvaluations::EvaluatedFlagRecord < Struct instance_method PostHog::FeatureFlagEvaluations::EvaluatedFlagRecord#enabled() instance_method PostHog::FeatureFlagEvaluations::EvaluatedFlagRecord#enabled=(_) +instance_method PostHog::FeatureFlagEvaluations::EvaluatedFlagRecord#has_experiment() +instance_method PostHog::FeatureFlagEvaluations::EvaluatedFlagRecord#has_experiment=(_) instance_method PostHog::FeatureFlagEvaluations::EvaluatedFlagRecord#id() instance_method PostHog::FeatureFlagEvaluations::EvaluatedFlagRecord#id=(_) instance_method PostHog::FeatureFlagEvaluations::EvaluatedFlagRecord#key() diff --git a/spec/posthog/client_spec.rb b/spec/posthog/client_spec.rb index 139ac992..1289e6f4 100644 --- a/spec/posthog/client_spec.rb +++ b/spec/posthog/client_spec.rb @@ -559,6 +559,7 @@ module PostHog expect(captured_message[:properties]).to eq( '$feature_flag' => 'beta-feature', '$feature_flag_response' => true, + '$feature_flag_has_experiment' => false, '$lib' => 'posthog-ruby', '$lib_version' => '1.2.4', '$is_server' => true, @@ -622,7 +623,8 @@ module PostHog properties: { '$feature_flag' => 'beta-feature', '$feature_flag_response' => true, - 'locally_evaluated' => true + 'locally_evaluated' => true, + '$feature_flag_has_experiment' => false }, groups: {} }).exactly(1).times @@ -646,7 +648,8 @@ module PostHog properties: { '$feature_flag' => 'beta-feature', '$feature_flag_response' => true, - 'locally_evaluated' => true + 'locally_evaluated' => true, + '$feature_flag_has_experiment' => false }, groups: {} }).exactly(1).times @@ -668,7 +671,8 @@ module PostHog properties: { '$feature_flag' => 'decide-flag', '$feature_flag_response' => 'decide-value', - 'locally_evaluated' => false + 'locally_evaluated' => false, + '$feature_flag_has_experiment' => false }, groups: { organization: 'org1' } }).exactly(1).times diff --git a/spec/posthog/feature_flag_evaluations_spec.rb b/spec/posthog/feature_flag_evaluations_spec.rb index 515d503c..a2facf9c 100644 --- a/spec/posthog/feature_flag_evaluations_spec.rb +++ b/spec/posthog/feature_flag_evaluations_spec.rb @@ -304,6 +304,56 @@ def capture_stderr end end + describe '$feature_flag_has_experiment' do + let(:has_experiment_response) do + { + flags: { + 'experiment-flag' => { + key: 'experiment-flag', enabled: true, variant: nil, + metadata: { id: 10, version: 1, has_experiment: true } + }, + 'plain-flag' => { + key: 'plain-flag', enabled: true, variant: nil, + metadata: { id: 11, version: 1, has_experiment: false } + }, + 'legacy-flag' => { + key: 'legacy-flag', enabled: true, variant: nil, + metadata: { id: 12, version: 1 } + } + }, + requestId: 'request-id-2' + } + end + + def flag_called_properties(client, key) + client.evaluate_flags('user-1').enabled?(key) + event = drain_messages(client).find do |m| + m[:event] == '$feature_flag_called' && m[:properties]['$feature_flag'] == key + end + event[:properties] + end + + it 'is true when the server reports has_experiment' do + stub_flags(has_experiment_response) + expect(flag_called_properties(client, 'experiment-flag')['$feature_flag_has_experiment']).to be(true) + end + + it 'is false when the server reports has_experiment false' do + stub_flags(has_experiment_response) + expect(flag_called_properties(client, 'plain-flag')['$feature_flag_has_experiment']).to be(false) + end + + it 'is false when the server omits has_experiment' do + stub_flags(has_experiment_response) + expect(flag_called_properties(client, 'legacy-flag')['$feature_flag_has_experiment']).to be(false) + end + + it 'is false for flags missing from the evaluation' do + stub_flags(has_experiment_response) + expect(flag_called_properties(client, 'not-a-flag')['$feature_flag_has_experiment']).to be(false) + end + end + describe 'response-level errors' do it 'combines errorsWhileComputingFlags with flag_missing on $feature_flag_error' do stub_flags(flags_response.merge(errorsWhileComputingFlags: true)) @@ -387,7 +437,11 @@ def capture_stderr { flags: [ { - id: 99, name: 'Local flag', key: 'local-flag', active: true, + id: 99, name: 'Local flag', key: 'local-flag', active: true, has_experiment: true, + filters: { groups: [{ properties: [], rollout_percentage: 100 }] } + }, + { + id: 100, name: 'Plain local flag', key: 'plain-local-flag', active: true, filters: { groups: [{ properties: [], rollout_percentage: 100 }] } } ] @@ -412,6 +466,21 @@ def capture_stderr expect(event[:properties]['$feature_flag_definitions_loaded_at']).to be_a(Integer) end + it 'sources $feature_flag_has_experiment from the local flag definition' do + stub_request(:get, %r{https://us\.i\.posthog\.com/flags/definitions}) + .to_return(status: 200, body: local_definitions.to_json) + c = Client.new(api_key: API_KEY, personal_api_key: API_KEY, test_mode: true) + snapshot = c.evaluate_flags('user-1', only_evaluate_locally: true) + + snapshot.enabled?('local-flag') + snapshot.enabled?('plain-local-flag') + + msgs = drain_messages(c).select { |m| m[:event] == '$feature_flag_called' } + by_key = msgs.to_h { |m| [m[:properties]['$feature_flag'], m[:properties]] } + expect(by_key['local-flag']['$feature_flag_has_experiment']).to be(true) + expect(by_key['plain-local-flag']['$feature_flag_has_experiment']).to be(false) + end + it 'skips the remote /flags call when flag_keys are all resolved locally' do stub_request(:get, %r{https://us\.i\.posthog\.com/flags/definitions}) .to_return(status: 200, body: local_definitions.to_json) diff --git a/spec/posthog/feature_flag_spec.rb b/spec/posthog/feature_flag_spec.rb index 93963091..674f2688 100644 --- a/spec/posthog/feature_flag_spec.rb +++ b/spec/posthog/feature_flag_spec.rb @@ -5267,5 +5267,86 @@ module PostHog expect(captured_message[:properties]['$feature_flag_evaluated_at']).to eq(1_704_067_200_000) expect(captured_message[:properties]['locally_evaluated']).to be false end + + describe '$feature_flag_has_experiment' do + def local_definition(has_experiment: nil) + flag = { + 'id' => 1, + 'name' => 'Beta Feature', + 'key' => 'test-flag', + 'active' => true, + 'filters' => { 'groups' => [{ 'rollout_percentage' => 100 }] } + } + flag['has_experiment'] = has_experiment unless has_experiment.nil? + { 'flags' => [flag] } + end + + def stub_definitions(body) + stub_request( + :get, + 'https://us.i.posthog.com/flags/definitions?token=testsecret&send_cohorts=true' + ).to_return(status: 200, body: body.to_json) + end + + def flag_called_properties(client, key) + client.get_feature_flag_result(key, 'some-distinct-id') + captured_message = client.dequeue_last_message + expect(captured_message[:event]).to eq('$feature_flag_called') + captured_message[:properties] + end + + it 'is true when the local definition reports has_experiment' do + stub_definitions(local_definition(has_experiment: true)) + stub_request(:post, flags_endpoint).to_return(status: 400) + c = Client.new(api_key: API_KEY, personal_api_key: API_KEY, test_mode: true) + + expect(flag_called_properties(c, 'test-flag')['$feature_flag_has_experiment']).to be true + end + + it 'is false when the local definition reports has_experiment false' do + stub_definitions(local_definition(has_experiment: false)) + stub_request(:post, flags_endpoint).to_return(status: 400) + c = Client.new(api_key: API_KEY, personal_api_key: API_KEY, test_mode: true) + + expect(flag_called_properties(c, 'test-flag')['$feature_flag_has_experiment']).to be false + end + + it 'is false when the local definition omits has_experiment' do + stub_definitions(local_definition) + stub_request(:post, flags_endpoint).to_return(status: 400) + c = Client.new(api_key: API_KEY, personal_api_key: API_KEY, test_mode: true) + + expect(flag_called_properties(c, 'test-flag')['$feature_flag_has_experiment']).to be false + end + + it 'sources has_experiment from the /flags response metadata on remote evaluation' do + stub_definitions('flags' => []) + stub_request(:post, flags_endpoint) + .to_return(status: 200, body: { + 'flags' => { + 'remote-flag' => { + 'key' => 'remote-flag', 'enabled' => true, 'variant' => nil, + 'metadata' => { 'id' => 7, 'version' => 3, 'has_experiment' => true } + } + }, + 'requestId' => 'test-request-id' + }.to_json) + c = Client.new(api_key: API_KEY, personal_api_key: API_KEY, test_mode: true) + + expect(flag_called_properties(c, 'remote-flag')['$feature_flag_has_experiment']).to be true + end + + it 'is false when the /flags response omits has_experiment' do + stub_definitions('flags' => []) + stub_request(:post, flags_endpoint) + .to_return(status: 200, body: { + 'featureFlags' => { 'remote-flag' => true }, + 'requestId' => 'test-request-id' + }.to_json) + c = Client.new(api_key: API_KEY, personal_api_key: API_KEY, test_mode: true) + + expect(flag_called_properties(c, 'remote-flag')['$feature_flag_has_experiment']).to be false + end + end end end diff --git a/spec/posthog/flags_spec.rb b/spec/posthog/flags_spec.rb index b12085c9..fb2812b2 100644 --- a/spec/posthog/flags_spec.rb +++ b/spec/posthog/flags_spec.rb @@ -462,10 +462,26 @@ module PostHog id: 1, version: 23, payload: '{"foo": 1}', - description: 'This is an enabled flag' + description: 'This is an enabled flag', + has_experiment: false ) ) end + + it 'parses has_experiment from metadata' do + result = FeatureFlag.new({ + 'key' => 'experiment-flag', + 'enabled' => true, + 'variant' => nil, + 'metadata' => { + 'id' => 1, + 'version' => 23, + 'has_experiment' => true + } + }) + + expect(result.metadata.has_experiment).to be(true) + end end describe 'Client#get_feature_flag' do @@ -488,6 +504,7 @@ module PostHog eq({ '$feature_flag' => 'enabled-flag', '$feature_flag_response' => true, + '$feature_flag_has_experiment' => false, '$feature_flag_request_id' => '42853c54-1431-4861-996e-3a548989fa2c', '$feature_flag_evaluated_at' => 1_704_067_200_000, '$lib' => 'posthog-ruby', From 471b38ed402e5d5119b0aef1bacc2d6d279af71f Mon Sep 17 00:00:00 2001 From: Phil Haack Date: Wed, 15 Jul 2026 16:22:44 -0700 Subject: [PATCH 2/2] Omit $feature_flag_has_experiment when the server does not report it --- .changeset/feature-flag-has-experiment.md | 2 +- lib/posthog/client.rb | 8 ++++---- lib/posthog/feature_flag.rb | 6 +++--- lib/posthog/feature_flag_evaluations.rb | 2 +- lib/posthog/feature_flags.rb | 8 ++++---- spec/posthog/client_spec.rb | 10 +++------- spec/posthog/feature_flag_evaluations_spec.rb | 10 +++++----- spec/posthog/feature_flag_spec.rb | 8 ++++---- spec/posthog/flags_spec.rb | 3 +-- 9 files changed, 26 insertions(+), 31 deletions(-) diff --git a/.changeset/feature-flag-has-experiment.md b/.changeset/feature-flag-has-experiment.md index a203814d..3d062de6 100644 --- a/.changeset/feature-flag-has-experiment.md +++ b/.changeset/feature-flag-has-experiment.md @@ -2,4 +2,4 @@ "posthog-ruby": minor --- -feat: add a `$feature_flag_has_experiment` boolean property to every `$feature_flag_called` event, sourced from the server-reported `has_experiment` field (defaults to false when the server does not report it) +feat: add a `$feature_flag_has_experiment` boolean property to `$feature_flag_called` events when the server explicitly reports the `has_experiment` field; the property is omitted when the server does not report it (older deployments) diff --git a/lib/posthog/client.rb b/lib/posthog/client.rb index 2e04aab9..f2bdad4b 100644 --- a/lib/posthog/client.rb +++ b/lib/posthog/client.rb @@ -610,7 +610,7 @@ def evaluate_flags( version: nil, reason: FeatureFlagEvaluations::EVALUATED_LOCALLY_REASON, locally_evaluated: true, - has_experiment: definition[:has_experiment] ? true : false + has_experiment: definition[:has_experiment] ) locally_evaluated_keys << key.to_s end @@ -651,7 +651,7 @@ def evaluate_flags( version: metadata ? metadata.version : nil, reason: reason ? (reason.description || reason.code) : nil, locally_evaluated: false, - has_experiment: metadata ? metadata.has_experiment : false + has_experiment: metadata&.has_experiment ) end rescue StandardError => e @@ -933,9 +933,9 @@ def _get_feature_flag_result( properties = { '$feature_flag' => key, '$feature_flag_response' => feature_flag_response, - 'locally_evaluated' => flag_was_locally_evaluated, - '$feature_flag_has_experiment' => has_experiment ? true : false + 'locally_evaluated' => flag_was_locally_evaluated } + properties['$feature_flag_has_experiment'] = has_experiment unless has_experiment.nil? properties['$feature_flag_request_id'] = request_id if request_id properties['$feature_flag_evaluated_at'] = evaluated_at if evaluated_at properties['$feature_flag_error'] = feature_flag_error if feature_flag_error diff --git a/lib/posthog/feature_flag.rb b/lib/posthog/feature_flag.rb index 0c68bc53..08dccd1f 100644 --- a/lib/posthog/feature_flag.rb +++ b/lib/posthog/feature_flag.rb @@ -77,9 +77,9 @@ def initialize(json) @version = json['version'] @payload = json['payload'] @description = json['description'] - # Whether the flag is linked to an experiment. Defaults to false when the - # server (an older deployment) does not report the field. - @has_experiment = json['has_experiment'] ? true : false + # Whether the flag is linked to an experiment. nil when the server + # (an older deployment) does not report the field. + @has_experiment = json['has_experiment'] end end end diff --git a/lib/posthog/feature_flag_evaluations.rb b/lib/posthog/feature_flag_evaluations.rb index b188811f..0be23f1d 100644 --- a/lib/posthog/feature_flag_evaluations.rb +++ b/lib/posthog/feature_flag_evaluations.rb @@ -165,11 +165,11 @@ def _record_access(key, flag) '$feature_flag' => key, '$feature_flag_response' => response, 'locally_evaluated' => flag&.locally_evaluated ? true : false, - '$feature_flag_has_experiment' => flag&.has_experiment ? true : false, "$feature/#{key}" => response } if flag + properties['$feature_flag_has_experiment'] = flag.has_experiment unless flag.has_experiment.nil? properties['$feature_flag_payload'] = flag.payload unless flag.payload.nil? properties['$feature_flag_id'] = flag.id if flag.id properties['$feature_flag_version'] = flag.version if flag.version diff --git a/lib/posthog/feature_flags.rb b/lib/posthog/feature_flags.rb index a5e4d3bc..dde847ce 100644 --- a/lib/posthog/feature_flags.rb +++ b/lib/posthog/feature_flags.rb @@ -223,9 +223,9 @@ def get_feature_flag( flag_was_locally_evaluated = !response.nil? # Whether the flag is linked to an experiment, as reported by the server. # Locally-evaluated flags carry it in the stored definition; remotely - # evaluated flags carry it in the response metadata. Defaults to false - # when the server (an older deployment) does not report it. - has_experiment = flag_was_locally_evaluated && feature_flag[:has_experiment] ? true : false + # evaluated flags carry it in the response metadata. nil when the server + # (an older deployment) does not report it. + has_experiment = feature_flag[:has_experiment] if flag_was_locally_evaluated request_id = nil evaluated_at = nil @@ -259,7 +259,7 @@ def get_feature_flag( feature_flag_error = errors.join(',') unless errors.empty? flag_detail = flags_data[:flagDetails]&.[](key.to_sym) - has_experiment = flag_detail&.metadata&.has_experiment ? true : false + has_experiment = flag_detail&.metadata&.has_experiment logger.debug "Successfully computed flag remotely: #{key} -> #{response}" rescue Timeout::Error => e diff --git a/spec/posthog/client_spec.rb b/spec/posthog/client_spec.rb index 1289e6f4..139ac992 100644 --- a/spec/posthog/client_spec.rb +++ b/spec/posthog/client_spec.rb @@ -559,7 +559,6 @@ module PostHog expect(captured_message[:properties]).to eq( '$feature_flag' => 'beta-feature', '$feature_flag_response' => true, - '$feature_flag_has_experiment' => false, '$lib' => 'posthog-ruby', '$lib_version' => '1.2.4', '$is_server' => true, @@ -623,8 +622,7 @@ module PostHog properties: { '$feature_flag' => 'beta-feature', '$feature_flag_response' => true, - 'locally_evaluated' => true, - '$feature_flag_has_experiment' => false + 'locally_evaluated' => true }, groups: {} }).exactly(1).times @@ -648,8 +646,7 @@ module PostHog properties: { '$feature_flag' => 'beta-feature', '$feature_flag_response' => true, - 'locally_evaluated' => true, - '$feature_flag_has_experiment' => false + 'locally_evaluated' => true }, groups: {} }).exactly(1).times @@ -671,8 +668,7 @@ module PostHog properties: { '$feature_flag' => 'decide-flag', '$feature_flag_response' => 'decide-value', - 'locally_evaluated' => false, - '$feature_flag_has_experiment' => false + 'locally_evaluated' => false }, groups: { organization: 'org1' } }).exactly(1).times diff --git a/spec/posthog/feature_flag_evaluations_spec.rb b/spec/posthog/feature_flag_evaluations_spec.rb index a2facf9c..4b270e35 100644 --- a/spec/posthog/feature_flag_evaluations_spec.rb +++ b/spec/posthog/feature_flag_evaluations_spec.rb @@ -343,14 +343,14 @@ def flag_called_properties(client, key) expect(flag_called_properties(client, 'plain-flag')['$feature_flag_has_experiment']).to be(false) end - it 'is false when the server omits has_experiment' do + it 'is omitted when the server does not report has_experiment' do stub_flags(has_experiment_response) - expect(flag_called_properties(client, 'legacy-flag')['$feature_flag_has_experiment']).to be(false) + expect(flag_called_properties(client, 'legacy-flag')).not_to have_key('$feature_flag_has_experiment') end - it 'is false for flags missing from the evaluation' do + it 'is omitted for flags missing from the evaluation' do stub_flags(has_experiment_response) - expect(flag_called_properties(client, 'not-a-flag')['$feature_flag_has_experiment']).to be(false) + expect(flag_called_properties(client, 'not-a-flag')).not_to have_key('$feature_flag_has_experiment') end end @@ -478,7 +478,7 @@ def flag_called_properties(client, key) msgs = drain_messages(c).select { |m| m[:event] == '$feature_flag_called' } by_key = msgs.to_h { |m| [m[:properties]['$feature_flag'], m[:properties]] } expect(by_key['local-flag']['$feature_flag_has_experiment']).to be(true) - expect(by_key['plain-local-flag']['$feature_flag_has_experiment']).to be(false) + expect(by_key['plain-local-flag']).not_to have_key('$feature_flag_has_experiment') end it 'skips the remote /flags call when flag_keys are all resolved locally' do diff --git a/spec/posthog/feature_flag_spec.rb b/spec/posthog/feature_flag_spec.rb index 674f2688..287183c0 100644 --- a/spec/posthog/feature_flag_spec.rb +++ b/spec/posthog/feature_flag_spec.rb @@ -5311,12 +5311,12 @@ def flag_called_properties(client, key) expect(flag_called_properties(c, 'test-flag')['$feature_flag_has_experiment']).to be false end - it 'is false when the local definition omits has_experiment' do + it 'is omitted when the local definition omits has_experiment' do stub_definitions(local_definition) stub_request(:post, flags_endpoint).to_return(status: 400) c = Client.new(api_key: API_KEY, personal_api_key: API_KEY, test_mode: true) - expect(flag_called_properties(c, 'test-flag')['$feature_flag_has_experiment']).to be false + expect(flag_called_properties(c, 'test-flag')).not_to have_key('$feature_flag_has_experiment') end it 'sources has_experiment from the /flags response metadata on remote evaluation' do @@ -5336,7 +5336,7 @@ def flag_called_properties(client, key) expect(flag_called_properties(c, 'remote-flag')['$feature_flag_has_experiment']).to be true end - it 'is false when the /flags response omits has_experiment' do + it 'is omitted when the /flags response omits has_experiment' do stub_definitions('flags' => []) stub_request(:post, flags_endpoint) .to_return(status: 200, body: { @@ -5345,7 +5345,7 @@ def flag_called_properties(client, key) }.to_json) c = Client.new(api_key: API_KEY, personal_api_key: API_KEY, test_mode: true) - expect(flag_called_properties(c, 'remote-flag')['$feature_flag_has_experiment']).to be false + expect(flag_called_properties(c, 'remote-flag')).not_to have_key('$feature_flag_has_experiment') end end end diff --git a/spec/posthog/flags_spec.rb b/spec/posthog/flags_spec.rb index fb2812b2..5d9e5d4e 100644 --- a/spec/posthog/flags_spec.rb +++ b/spec/posthog/flags_spec.rb @@ -463,7 +463,7 @@ module PostHog version: 23, payload: '{"foo": 1}', description: 'This is an enabled flag', - has_experiment: false + has_experiment: nil ) ) end @@ -504,7 +504,6 @@ module PostHog eq({ '$feature_flag' => 'enabled-flag', '$feature_flag_response' => true, - '$feature_flag_has_experiment' => false, '$feature_flag_request_id' => '42853c54-1431-4861-996e-3a548989fa2c', '$feature_flag_evaluated_at' => 1_704_067_200_000, '$lib' => 'posthog-ruby',