Class: Subflag::Rails::FlagAccessor

Inherits:
Object
  • Object
show all
Defined in:
lib/subflag/rails/flag_accessor.rb,
lib/subflag/rails/test_helpers.rb

Overview

Dynamic flag accessor using method_missing

Provides a clean DSL for accessing flags:

Flag names are automatically converted:

  • Underscores become dashes: new_checkoutnew-checkout
  • Trailing ? is removed for booleans: enabled?enabled

Examples:

Boolean flags (? suffix) - default is optional (false)

Subflag.flags.new_checkout?
Subflag.flags.new_checkout?(default: true)

Value flags - default is REQUIRED

Subflag.flags.homepage_headline(default: "Welcome")
Subflag.flags.max_projects(default: 3)
Subflag.flags.tax_rate(default: 0.08)
Subflag.flags.feature_limits(default: {})

With user context

Subflag.flags(user: current_user).max_projects(default: 3)

Bracket access (exact flag names, default required)

Subflag.flags["my-exact-flag", default: "value"]

Full evaluation details

result = Subflag.flags.evaluate(:max_projects, default: 3)
result.value    # => 100
result.reason   # => :targeting_match

Instance Method Summary collapse

Constructor Details

#initialize(user: nil, context: nil) ⇒ FlagAccessor

Returns a new instance of FlagAccessor.



35
36
37
38
# File 'lib/subflag/rails/flag_accessor.rb', line 35

def initialize(user: nil, context: nil)
  @user = user
  @context = context
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, **kwargs, &block) ⇒ Object

Handle dynamic flag access

Method names ending in ? are treated as boolean flags (default: false). All other methods require a default: keyword argument.

Examples:

Boolean (default optional)

flags.new_checkout?              # default: false
flags.new_checkout?(default: true)

Value (default required)

flags.max_projects(default: 3)
flags.headline(default: "Hi")


86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/subflag/rails/flag_accessor.rb', line 86

def method_missing(method_name, *args, **kwargs, &block)
  flag_key = normalize_flag_name(method_name)

  if method_name.to_s.end_with?("?")
    # Boolean flag - default is optional (false)
    default = kwargs.fetch(:default, false)
    client.enabled?(flag_key, user: @user, context: @context, default: default)
  else
    # Value flag - default is required
    unless kwargs.key?(:default)
      raise ArgumentError, "default is required: Subflag.flags.#{method_name}(default: <value>)"
    end
    client.value(flag_key, user: @user, context: @context, default: kwargs[:default])
  end
end

Instance Method Details

#[](flag_name, default:) ⇒ Object

Bracket access for exact flag names (no conversion)

Examples:

Subflag.flags["my-exact-flag", default: "value"]

Parameters:

  • flag_name (String, Symbol)

    The exact flag name

  • default (Object)

    Default value (required)

Returns:

  • (Object)

    The flag value

Raises:

  • (ArgumentError)

    If default is not provided



50
51
52
53
# File 'lib/subflag/rails/flag_accessor.rb', line 50

def [](flag_name, default:)
  flag_key = flag_name.to_s
  client.value(flag_key, user: @user, context: @context, default: default)
end

#evaluate(flag_name, default:) ⇒ EvaluationResult

Get full evaluation details for a flag

Examples:

result = Subflag.flags.evaluate(:max_projects, default: 3)
result.value    # => 100
result.variant  # => "premium"
result.reason   # => :targeting_match

Parameters:

  • flag_name (String, Symbol)

    The flag name

  • default (Object)

    Default value (required)

Returns:

Raises:

  • (ArgumentError)

    If default is not provided



68
69
70
71
# File 'lib/subflag/rails/flag_accessor.rb', line 68

def evaluate(flag_name, default:)
  flag_key = normalize_flag_name(flag_name)
  client.evaluate(flag_key, user: @user, context: @context, default: default)
end

#respond_to_missing?(method_name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


102
103
104
# File 'lib/subflag/rails/flag_accessor.rb', line 102

def respond_to_missing?(method_name, include_private = false)
  true
end