Module: Axn::Configurable::PerClassOverrides
- Included in:
- Axn::Configurable, Settings
- Defined in:
- lib/axn/configurable.rb
Overview
Per-class override accessors, shared by both config flavors (the
module-singleton Configurable and the class-level Settings). Included
into each, so its methods become singleton methods of whatever module/class
extends that flavor. The only per-flavor difference is where the resolution
fallback reads the library-level value, so _define_override_methods takes
that as a lambda.
Instance Method Summary collapse
-
#_validate_override_setter!(name, value) ⇒ Object
Eager validation for the
configurewriter when this source owns the namespace being written: rejects a setter name that isn't an overridable setting (a typo that would otherwise store silently and never resolve), then validates the value against the setting. -
#config_namespace(value = UNSET) ⇒ Object
The store namespace this config source owns.
-
#overrides ⇒ Object
Returns a module that, when included in an action class, extends it with the per-class override accessors for each overridable setting.
-
#resolve_override_for(klass, name) ⇒ Object
Resolves
nameforklassthrough the same override store + fallback the generated accessors use, WITHOUT dispatching to a class method onklass.
Instance Method Details
#_validate_override_setter!(name, value) ⇒ Object
Eager validation for the configure writer when this source owns the namespace being
written: rejects a setter name that isn't an overridable setting (a typo that would
otherwise store silently and never resolve), then validates the value against the setting.
220 221 222 223 224 225 |
# File 'lib/axn/configurable.rb', line 220 def _validate_override_setter!(name, value) setting = _override_settings[name.to_sym] raise ArgumentError, "unknown overridable setting #{name.inspect} for namespace #{config_namespace.inspect}" unless setting setting.validate!(value) end |
#config_namespace(value = UNSET) ⇒ Object
The store namespace this config source owns. Overridable settings and their
per-class overrides are keyed by [namespace, setting], so two modules that
declare a same-named setting (e.g. a tool composing several adapter mixins)
never collide in the consumer class's single override store. Declared once via
config_namespace :mcp; the symbol is also what configure(:mcp) { … } targets.
Defaults to the module/class itself — unique per source, so flat-accessor-only
consumers stay collision-safe without declaring anything.
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/axn/configurable.rb', line 189 def config_namespace(value = UNSET) return (@_config_namespace ||= self) if UNSET.equal?(value) # The namespace gets baked in the first time it's used — into each overridable setting's # accessor closures (at declaration) and into a class's source registry (at include). Changing # it afterward would strand those under the old key while `configure(value)` writes/validates # under the new one. Lock on first use and enforce the documented "declare it first" rule. if @_config_namespace_locked && value != @_config_namespace raise ArgumentError, "config_namespace must be declared before any overridable setting is defined or its " \ "overrides are included (got #{value.inspect} after use under #{(@_config_namespace || self).inspect})" end @_config_namespace = value end |
#overrides ⇒ Object
Returns a module that, when included in an action class, extends it with the
per-class override accessors for each overridable setting. setting adds to
a shared methods module as overridable settings are declared, and Ruby
reflects those additions on already-extended classes — so it's insensitive
to load order.
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/axn/configurable.rb', line 147 def overrides @overrides ||= begin methods_module = _override_methods_module config_source = self Module.new do define_singleton_method(:included) do |base| # Breadcrumb before extending, while `base`'s own lookup still reflects only its # ancestors (not yet axn's accessors), so the check sees a genuine external definition. config_source.send(:_warn_on_shadowed_overrides, base) # Record which config source owns each namespace on this class, so the tolerant # `configure` writer can validate a setter eagerly when the namespace is registered # (schema known) and stay tolerant only when it isn't (adapter not loaded / not included). config_source.send(:_register_overrides_on, base) # `axn_configure` is the always-available, collision-proof writer. Bare `configure` is a # generic name a non-axn base class may already own; Ruby places an extended module above # the superclass chain, so installing it unconditionally would shadow that base hook and # reroute its `configure(...)` calls into axn's writer. Install the ergonomic bare alias # only when the name is free — same PRO-2875 discipline the Naming/SchemaReflection generic # names use — and always leave `axn_configure` as the guaranteed way to reach axn's config. base.extend(ClassConfigWriter) shadowed = defined?(Axn::Core::MethodShadowing) && Axn::Core::MethodShadowing.externally_defined?(base, :configure) unless shadowed base.define_singleton_method(:configure) do |namespace = :core, &block| axn_configure(namespace, &block) end end base.extend(methods_module) end end end end |
#resolve_override_for(klass, name) ⇒ Object
Resolves name for klass through the same override store + fallback the
generated accessors use, WITHOUT dispatching to a class method on klass.
For framework code that consumes an override: the generated <name> /
<name>? readers are all shadowable by a same-named class method
on the action (or a subclass), which would silently bypass the override
store — so the framework resolves through this registry instead. Raises
KeyError if name isn't an overridable setting (a declaration-time bug).
212 213 214 215 |
# File 'lib/axn/configurable.rb', line 212 def resolve_override_for(klass, name) _validate_slot_keys!(klass) _override_resolvers.fetch(name.to_sym).call(klass) end |