Class: Yes::Core::Commands::CommandGroup

Inherits:
Object
  • Object
show all
Defined in:
lib/yes/core/commands/command_group.rb

Overview

Base class for aggregate-DSL command groups.

A CommandGroup represents a compound, transactional action declared inside an aggregate via the command_group :name do … end macro. It references its sub-commands by symbol (not by class constant) so declaration order in the aggregate body does not matter — resolution happens lazily against the Yes configuration registry.

The legacy Group is intentionally untouched and continues to serve the stateless command flow.

Constant Summary collapse

Attributes =

Reuse the legacy Group's reserved-key Attributes struct — the shape is identical for both group flavours.

Yes::Core::Commands::Group::Attributes

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params) ⇒ CommandGroup

Returns a new instance of CommandGroup.

Parameters:

  • params (Hash) —

    flat / partially-nested input payload, optionally carrying reserved keys (transaction, origin, batch_id, metadata, command_id, es_encrypted)



99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/yes/core/commands/command_group.rb', line 99

def initialize(params)
  @group_attributes = Attributes.new(params.slice(*Yes::Core::Command::RESERVED_KEYS))
  @payload = params.except(*Yes::Core::Command::RESERVED_KEYS).symbolize_keys
  @normalized_payload = GroupPayloadNormalizer.call(
    params,
    command_contexts: self.class.command_contexts,
    own_context_subjects: self.class.own_context_subjects,
    own_context: self.class.own_context,
    own_subject: self.class.own_subject
  )
  @commands = build_commands
end

Class Attribute Details

.aggregate ⇒ String

Returns the group's owning aggregate, e.g. "Apprenticeship".

Returns:

  • (String) —

    the group's owning aggregate, e.g. "Apprenticeship"



26
27
28
# File 'lib/yes/core/commands/command_group.rb', line 26

def aggregate
  @aggregate
end

.context ⇒ String

Returns the group's owning context, e.g. "Companies".

Returns:

  • (String) —

    the group's owning context, e.g. "Companies"



23
24
25
# File 'lib/yes/core/commands/command_group.rb', line 23

def context
  @context
end

.group_name ⇒ Symbol

Returns the group's name as defined by command_group :name.

Returns:

  • (Symbol) —

    the group's name as defined by command_group :name



29
30
31
# File 'lib/yes/core/commands/command_group.rb', line 29

def group_name
  @group_name
end

.sub_command_names ⇒ Object



35
36
37
# File 'lib/yes/core/commands/command_group.rb', line 35

def sub_command_names
  @sub_command_names ||= []
end

Instance Attribute Details

#commands ⇒ Array<Yes::Core::Command> (readonly)

Returns sub-command instances in execution order.

Returns:



87
88
89
# File 'lib/yes/core/commands/command_group.rb', line 87

def commands
  @commands
end

#group_attributes ⇒ Attributes (readonly)

Returns the reserved-key attributes of the group.

Returns:

  • (Attributes) —

    the reserved-key attributes of the group



84
85
86
# File 'lib/yes/core/commands/command_group.rb', line 84

def group_attributes
  @group_attributes
end

#normalized_payload ⇒ Hash (readonly)

Returns the payload normalized into per-context/per-subject buckets. Used internally to construct sub-command instances and exposed so callers can introspect the group's per-sub-command shape.

Returns:

  • (Hash) —

    the payload normalized into per-context/per-subject buckets. Used internally to construct sub-command instances and exposed so callers can introspect the group's per-sub-command shape.



81
82
83
# File 'lib/yes/core/commands/command_group.rb', line 81

def normalized_payload
  @normalized_payload
end

#payload ⇒ Hash (readonly)

Returns the flat input payload (reserved keys stripped). This is what guard blocks see via payload.<attr> through PayloadProxy.

Returns:

  • (Hash) —

    the flat input payload (reserved keys stripped). This is what guard blocks see via payload.<attr> through PayloadProxy.



76
77
78
# File 'lib/yes/core/commands/command_group.rb', line 76

def payload
  @payload
end

Class Method Details

.command_contexts ⇒ Array<Symbol>

Returns unique contexts of all sub-commands.

Returns:

  • (Array<Symbol>) —

    unique contexts of all sub-commands



61
62
63
# File 'lib/yes/core/commands/command_group.rb', line 61

def command_contexts
  sub_command_classes.map { |c| c.name.split('::').first.underscore.to_sym }.uniq
end

.own_context ⇒ Symbol

Returns the own context as a snake-cased symbol.

Returns:

  • (Symbol) —

    the own context as a snake-cased symbol



40
41
42
# File 'lib/yes/core/commands/command_group.rb', line 40

def own_context
  context.to_s.underscore.to_sym
end

.own_context_subjects ⇒ Array<Symbol>

Returns subjects in the own context.

Returns:

  • (Array<Symbol>) —

    subjects in the own context



66
67
68
69
70
71
# File 'lib/yes/core/commands/command_group.rb', line 66

def own_context_subjects
  sub_command_classes.
    select { |c| c.name.split('::').first.underscore.to_sym == own_context }.
    map    { |c| c.name.split('::')[1].underscore.to_sym }.
    uniq
end

.own_subject ⇒ Symbol

Returns the own subject (aggregate name) as a snake-cased symbol.

Returns:

  • (Symbol) —

    the own subject (aggregate name) as a snake-cased symbol



45
46
47
# File 'lib/yes/core/commands/command_group.rb', line 45

def own_subject
  aggregate.to_s.underscore.to_sym
end

.sub_command_classes ⇒ Array<Class>

Resolves the sub-command classes lazily so declaration order in the aggregate body does not matter.

Returns:

  • (Array<Class>) —

    sub-command classes in declaration order



53
54
55
56
57
58
# File 'lib/yes/core/commands/command_group.rb', line 53

def sub_command_classes
  sub_command_names.map do |name|
    Yes::Core.configuration.aggregate_class(context, aggregate, name, :command) ||
      raise(ArgumentError, "Sub-command '#{name}' is not defined on #{context}::#{aggregate}")
  end
end

Instance Method Details

#aggregate_id ⇒ String?

Returns the aggregate ID derived from the first sub-command.

Returns:

  • (String, nil) —

    the aggregate ID derived from the first sub-command



92
93
94
# File 'lib/yes/core/commands/command_group.rb', line 92

def aggregate_id
  commands.first&.aggregate_id
end

#to_h ⇒ Hash

Returns hash form for serialization — the FLAT input payload merged with reserved keys (transaction/origin/batch_id/metadata/ command_id). This shape round-trips cleanly: calling self.class.new(cmd.to_h) produces an equivalent group with the same payload. Used by ActiveJobSerializers::CommandGroupSerializer and by the Command API controller's add_metadata.

Returns:

  • (Hash) —

    hash form for serialization — the FLAT input payload merged with reserved keys (transaction/origin/batch_id/metadata/ command_id). This shape round-trips cleanly: calling self.class.new(cmd.to_h) produces an equivalent group with the same payload. Used by ActiveJobSerializers::CommandGroupSerializer and by the Command API controller's add_metadata.



119
120
121
122
# File 'lib/yes/core/commands/command_group.rb', line 119

def to_h
  merged = payload.merge(group_attributes.to_h)
  transaction ? merged.merge(transaction:) : merged
end