Module: ConcernsOnRails::Support::Affix

Defined in:
lib/concerns_on_rails/support/affix.rb

Overview

Shared naming for concerns that generate affixable scopes or accessors.

Two concerns that each define .active (SoftDeletable, Activatable, Expirable) can coexist on one model only if their generated names can be renamed, so every such concern takes prefix:/suffix: and routes the name through here rather than re-implementing the join.

Class Method Summary collapse

Class Method Details

.capture(klass, names) ⇒ Object

Snapshot the scopes a concern just defined on klass: a name => UnboundMethod map, captured immediately after definition. Names that aren't defined are skipped (a concern may generate a scope only under some configurations).



30
31
32
33
34
35
36
37
38
# File 'lib/concerns_on_rails/support/affix.rb', line 30

def capture(klass, names)
  singleton = klass.singleton_class
  names.each_with_object({}) do |base, acc|
    name = base.to_sym
    next unless singleton.method_defined?(name)

    acc[name] = singleton.instance_method(name)
  end
end

.name(base, prefix: nil, suffix: nil) ⇒ Object

[prefix, base, suffix], underscore-joined, as a Symbol.



13
14
15
# File 'lib/concerns_on_rails/support/affix.rb', line 13

def name(base, prefix: nil, suffix: nil)
  [prefix, base, suffix].compact.join("_").to_sym
end

.normalize(option, default:) ⇒ Object

Normalize an affix option: true means "use the configured field name" (Stateable's semantics, generalized to every affixing concern), a String/Symbol is used literally, and nil/false means no affix.



20
21
22
23
24
# File 'lib/concerns_on_rails/support/affix.rb', line 20

def normalize(option, default:)
  return nil unless option

  option == true ? default.to_s : option.to_s
end

.retire!(klass, captured, label:) ⇒ Object

Remove the default-named scopes recorded by capture so their affixed replacements are the only ones left. Returns the names removed.

Three guards, all of which must pass before a name is removed:

1. it is in the captured map (never a name the concern did not create);
2. it is owned by THIS class's own singleton (never inherited);
3. it is still the exact method captured (never a model's override).

Guard 2 failing means the concern was configured on a parent and the affix is being declared on a subclass — retiring nothing would hand back an escape hatch that doesn't work, because the parent's colliding scopes would survive. That raises instead.



52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/concerns_on_rails/support/affix.rb', line 52

def retire!(klass, captured, label:)
  singleton = klass.singleton_class
  captured.each_with_object([]) do |(name, recorded), removed|
    next unless singleton.method_defined?(name)

    current = singleton.instance_method(name)
    retire_guard_owner!(current, singleton, klass, name, label)
    next unless current == recorded

    singleton.send(:remove_method, name)
    removed << name
  end
end