Class: LittleGhost::Support::Callbacks
- Inherits:
-
Object
- Object
- LittleGhost::Support::Callbacks
- Defined in:
- lib/little_ghost/support/callbacks.rb
Overview
Callbacks lets extensions prepare, replace, or cancel framework work in a predictable order. A later callback sees any replacement made earlier in the chain.
A callback may return Callbacks.continue, Callbacks.cancel, or Callbacks.replace. Any other return value means continue. Replacements become the payload for later callbacks.
Every decision responds to continue?, cancel?, and replace?. A
cancellation also exposes reason; a replacement exposes value.
Extensions should depend on these methods rather than a decision's
concrete class.
callbacks = LittleGhost::Support::Callbacks.new(:prepare)
callbacks.on(:prepare) { |payload| Callbacks.replace(payload.merge(debug: true)) }
decision = callbacks.run(:prepare, {})
decision.value # => {debug: true}
Defined Under Namespace
Classes: Cancel, Continue, Replace
Constant Summary collapse
Class Method Summary collapse
-
.cancel(reason = nil) ⇒ Object
Creates a decision whose
cancel?predicate indicates that callback processing should stop. -
.continue ⇒ Object
Uses the shared decision whose
continue?predicate indicates that callback processing should proceed. -
.replace(value) ⇒ Object
Creates a decision whose
replace?predicate indicates that later callbacks should receivevalue.
Instance Method Summary collapse
-
#initialize(*names) ⇒ Callbacks
constructor
Starts an empty chain for the declared callback
names. -
#initialize_copy(source) ⇒ Object
Duplicates callback arrays so subclasses and instances can extend a copy.
-
#merge(other) ⇒ Object
Combines this chain with
otherwhile preserving prepend ordering. -
#on(name, callable = nil, prepend: false, &block) ⇒ Object
Registers a callable, block, or receiver method name for
name. -
#run(name, payload, context: nil, receiver: nil) ⇒ Object
Runs
nameuntil callbacks finish or one cancels the chain.
Constructor Details
#initialize(*names) ⇒ Callbacks
Starts an empty chain for the declared callback names.
56 57 58 59 |
# File 'lib/little_ghost/support/callbacks.rb', line 56 def initialize(*names) @callbacks = names.to_h { |name| [name.to_sym, []] } @prepend_counts = names.to_h { |name| [name.to_sym, 0] } end |
Class Method Details
.cancel(reason = nil) ⇒ Object
Creates a decision whose cancel? predicate indicates that callback
processing should stop. The returned value exposes the optional
reason.
48 |
# File 'lib/little_ghost/support/callbacks.rb', line 48 def cancel(reason = nil) = Cancel.new(reason:) |
.continue ⇒ Object
Uses the shared decision whose continue? predicate indicates that
callback processing should proceed.
43 |
# File 'lib/little_ghost/support/callbacks.rb', line 43 def continue = CONTINUE |
Instance Method Details
#initialize_copy(source) ⇒ Object
Duplicates callback arrays so subclasses and instances can extend a copy.
62 63 64 65 66 |
# File 'lib/little_ghost/support/callbacks.rb', line 62 def initialize_copy(source) super @callbacks = source.instance_variable_get(:@callbacks).transform_values(&:dup) @prepend_counts = source.instance_variable_get(:@prepend_counts).dup end |
#merge(other) ⇒ Object
Combines this chain with other while
preserving prepend ordering.
89 90 91 92 93 94 95 96 97 |
# File 'lib/little_ghost/support/callbacks.rb', line 89 def merge(other) merged = dup other.instance_variable_get(:@callbacks).each do |name, callbacks| prepend_count = other.instance_variable_get(:@prepend_counts).fetch(name) callbacks.first(prepend_count).reverse_each { |callback| merged.on(name, callback, prepend: true) } callbacks.drop(prepend_count).each { |callback| merged.on(name, callback) } end merged end |
#on(name, callable = nil, prepend: false, &block) ⇒ Object
Registers a callable, block, or receiver method name for name.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/little_ghost/support/callbacks.rb', line 69 def on(name, callable = nil, prepend: false, &block) callback = callable || block unless callback.respond_to?(:call) || callback.is_a?(String) || callback.is_a?(Symbol) raise ArgumentError, "A callback is required" end registered = @callbacks.fetch(name.to_sym) { raise ArgumentError, "Unknown callback: #{name}" } unless registered.include?(callback) if prepend registered.unshift(callback) @prepend_counts[name.to_sym] += 1 else registered << callback end end self end |
#run(name, payload, context: nil, receiver: nil) ⇒ Object
Runs name until callbacks finish or one cancels the chain.
The returned decision responds to continue?, cancel?, and
replace?. Cancellation decisions expose reason, while replacement
decisions expose the final value.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/little_ghost/support/callbacks.rb', line 104 def run(name, payload, context: nil, receiver: nil) current = payload @callbacks.fetch(name.to_sym) { raise ArgumentError, "Unknown callback: #{name}" }.each do |callback| decision = normalize(invoke(callback, current, context, receiver)) case decision when Continue next when Replace current = decision.value else return decision end end current.equal?(payload) ? self.class.continue : self.class.replace(current) end |