Class: Yes::Core::Event

Inherits:
PgEventstore::Event
  • Object
show all
Defined in:
lib/yes/core/event.rb

Overview

Base event class for all events in the system. Inherits from PgEventstore::Event for PostgreSQL-based event storage.

Defined Under Namespace

Modules: VersionedEvent

Constant Summary collapse

DEFAULT_VERSION =
1
PAYLOAD_STORE_VALUE_PREFIX =
'payload-store:'
InvalidDataError =
Class.new(StandardError)

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(**attrs) ⇒ Event

Returns a new instance of Event.



107
108
109
110
# File 'lib/yes/core/event.rb', line 107

def initialize(**attrs)
  validate(attrs[:data]) unless attrs[:skip_validation]
  super
end

Class Method Details

.inherited(subclass) ⇒ Object

Initializes class variables for subclasses.

Parameters:

  • subclass (Class)


99
100
101
102
103
104
# File 'lib/yes/core/event.rb', line 99

def inherited(subclass)
  subclass.instance_variable_set(:@versions, {})
  subclass.instance_variable_set(:@ps_store_fields, @ps_store_fields)

  super
end

.new(**args) ⇒ Yes::Core::Event

Instantiates a new event. If no version is given, the default version is used.

Parameters:

  • args (Hash)

Returns:



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/yes/core/event.rb', line 45

def new(**args)
  # Prevent creating infinite subclasses of versioned events
  return super if include?(VersionedEvent)

  options = args.dup
  version = options[:version] || options.dig(:metadata, 'version')

  # Add version to custom metadata if provided
  if options[:version]
    options[:metadata] = options[:metadata]&.dup || {}
    options[:metadata]['version'] ||= version
  end

  versioned_event_class(version).new(**options)
end

.payload_store_fields(fields) ⇒ Object

Defines the fields that are stored in the payload store

Parameters:

  • fields (Array<Symbol>)


75
76
77
# File 'lib/yes/core/event.rb', line 75

def payload_store_fields(fields)
  @ps_store_fields = [*fields].map(&:to_s)
end

.ps_store_fieldsArray<String>

Returns the fields that are stored in the payload store.

Returns:

  • (Array<String>)

    the fields that are stored in the payload store



80
81
82
# File 'lib/yes/core/event.rb', line 80

def ps_store_fields
  @ps_store_fields&.map(&:to_s) || []
end

.version(number, &blk) ⇒ Object

Defines a new version of the event. The block is evaluated in the context of the new event class.

Parameters:

  • number (Integer)

    The version number

  • blk (Proc)

    The block containing schema and transformations

Raises:

  • (ArgumentError)


88
89
90
91
92
93
94
95
# File 'lib/yes/core/event.rb', line 88

def version(number, &blk)
  raise ArgumentError, 'Version number must be an integer' unless number.is_a?(Integer)

  undefined_versions = (1...number).reject { |n| @versions.key?(n) }
  raise ArgumentError, "Previous versions not defined: #{undefined_versions.join(', ')}" if undefined_versions.any?

  @versions[number] = blk
end

.versioned_event_class(version) ⇒ Class<Yes::Core::Event>

Returns a dynamic event class as a subclass of the current event class. This is done so that we can have different versions of the same event with different schemas.

Parameters:

  • version (Integer)

Returns:



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

def versioned_event_class(version)
  Class.new(self, &@versions[version || DEFAULT_VERSION]).tap do |versioned_event_class|
    versioned_event_class.instance_variable_set(:@_class_name, name)
    versioned_event_class.include(VersionedEvent)
  end
end

Instance Method Details

#as_jsonHash

Returns:

  • (Hash)


115
116
117
# File 'lib/yes/core/event.rb', line 115

def as_json(*)
  to_h.transform_keys(&:to_s)
end

#encrypted?Boolean

Checks whether the event contains encrypted data

Returns:

  • (Boolean)


135
136
137
# File 'lib/yes/core/event.rb', line 135

def encrypted?
  &.key?('encryption') && !['encryption'].empty?
end

#otl_context(type:) ⇒ Hash

Returns the otl context.

Parameters:

  • type (Symbol, nil)

    :publisher or :root

Returns:

  • (Hash)

    the otl context



151
152
153
154
155
# File 'lib/yes/core/event.rb', line 151

def otl_context(type:)
  return ['otl_contexts'] unless type

  .dig('otl_contexts', type.to_s) || {}
end

#otl_context=(context, type:) ⇒ Object

Parameters:



160
161
162
163
164
165
# File 'lib/yes/core/event.rb', line 160

def otl_context=(context, type:)
  return ['otl_contexts'] = context unless type

  ['otl_contexts'] ||= {}
  ['otl_contexts'][type.to_s] = context
end

#ps_fields_with_valuesHash

Returns payload store fields with their values.

Returns:

  • (Hash)

    payload store fields with their values



125
126
127
128
129
130
131
# File 'lib/yes/core/event.rb', line 125

def ps_fields_with_values
  data.select do |_, value|
    next unless value.is_a?(String)

    value.start_with?(PAYLOAD_STORE_VALUE_PREFIX) && value.split(':').last =~ Types::UUID_REGEXP
  end
end

#schemaObject

event schema



180
# File 'lib/yes/core/event.rb', line 180

def schema; end

#to_jsonString

Returns:

  • (String)


120
121
122
# File 'lib/yes/core/event.rb', line 120

def to_json(*)
  as_json.to_json(*)
end

#transform(direction) ⇒ Event

Transforms the event to a new version

Parameters:

  • direction (Symbol)

    :up or :down

Returns:

  • (Event)

    the transformed event

Raises:

  • (ArgumentError)


170
171
172
173
174
175
176
177
# File 'lib/yes/core/event.rb', line 170

def transform(direction)
  return self unless self.class.include?(VersionedEvent)
  raise ArgumentError, 'Direction is not valid' unless %i[up down].include?(direction)

  transformed_event = DataTransformation.new(send(direction), data).call
  v = direction == :up ? version + 1 : version - 1
  Yes::Core::Event.new(data: transformed_event, version: v)
end

#versionInteger

Returns the event version.

Returns:

  • (Integer)

    the event version



140
141
142
# File 'lib/yes/core/event.rb', line 140

def version
  ['version'] || DEFAULT_VERSION
end

#version=(version) ⇒ Object

Parameters:

  • version (Integer)

    the event version



145
146
147
# File 'lib/yes/core/event.rb', line 145

def version=(version)
  ['version'] = version
end