Module: Ecoportal::API::GraphQL::Model::Template::Node

Included in:
Binding, Field, Force, Helper, Option, Section, Stage
Defined in:
lib/ecoportal/api/graphql/model/template/node.rb

Overview

Shared behaviour for every mutable node in the editable template tree (Stage, Section, Field, Option, Force, Binding, Helper). A node is either:

* LOADED — `id` is a real server id, carried in from `Read.call`. `snapshot`
freezes the mutable attributes as they were at load time, so `#dirty?` /
`#changed_attributes` can diff "now" against "as loaded" later.
* NEW — `id` is nil; the node was created in memory (`Stage#add_section`, etc.)
and has no server counterpart yet. `#ref` mints a client-chosen `placeholderId`
the first time it is asked (deterministic, `"ph_<prefix>_<n>"`, matching the
scheme `Builder::TemplateBuilder`/`Diff::CommandSynthesizer` already use,
counter owned by the root `Instance` so two loaded templates never share
counters) and memoises it — every command in the same `#as_commands` batch
that addresses this node reuses the SAME token.

#ref is the ONE thing every command-emission method should call to address a node — never id directly — because it transparently upgrades from placeholder to real id the moment StagedExecutor resolves this node against a live re-read (#resolve!), without the caller needing to know which phase it is in.

Including class MUST define PLACEHOLDER_PREFIX (a short String, e.g. 'sec') and #template (the root Instance, which owns the placeholder counters).

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#id ⇒ Object (readonly)

Returns the value of attribute id.



28
29
30
# File 'lib/ecoportal/api/graphql/model/template/node.rb', line 28

def id
  @id
end

Instance Method Details

#new? ⇒ Boolean

Returns:

  • (Boolean)


30
31
32
# File 'lib/ecoportal/api/graphql/model/template/node.rb', line 30

def new?
  id.nil?
end

#placeholder ⇒ Object



58
59
60
# File 'lib/ecoportal/api/graphql/model/template/node.rb', line 58

def placeholder
  @placeholder ||= template.next_placeholder(self.class::PLACEHOLDER_PREFIX)
end

#ref ⇒ Object

The token every command that addresses this node must use: the real id once known, otherwise a memoised placeholder. StagedExecutor#resolve! is the only code that ever calls #resolve! — everything downstream just calls #ref again and transparently gets the real id from then on.



54
55
56
# File 'lib/ecoportal/api/graphql/model/template/node.rb', line 54

def ref
  id || placeholder
end

#remove! ⇒ Object

Flags this node for a remove* command. Only meaningful for a LOADED node — a NEW node that is removed before ever being saved should simply be dropped from its parent collection instead (there is nothing server-side to remove yet).



41
42
43
44
45
46
47
48
# File 'lib/ecoportal/api/graphql/model/template/node.rb', line 41

def remove!
  if new?
    raise ArgumentError, "#{self.class}: cannot remove a node that was never saved " \
                         '(drop it from its parent collection instead)'
  end

  @removed = true
end

#removed? ⇒ Boolean

Returns:

  • (Boolean)


34
35
36
# File 'lib/ecoportal/api/graphql/model/template/node.rb', line 34

def removed?
  !!@removed
end

#resolve!(real_id) ⇒ Object

Called once a live re-read has matched this NEW node to its real server id. Raises if called on an already-resolved node with a DIFFERENT id (a resolver bug, not a transient condition) — silently overwriting a resolved id would hide a mismatched cross-check upstream.

Raises:

  • (ArgumentError)


66
67
68
69
70
71
72
# File 'lib/ecoportal/api/graphql/model/template/node.rb', line 66

def resolve!(real_id)
  raise ArgumentError, "#{self.class}: resolve! requires a real id" if real_id.nil?
  return if id == real_id
  raise "#{self.class}: already resolved to #{id.inspect}, cannot re-resolve to #{real_id.inspect}" if id

  @id = real_id
end