Class: Axn::Configurable::Setting

Inherits:
Struct
  • Object
show all
Defined in:
lib/axn/configurable.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#defaultObject

Returns the value of attribute default

Returns:

  • (Object)

    the current value of default



86
87
88
# File 'lib/axn/configurable.rb', line 86

def default
  @default
end

#nameObject

Returns the value of attribute name

Returns:

  • (Object)

    the current value of name



86
87
88
# File 'lib/axn/configurable.rb', line 86

def name
  @name
end

#one_ofObject

Returns the value of attribute one_of

Returns:

  • (Object)

    the current value of one_of



86
87
88
# File 'lib/axn/configurable.rb', line 86

def one_of
  @one_of
end

#overridableObject

Returns the value of attribute overridable

Returns:

  • (Object)

    the current value of overridable



86
87
88
# File 'lib/axn/configurable.rb', line 86

def overridable
  @overridable
end

#validateObject

Returns the value of attribute validate

Returns:

  • (Object)

    the current value of validate



86
87
88
# File 'lib/axn/configurable.rb', line 86

def validate
  @validate
end

Instance Method Details

#dup_defaultObject

A fresh copy of the default, so mutable defaults (e.g. []) aren't shared across instances. dup is a no-op for nil/true/false/Symbol/Integer.



130
131
132
# File 'lib/axn/configurable.rb', line 130

def dup_default
  default.dup
end

#dynamic_default?Boolean

A Proc default is DYNAMIC: re-derived on every read while the setting is unset, and never stored. Settings whose default depends on the host app's boot state (a tracer that OpenTelemetry may register after axn loads, a Rails.env-derived flag) would otherwise cache an answer taken before that state existed.

Returns:

  • (Boolean)


126
# File 'lib/axn/configurable.rb', line 126

def dynamic_default? = Axn::Internal::Identity.kind?(default, Proc)

#validate!(value) ⇒ Object

Raises ArgumentError if the assigned value is not permitted. A validate: lambda may return a String instead of true to say WHY the value was rejected — worth it for a setting whose value is an object the app supplies, where "invalid" alone doesn't hint at the contract.

Raises:

  • (ArgumentError)


90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/axn/configurable.rb', line 90

def validate!(value)
  # `describe` for the rejected value, plain `inspect` for the allowlist: the allowlist is what the
  # library author declared, while the value is whatever a caller assigned — and only the latter
  # can take down the error being raised about it.
  if one_of && !one_of.include?(value)
    raise ArgumentError,
          "#{name} must be one of #{one_of.map(&:inspect).join(', ')}; " \
          "got #{Axn::Internal::Identity.describe(value)}"
  end
  return unless validate.respond_to?(:call)

  outcome = validate.call(value)
  # `String === outcome`, not `outcome.is_a?(String)`: the value comes back from a caller's
  # lambda, and Module#=== settles the type without dispatching anything to it.
  return if outcome && !Axn::Internal::Identity.kind?(outcome, String)

  # A blank reason is no reason: fall back to the plain form below rather than raising with a
  # dangling " — " and nothing after it. Checked without ActiveSupport's blank extensions, since
  # this file is loadable on its own and must not depend on them being present.
  # Rendered to UTF-8 before it is joined: a reason in an incompatible encoding would otherwise
  # raise Encoding::CompatibilityError out of the interpolation, replacing the ArgumentError this
  # method promises with one about encodings.
  # Rendered BEFORE the blank test, not after. `blank_string?` runs `strip`, which raises
  # Encoding::CompatibilityError on invalid UTF-8 — so testing the raw reason first reintroduced
  # the very failure the rendering exists to prevent, one step earlier.
  rendered = Axn::Internal::Identity.utf8_string(outcome) if Axn::Internal::Identity.kind?(outcome, String)
  detail = rendered unless Axn::Internal::Identity.nil_value?(rendered) ||
                           Axn::Internal::Identity.blank_string?(rendered)
  raise ArgumentError,
        ["#{name} got invalid value: #{Axn::Internal::Identity.describe(value)}", detail].compact.join("")
end