Module: Philiprehberger::FeatureFlag

Extended by:
Dependencies, Groups, Metrics, Scheduling, Targeting
Defined in:
lib/philiprehberger/feature_flag.rb,
lib/philiprehberger/feature_flag/groups.rb,
lib/philiprehberger/feature_flag/metrics.rb,
lib/philiprehberger/feature_flag/rollout.rb,
lib/philiprehberger/feature_flag/version.rb,
lib/philiprehberger/feature_flag/targeting.rb,
lib/philiprehberger/feature_flag/scheduling.rb,
lib/philiprehberger/feature_flag/dependencies.rb,
lib/philiprehberger/feature_flag/configuration.rb,
lib/philiprehberger/feature_flag/backends/env_backend.rb,
lib/philiprehberger/feature_flag/backends/yaml_backend.rb,
lib/philiprehberger/feature_flag/backends/memory_backend.rb

Defined Under Namespace

Modules: Backends, Dependencies, Groups, Metrics, Rollout, Scheduling, Targeting Classes: Configuration

Constant Summary collapse

VERSION =
'0.4.0'

Class Method Summary collapse

Methods included from Groups

disable_group, enable_group, group, group_flags, reset_groups!

Methods included from Targeting

disable_for, enable_for, reset_targets!, targeted?, targeted_contexts, targeted_users

Methods included from Metrics

metrics, record_metric, reset_metrics!

Methods included from Scheduling

reset_schedules!, schedule, schedule_for, scheduled_active?

Methods included from Dependencies

dependencies_met?, dependency_for, depends_on, reset_dependencies!

Class Method Details

.configurationObject



24
25
26
# File 'lib/philiprehberger/feature_flag.rb', line 24

def configuration
  @configuration ||= Configuration.new
end

.configure {|config| ... } ⇒ void

This method returns an undefined value.

Configure the feature flag system. Yields the shared Configuration instance so callers can pick a backend or mutate settings.

Yield Parameters:



34
35
36
# File 'lib/philiprehberger/feature_flag.rb', line 34

def configure
  yield(configuration)
end

.enabled?(flag, user_id: nil, user: nil, context: {}) ⇒ Boolean, Object

Check whether flag is enabled. The lookup honors (in order): in-flight overrides from with, dependency gates, scheduling windows, user targeting, and finally the backend value — which may be a boolean, a percentage rollout, or any truthy value.

Parameters:

  • flag (Symbol, String)

    flag name

  • user_id (String, Integer, nil) (defaults to: nil)

    used for percentage rollouts

  • user (String, Integer, nil) (defaults to: nil)

    used for user-list targeting

  • context (Hash) (defaults to: {})

    optional context passed to targeting predicates and rollout bucketing (see rollout_by on the flag configuration)

Returns:

  • (Boolean, Object)

    boolean for normal flags; whatever was stored (including nil) when an override is active



51
52
53
54
55
56
57
# File 'lib/philiprehberger/feature_flag.rb', line 51

def enabled?(flag, user_id: nil, user: nil, context: {})
  return @overrides[flag.to_s] if overridden?(flag)

  result = evaluate_flag(flag, user_id, user, context)
  record_metric(flag, result)
  result
end

.flag_namesArray<Symbol>

Return the sorted, deduplicated union of every flag name known to the configured backend and the registered dependency, schedule, targeting, and group subsystems.

Backends that do not expose their flag names (for example, opaque remote backends without an all accessor) are skipped silently.

Returns:

  • (Array<Symbol>)

    ascending-sorted unique flag names



109
110
111
112
113
114
115
116
117
118
# File 'lib/philiprehberger/feature_flag.rb', line 109

def flag_names
  names = []
  names.concat(backend_flag_names)
  names.concat(Array(@dependencies&.keys))
  names.concat(Array(@dependencies&.values))
  names.concat(Array(@schedules&.keys))
  names.concat(Array(@targets&.keys))
  names.concat(Array(@groups&.values).flatten)
  names.map(&:to_sym).uniq.sort
end

.reload!Object



97
98
99
# File 'lib/philiprehberger/feature_flag.rb', line 97

def reload!
  configuration.backend.reload!
end

.reset!Object



120
121
122
123
124
125
126
127
128
# File 'lib/philiprehberger/feature_flag.rb', line 120

def reset!
  @configuration = nil
  @overrides = nil
  reset_dependencies!
  reset_schedules!
  reset_metrics!
  reset_targets!
  reset_groups!
end

.variant(flag, user_id:, context: {}) ⇒ String?

Return the A/B variant for user_id on flag. Variants are stored as { ‘variants’ => […] } on the backend and selected deterministically from the user id (optionally combined with context when the flag declares rollout_by).

Parameters:

  • flag (Symbol, String)

    flag name

  • user_id (String, Integer)

    bucket key

  • context (Hash) (defaults to: {})

    optional context hash; used with the flag’s rollout_by to diversify variant selection

Returns:

  • (String, nil)

    the selected variant, or nil when the flag is not a variant-shaped hash



70
71
72
73
74
75
76
77
78
79
80
# File 'lib/philiprehberger/feature_flag.rb', line 70

def variant(flag, user_id:, context: {})
  value = configuration.backend.get(flag)
  return nil unless variant_value?(value)

  variants = value['variants']
  rollout_by = Array(value['rollout_by']).map(&:to_sym)
  rollout_by = [:user_id] if rollout_by.empty?
  key = Rollout.bucket_key(user_id, context, rollout_by) || user_id.to_s
  bucket = Zlib.crc32("#{flag}:#{key}") % variants.size
  variants[bucket]
end

.with(flag, value) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/philiprehberger/feature_flag.rb', line 82

def with(flag, value)
  @overrides ||= {}
  key = flag.to_s
  had_previous = @overrides.key?(key)
  previous = @overrides[key]
  @overrides[key] = value
  yield
ensure
  if had_previous
    @overrides[key] = previous
  else
    @overrides&.delete(key)
  end
end