Class: Yes::Core::Utils::CommandUtils Private

Inherits:
Object
  • Object
show all
Defined in:
lib/yes/core/utils/command_utils.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Handles command and handler class operations for aggregates

Since:

  • 0.1.0

Defined Under Namespace

Classes: CommandNotFoundError

Constant Summary collapse

ASSIGN_COMMAND_PREFIX =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Since:

  • 0.1.0

'assign_'

Instance Method Summary collapse

Constructor Details

#initialize(context:, aggregate:, aggregate_id:) ⇒ CommandUtils

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of CommandUtils.

Parameters:

  • context (String)

    The context namespace

  • aggregate (String)

    The aggregate name

  • aggregate_id (String)

    The ID of the aggregate

Since:

  • 0.1.0



18
19
20
21
22
# File 'lib/yes/core/utils/command_utils.rb', line 18

def initialize(context:, aggregate:, aggregate_id:)
  @context = context
  @aggregate = aggregate
  @aggregate_id = aggregate_id
end

Instance Method Details

#build_attribute_command(attribute, payload) ⇒ Yes::Core::Command

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Builds a change command instance for the given attribute and payload

Parameters:

  • attribute (Symbol)

    The attribute name

  • payload (Hash)

    The command payload

Returns:

Raises:

  • (RuntimeError)

    If the command class cannot be found

Since:

  • 0.1.0



30
31
32
# File 'lib/yes/core/utils/command_utils.rb', line 30

def build_attribute_command(attribute, payload)
  build_command(:"change_#{attribute}", payload)
end

#build_command(command_name, payload) ⇒ Yes::Core::Command

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Builds a command instance for the given command name and payload

Parameters:

  • command_name (Symbol)

    The command name

  • payload (Hash)

    The command payload

Returns:

Raises:

  • (RuntimeError)

    If the command class cannot be found

Since:

  • 0.1.0



40
41
42
43
# File 'lib/yes/core/utils/command_utils.rb', line 40

def build_command(command_name, payload)
  command_class = fetch_class(command_name, :command)
  command_class.new("#{aggregate.underscore}_id": aggregate_id, **payload)
end

#build_event(command_name:, payload:, metadata: {}) ⇒ PgEventstore::Event

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Builds a PgEventstore::Event instance

Parameters:

  • command_name (Symbol)

    The command name

  • payload (Hash)

    The event payload

  • metadata (Hash) (defaults to: {})

    Event metadata

Returns:

  • (PgEventstore::Event)

    The event instance

Since:

  • 0.1.0



68
69
70
71
72
73
74
75
# File 'lib/yes/core/utils/command_utils.rb', line 68

def build_event(command_name:, payload:, metadata: {})
  event_class = Yes::Core.configuration.event_classes_for_command(context, aggregate, command_name).first
  event_class.new(
    type: "#{context}::#{aggregate_name_with_draft_suffix(aggregate, , context:)}#{event_class.name.demodulize}",
    data: payload,
    metadata:
  )
end

#build_stream(context: @context, name: @aggregate, id: @aggregate_id, metadata: {}) ⇒ PgEventstore::Stream

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Builds a PgEventstore::Stream instance

Parameters:

  • context (String) (defaults to: @context)

    The context name

  • name (String) (defaults to: @aggregate)

    The stream name

  • id (String) (defaults to: @aggregate_id)

    The stream ID

Returns:

  • (PgEventstore::Stream)

    The stream instance

Since:

  • 0.1.0



83
84
85
86
87
88
89
# File 'lib/yes/core/utils/command_utils.rb', line 83

def build_stream(context: @context, name: @aggregate, id: @aggregate_id, metadata: {})
  PgEventstore::Stream.new(
    context:,
    stream_name: aggregate_name_with_draft_suffix(name, , context:),
    stream_id: id
  )
end

#command_name_from_event(event, aggregate_class) ⇒ Symbol

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns The command name.

Parameters:

  • event (PgEventstore::Event)

    The event

  • aggregate_class (Class)

    The aggregate class

Returns:

  • (Symbol)

    The command name

Raises:

Since:

  • 0.1.0



122
123
124
125
126
127
128
# File 'lib/yes/core/utils/command_utils.rb', line 122

def command_name_from_event(event, aggregate_class)
  event_name = event.type.split('::').last.sub(event.stream.stream_name.sub(/(Draft|EditTemplate)/, ''), '').underscore
  command = aggregate_class.commands.values.find { _1.event_name.to_s == event_name }
  raise CommandNotFoundError, "Command not found for event #{event_name}" unless command

  command.name
end

#fetch_guard_evaluator_class(name) ⇒ Class

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Fetches the guard evaluator class for a given command name

Parameters:

  • name (Symbol)

    The command name

Returns:

  • (Class)

    The guard evaluator class

Raises:

  • (RuntimeError)

    If the guard evaluator class cannot be found

Since:

  • 0.1.0



50
51
52
# File 'lib/yes/core/utils/command_utils.rb', line 50

def fetch_guard_evaluator_class(name)
  fetch_class(name, :guard_evaluator)
end

#fetch_state_updater_class(name) ⇒ Class

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Fetches the state updater class for a given command name

Parameters:

  • name (Symbol)

    The command name

Returns:

  • (Class)

    The state updater class

Since:

  • 0.1.0



58
59
60
# File 'lib/yes/core/utils/command_utils.rb', line 58

def fetch_state_updater_class(name)
  fetch_class(name, :state_updater)
end

#prepare_assign_command_payload(command_name, payload) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Since:

  • 0.1.0



105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/yes/core/utils/command_utils.rb', line 105

def prepare_assign_command_payload(command_name, payload)
  return payload unless command_name.to_s.starts_with?(ASSIGN_COMMAND_PREFIX)

  attribute_name = command_name.to_s.split(ASSIGN_COMMAND_PREFIX).last.to_sym
  name_with_id = :"#{attribute_name}_id"
  key = payload.key?(attribute_name) ? attribute_name : name_with_id

  return payload unless payload[key].is_a?(Yes::Core::Aggregate)

  payload[name_with_id] = payload.delete(key).id
  payload
end

#prepare_command_payload(command_name, payload, aggregate_class) ⇒ Hash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Prepares the payload for a command

Parameters:

  • command_name (Symbol)

    The command name

  • payload (Hash)

    The command payload

  • aggregate_class (Class)

    The aggregate class

Returns:

  • (Hash)

    The prepared payload

Since:

  • 0.1.0



136
137
138
139
140
141
142
143
# File 'lib/yes/core/utils/command_utils.rb', line 136

def prepare_command_payload(command_name, payload, aggregate_class)
  return append_locale_param(command_name, payload, aggregate_class) if payload.is_a?(Hash)

  payload_attributes = aggregate_class.commands[command_name].payload_attributes.except(:locale)
  raise 'Payload attributes must be a Hash with a single key (not including locale key)' if payload_attributes.length > 1

  append_locale_param(command_name, { payload_attributes.keys.first => payload }, aggregate_class)
end

#prepare_default_payload(command_name, payload, aggregate_class) ⇒ Hash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Prefills default values in the payload if they are not provided

Parameters:

  • command_name (Symbol)

    The command name

  • payload (Hash)

    The command payload

  • aggregate_class (Class)

    The aggregate class

Returns:

  • (Hash)

    The prepared payload

Since:

  • 0.1.0



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/yes/core/utils/command_utils.rb', line 151

def prepare_default_payload(command_name, payload, aggregate_class)
  return payload unless payload.is_a?(Hash)

  attributes_with_defaults = aggregate_class.commands[command_name].payload_attributes.select do |_key, value|
    value.is_a?(Hash) && value.key?(:default)
  end

  return payload unless attributes_with_defaults.any?

  additions = {}
  attributes_with_defaults.each do |key, value|
    next if payload.key?(key)

    default = value[:default]
    additions[key] = default.respond_to?(:call) ? default.call : default
  end
  payload.merge(additions)
end

#stream_revision(stream) ⇒ Integer

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Gets the current revision of a stream

Parameters:

  • stream (PgEventstore::Stream)

    The stream to check

Returns:

  • (Integer)

    The current revision

Since:

  • 0.1.0



95
96
97
98
99
100
101
102
103
# File 'lib/yes/core/utils/command_utils.rb', line 95

def stream_revision(stream)
  PgEventstore.client.read(
    stream,
    options: { direction: 'Backwards', max_count: 1 },
    middlewares: []
  ).first&.stream_revision || 0
rescue PgEventstore::StreamNotFoundError
  :no_stream
end