Module: Hecks::Runtime::Routing

Defined in:
lib/hecks/runtime/routing.rb

Overview

The invocation address is not part of a command's domain payload. Aggregate commands carry one receiver identity; entity commands carry the aggregate receiver followed by one identity for every entity hop.

Defined Under Namespace

Classes: Envelope

Class Method Summary collapse

Class Method Details

.envelope(to, entity_depth: 0) ⇒ Object



19
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/hecks/runtime/routing.rb', line 19

def envelope(to, entity_depth: 0)
  return nil if to.nil?

  aggregate, entities = if to.is_a?(Hash)
                          hash = to.transform_keys(&:to_sym)
                          unknown = hash.keys - %i[aggregate entity entities]
                          unless unknown.empty?
                            raise TypeMismatch, "to: does not recognize #{unknown.sort.join(', ')}"
                          end

                          [hash[:aggregate], entity_identities(hash)]
                        else
                          [to, []]
                        end

  if aggregate.nil? || aggregate.to_s.empty?
    raise TypeMismatch, "to: must name the receiving aggregate identity"
  end
  if entities.size != entity_depth
    raise TypeMismatch,
          "to: for an entity command needs #{entity_depth} entity " \
          "#{entity_depth == 1 ? 'identity' : 'identities'} after the aggregate — got #{entities.size}"
  end
  if entities.any? { |identity| identity.nil? || identity.to_s.empty? }
    raise TypeMismatch, "to: contains a blank entity identity"
  end

  Envelope.new(aggregate: aggregate, entities: entities)
end

.payload(command, with:, legacy:) ⇒ Object

with: is deliberately strict. Compatibility-only calls still pass loose keyword arguments through the old addressing gate, but a caller choosing the explicit envelope cannot smuggle receiver identity back into the payload.



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/hecks/runtime/routing.rb', line 53

def payload(command, with:, legacy:)
  if with && !legacy.empty?
    raise TypeMismatch, "dispatch takes command facts in with:, not both with: and loose keyword arguments"
  end

  return legacy unless with
  unless with.is_a?(Hash)
    raise TypeMismatch, "with: must be a hash of command facts"
  end

  offered = with.transform_keys(&:to_sym)
  declared = command.attributes.map { |attribute| attribute.name.to_sym }
  unknown = (offered.keys - declared).sort
  unless unknown.empty?
    reading = declared.empty? ? "none" : declared.join(", ")
    raise UnknownArgument,
          RefusalWording.render("UnknownArgument", "unknown_args",
                                command: command.hecks_name, unknown: unknown.join(", "),
                                declared: reading)
  end

  absent = command.attributes.reject(&:optional?).map { |attribute| attribute.name.to_sym } - offered.keys
  unless absent.empty?
    reading = declared.empty? ? "none" : declared.join(", ")
    raise AbsentArgument,
          RefusalWording.render("AbsentArgument", "absent_args",
                                command: command.hecks_name, absent: absent.sort.join(", "),
                                declared: reading)
  end

  offered
end