Module: Jazari::Anchors

Defined in:
lib/jazari/anchors.rb

Overview

Anchor resolution, shared by every path that needs a subject for an AnchorTarget.

This exists because the read path and the write path had drifted: runs resolved anchors through the host's configured resolver, while customizing a runbook created one directly. A host whose anchor table carries its own NOT NULL columns — because it adopted jazari onto a table it already had — could therefore read but never write. One resolver, used everywhere.

Class Method Summary collapse

Class Method Details

.resolve(target, strict: true, create: false) ⇒ Object

strict: a write path; an unresolvable anchor fails closed. a read path passes false — an anchor that does not exist yet is the normal state of an uncustomized subject, not an error. create: may this call materialise the anchor? Only true when the caller is about to persist a customization.

Raises:



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/jazari/anchors.rb', line 20

def resolve(target, strict: true, create: false)
  scopes = Jazari.config.anchor_scopes
  unless scopes.key?(target.scope_type)
    raise TargetNotFound, "anchor scope #{target.scope_type.inspect} is not registered"
  end

  resolver = scopes[target.scope_type]
  subject =
    if resolver.respond_to?(:call)
      # The host owns creation as well as lookup — its table may demand
      # columns the gem knows nothing about — so it MUST be told whether
      # creation is permitted. Without `create`, a host resolver would
      # materialise an anchor on every read, breaking the rule that
      # resolving a default writes nothing.
      resolver.arity == 1 ? resolver.call(target) : resolver.call(target, create)
    elsif create
      Anchor.create_or_find_by!(scope_type: target.scope_type,
                                scope_id: target.scope_id, key: target.key)
    else
      Anchor.find_by(scope_type: target.scope_type,
                     scope_id: target.scope_id, key: target.key)
    end

  return subject if subject.is_a?(ActiveRecord::Base) && subject.persisted?
  return nil unless strict

  raise TargetNotFound, "anchor #{target.key.inspect} did not resolve to a persisted record"
end