Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/flag-evaluations-enabled-default-value.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'posthog-ruby': minor
---

Add an optional `default_value:` keyword to `PostHog::FeatureFlagEvaluations#enabled?`. It is returned when the flag has no value in the snapshot β€” it was never loaded, the evaluation failed, or no flag with that key exists β€” while a flag that does have a value, including `false` and variant strings, still wins over the default. Calls that omit the keyword keep returning `false` for a missing flag.
12 changes: 10 additions & 2 deletions lib/posthog/feature_flag_evaluations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,19 @@ def keys
end

# @param key [String, Symbol] The feature flag key.
# @return [Boolean] true when the flag is enabled, false when disabled or missing.
def enabled?(key)
# @param default_value [Boolean, nil] Returned when the flag has no value in this
# snapshot β€” it was never loaded, the evaluation failed, or no flag with that key
# exists. A flag that does have a value, including `false` and variant strings,
# always wins over this default. Defaults to `nil`, which keeps the historical
# `false` result for a missing flag.
# @return [Boolean] true when the flag is enabled, false when disabled, and the
# caller-supplied default (or false when none was supplied) when the flag is missing.
def enabled?(key, default_value: nil)
key = key.to_s
flag = @flags[key]
_record_access(key, flag)
return !!default_value if flag.nil? && !default_value.nil?

flag&.enabled ? true : false
end

Expand Down
2 changes: 1 addition & 1 deletion public_api_snapshot.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ constant PostHog::Defaults::Request::RETRIES: Integer
constant PostHog::Defaults::Request::SSL: Boolean
class PostHog::FeatureFlagEvaluations
instance_method PostHog::FeatureFlagEvaluations#distinct_id()
instance_method PostHog::FeatureFlagEvaluations#enabled?(key)
instance_method PostHog::FeatureFlagEvaluations#enabled?(key, default_value: ...)
instance_method PostHog::FeatureFlagEvaluations#evaluated_at()
instance_method PostHog::FeatureFlagEvaluations#flag_definitions_loaded_at()
instance_method PostHog::FeatureFlagEvaluations#get_flag(key)
Expand Down
34 changes: 34 additions & 0 deletions spec/posthog/feature_flag_evaluations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,40 @@ def capture_stderr
expect(snapshot.enabled?('not-a-flag')).to be(false)
end

it 'enabled? resolves a missing flag to the caller-supplied default' do
stub_flags(flags_response)
snapshot = client.evaluate_flags('user-1')
expect(snapshot.enabled?('not-a-flag', default_value: true)).to be(true)
expect(snapshot.enabled?('not-a-flag', default_value: false)).to be(false)
end

it 'enabled? prefers an existing flag value over the caller-supplied default' do
stub_flags(flags_response)
snapshot = client.evaluate_flags('user-1')
expect(snapshot.enabled?('disabled-flag', default_value: true)).to be(false)
expect(snapshot.enabled?('variant-flag', default_value: false)).to be(true)
expect(snapshot.enabled?('boolean-flag', default_value: false)).to be(true)
end

it 'enabled? uses the caller default on an empty snapshot' do
snapshot = client.evaluate_flags('')
expect(snapshot.enabled?('anything', default_value: true)).to be(true)
expect(snapshot.enabled?('anything')).to be(false)
end

it 'enabled? still reports the evaluated response, not the default, on $feature_flag_called' do
stub_flags(flags_response)
snapshot = client.evaluate_flags('user-1')
snapshot.enabled?('not-a-flag', default_value: true)

msgs = drain_messages(client).select do |m|
m[:event] == '$feature_flag_called' && m[:properties]['$feature_flag'] == 'not-a-flag'
end
expect(msgs.length).to eq(1)
expect(msgs.first[:properties]['$feature_flag_response']).to be_nil
expect(msgs.first[:properties]['$feature_flag_error']).to eq('flag_missing')
end

it 'enabled? and get_flag on a variant flag dedupe to a single event with the variant response' do
stub_flags(flags_response)
snapshot = client.evaluate_flags('user-1')
Expand Down
Loading