yes-core 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c43247d2dbf4c79fac0069c1fb4887a9fba4b38f845736b67dbb9ee2f213e96f
4
- data.tar.gz: f78fa82e84433fb209a0e0dc5258ee7569e1993af11436112c590c7094f73b9c
3
+ metadata.gz: b3e28057d0c8b0fd6c0bc4f4692c55609b353258637320027efe1d2bcbb02841
4
+ data.tar.gz: fdb462463528fdbecf3df473f5c0f98601a8d301ec7e4d52f98ec0b2aabdc0f3
5
5
  SHA512:
6
- metadata.gz: 1c82e17ddceef537ce4f473577e318032b71c46c090ae70b44ea0264e29ad28b6eedef7115db1aafba3f1878a238a7269d533eb0b06ba07de17422b5e2ce9d70
7
- data.tar.gz: 1ed596dba1c9b64292a3a517f823163fb2d1e36261b35fc96812be85abd919ab6b4d537da353114f45f799e0d33a47edd313d0731ec35c014d7cabd86ea8b396
6
+ metadata.gz: 415e51919c284f2207fbd6ee6dfd9a9427830a6f20ab88986a69b94c14c62b15a75d0fe19360d58088a3d4de8beec953fa78e69c180458874c1d639d677fd9e0
7
+ data.tar.gz: 85a8557a897a3c2d02b315bf1e637f2bd6837300bf65b71430c314d9a047b9663cf8d33cfaeb7a681ea2bb5eaaf645a4297e351c3304abd56c8026a03d978792
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.1.0] - 2026-04-28
4
+
5
+ - See root CHANGELOG.md for details.
6
+
3
7
  ## [1.0.0] - 2026-03-21
4
8
 
5
9
  - Initial open-source release (see root CHANGELOG.md for details)
@@ -15,6 +15,7 @@ module Yes
15
15
  included do
16
16
  class << self
17
17
  attr_accessor :_draft_context, :_draft_aggregate, :_changes_read_model_name,
18
+ :_changes_read_model_explicit,
18
19
  :_draft_foreign_key, :_is_draftable, :_changes_read_model_public
19
20
  end
20
21
  end
@@ -52,7 +53,8 @@ module Yes
52
53
  self._draft_context = draft_config[:context] || context
53
54
  self._draft_aggregate = draft_config[:aggregate] || "#{aggregate}Draft"
54
55
 
55
- self._changes_read_model_name = if changes_read_model
56
+ self._changes_read_model_explicit = changes_read_model.present?
57
+ self._changes_read_model_name = if changes_read_model.present?
56
58
  changes_read_model.to_s
57
59
  else
58
60
  "#{read_model_name}_change"
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yes
4
+ module Core
5
+ module Middlewares
6
+ class << self
7
+ # Returns middleware keys excluding the specified one.
8
+ #
9
+ # @param middleware_name [Symbol] the middleware key to exclude
10
+ # @return [Array<Symbol>] remaining middleware keys
11
+ def without(middleware_name)
12
+ PgEventstore.config.middlewares.except(middleware_name).keys
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yes
4
+ module Core
5
+ module TestSupport
6
+ module Aggregate
7
+ # DSL for writing concise aggregate command specs.
8
+ #
9
+ # Provides `command`, `success`, `invalid`, `no_change`, and `setup` methods
10
+ # that generate RSpec describe/context blocks with appropriate shared examples.
11
+ #
12
+ # @example
13
+ # RSpec.describe MyContext::MyAggregate::Aggregate, type: :aggregate do
14
+ # command 'do_something' do
15
+ # let(:command_data) { { name: 'test' } }
16
+ # let(:success_attributes) { { name: 'test' } }
17
+ #
18
+ # success
19
+ # invalid 'when precondition not met'
20
+ # no_change
21
+ # end
22
+ # end
23
+ module CommandTestDsl
24
+ # Defines a test block for a command
25
+ #
26
+ # @param command_name [String, Symbol] the name of the command to test
27
+ # @param options [Array<Hash>] additional options (e.g., `draft: true`, VCR cassettes)
28
+ # @yield block for configuring test cases with success/invalid/no_change
29
+ def command(command_name, *options, &block)
30
+ describe command_name.to_s, *options do
31
+ let(:draft) { options.first&.dig(:draft) }
32
+
33
+ let(:aggregate) { described_class.new(draft:) } unless method_defined?(:aggregate)
34
+
35
+ subject { aggregate.public_send(command, command_data, guards: !draft) }
36
+
37
+ let(:command) { command_name.to_sym }
38
+ let(:aggregate_class) { aggregate.class }
39
+ let(:command_data_with_id) do
40
+ { "#{aggregate_class.aggregate.underscore}_id" => aggregate.id }.merge(command_data)
41
+ end
42
+ let(:command_data) { {} }
43
+ let(:expected_event_type) do
44
+ "#{aggregate_class.context}::#{aggregate_class.aggregate}" \
45
+ "#{'Draft' if draft}#{aggregate_class.commands[command].event_name.to_s.classify}"
46
+ end
47
+ let(:expected_event_data) { command_data_with_id }
48
+ let(:expected_event_metadata) { nil }
49
+ let(:success_attributes) { command_data.without(:locale) } unless method_defined?(:success_attributes)
50
+
51
+ class_eval(&block) if block_given?
52
+ end
53
+ end
54
+
55
+ # Defines a test case for a successful command execution
56
+ #
57
+ # @param description [String] optional description
58
+ # @param options [Hash] additional options (e.g., VCR cassettes)
59
+ # @yield optional block for additional setup or custom assertions
60
+ def success(description = 'when successfully executing command', options = {}, &block)
61
+ context description, options do
62
+ instance_eval(&block) if block_given?
63
+
64
+ it_behaves_like 'successful command'
65
+ end
66
+ end
67
+
68
+ # Defines a test case for a command that causes no state change
69
+ #
70
+ # @param description [String] optional description
71
+ # @param options [Hash] additional options
72
+ # @yield optional block for additional setup
73
+ def no_change(description = 'when command causes no change', options = {}, &block)
74
+ context description.to_s, options do
75
+ instance_eval(&block) if block_given?
76
+
77
+ before { aggregate.public_send(command, command_data) }
78
+
79
+ it_behaves_like 'no change transition'
80
+ end
81
+ end
82
+
83
+ # Defines a test case for an invalid transition
84
+ #
85
+ # @param description [String] description of the invalid scenario
86
+ # @param options [Hash] additional options
87
+ # @yield optional block for additional setup
88
+ def invalid(description, options = {}, &block)
89
+ context "when #{description}", options do
90
+ instance_eval(&block) if block_given?
91
+
92
+ it_behaves_like 'invalid transition'
93
+ end
94
+ end
95
+
96
+ # Alias for `before` — used for readable aggregate setup within command blocks
97
+ #
98
+ # @yield block for setup actions
99
+ def setup(&)
100
+ before(&)
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ if defined?(RSpec)
109
+ RSpec.configure do |config|
110
+ config.extend Yes::Core::TestSupport::Aggregate::CommandTestDsl, type: :aggregate
111
+ end
112
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec::Matchers.define :have_authorizer do
4
+ match do |actual|
5
+ actual&.authorizer_class&.< Yes::Core::Authorization::CommandAuthorizer
6
+ end
7
+
8
+ description do
9
+ 'have an authorizer'
10
+ end
11
+ end
12
+
13
+ RSpec::Matchers.define :have_read_model_class do |read_model_class|
14
+ match do |aggregate|
15
+ aggregate.read_model_class == read_model_class
16
+ end
17
+
18
+ failure_message do |aggregate|
19
+ "expected #{aggregate} to have read model class #{read_model_class}" \
20
+ "\n actual read model class is #{aggregate.read_model_class}"
21
+ end
22
+ end
23
+
24
+ RSpec::Matchers.define :have_cerbos_authorizer do
25
+ match do |aggregate|
26
+ next false unless aggregate.authorizer_class&.< Yes::Core::Authorization::CommandCerbosAuthorizer
27
+
28
+ next false if @read_model_class && aggregate.authorizer_options&.read_model_class != @read_model_class
29
+
30
+ next false if @resource_name && aggregate.authorizer_options&.resource_name != @resource_name
31
+
32
+ true
33
+ end
34
+
35
+ chain :with_read_model_class do |read_model_class|
36
+ @read_model_class = read_model_class
37
+ end
38
+
39
+ chain :with_resource_name do |resource_name|
40
+ @resource_name = resource_name
41
+ end
42
+
43
+ description do
44
+ msg = 'have a Cerbos authorizer'
45
+ msg += " with read model class #{@read_model_class}" if @read_model_class
46
+ msg += " with resource name #{@resource_name}" if @resource_name
47
+ msg
48
+ end
49
+
50
+ failure_message do |aggregate|
51
+ msg = "expected #{aggregate} to have a Cerbos authorizer"
52
+ msg += "\n with read model class #{@read_model_class}" if @read_model_class
53
+ msg += "\n with resource name #{@resource_name}" if @resource_name
54
+ msg += "\n actual read model class is #{aggregate.authorizer_options&.read_model_class}" if @read_model_class
55
+ msg += "\n actual resource name is #{aggregate.authorizer_options&.resource_name}" if @resource_name
56
+ msg
57
+ end
58
+ end
59
+
60
+ RSpec::Matchers.define :have_parent do |parent_name|
61
+ match do |aggregate|
62
+ @parent = aggregate.parent_aggregates.with_indifferent_access[parent_name]
63
+
64
+ return false unless @parent
65
+
66
+ return true unless @context
67
+
68
+ @parent[:context] == @context
69
+ end
70
+
71
+ chain :with_context do |context|
72
+ @context = context
73
+ end
74
+
75
+ failure_message do |aggregate|
76
+ msg = "expected #{aggregate} to have parent #{parent_name}"
77
+ msg += "\n with context #{@context}" if @context
78
+ msg += "\n actual context is #{@parent[:context].presence || aggregate.context}" if @parent && @context
79
+ msg
80
+ end
81
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_context 'with given events' do
4
+ before do
5
+ given_events.each do |event_data|
6
+ event = event_instance(event_data)
7
+ stream = event_stream(event_data)
8
+ append_event(stream, event)
9
+ end
10
+ end
11
+ end
12
+
13
+ RSpec.shared_examples 'successful command' do
14
+ let(:event) { aggregate.events.map(&:last).last }
15
+ let(:expected_event_metadata) { nil }
16
+
17
+ it 'correctly changes the aggregate state' do
18
+ if success_attributes.any?
19
+ expect { subject }.to change {
20
+ aggregate.read_model.attributes.to_h.symbolize_keys.slice(*success_attributes.keys)
21
+ }.to(success_attributes)
22
+ end
23
+ end
24
+
25
+ it 'publishes expected event' do
26
+ subject
27
+ aggregate_failures do
28
+ expect(event.type).to eq(expected_event_type)
29
+ expect(event.data).to eq(expected_event_data.deep_stringify_keys)
30
+ expect(event.metadata).to include(expected_event_metadata.deep_stringify_keys) if expected_event_metadata
31
+ end
32
+ end
33
+ end
34
+
35
+ RSpec.shared_examples 'invalid transition' do
36
+ it 'raises InvalidTransition error' do
37
+ expect(subject.error).to be_a(Yes::Core::CommandHandling::GuardEvaluator::InvalidTransition)
38
+ end
39
+
40
+ it 'does not change the aggregate state' do
41
+ success_attributes.each_key do |attribute|
42
+ expect { subject }.not_to(change { aggregate.public_send(attribute) })
43
+ end
44
+ end
45
+
46
+ it 'does not publish event' do
47
+ expect { subject }.not_to(
48
+ change do
49
+ aggregate.events.map(&:flatten).flatten.count
50
+ rescue PgEventstore::StreamNotFoundError
51
+ nil
52
+ end
53
+ )
54
+ end
55
+ end
56
+
57
+ RSpec.shared_examples 'no change transition' do
58
+ it 'raises NoChangeTransition error' do
59
+ expect(subject.error).to be_a(Yes::Core::CommandHandling::GuardEvaluator::NoChangeTransition)
60
+ end
61
+
62
+ it 'does not change the aggregate state' do
63
+ success_attributes.each_key do |attribute|
64
+ expect { subject }.not_to(change { aggregate.public_send(attribute) })
65
+ end
66
+ end
67
+
68
+ it 'does not publish event' do
69
+ expect { subject }.not_to(
70
+ change do
71
+ aggregate.events.map(&:flatten).flatten.count
72
+ rescue PgEventstore::StreamNotFoundError
73
+ nil
74
+ end
75
+ )
76
+ end
77
+ end
@@ -4,16 +4,33 @@ module Yes
4
4
  module Core
5
5
  module TestSupport
6
6
  # Helpers for working with PgEventstore events in tests.
7
+ #
8
+ # @example Include in RSpec
9
+ # RSpec.configure do |config|
10
+ # config.include Yes::Core::TestSupport::EventHelpers
11
+ # end
7
12
  module EventHelpers
13
+ # Appends an event to a stream
14
+ #
15
+ # @param stream [PgEventstore::Stream]
16
+ # @param event [Yes::Core::Event]
17
+ # @return [void]
18
+ def append_event(stream, event)
19
+ PgEventstore.client.append_to_stream(stream, event)
20
+ end
21
+
22
+ # Appends an event to a stream and reloads it
23
+ #
8
24
  # @param stream [PgEventstore::Stream]
9
25
  # @param event [Yes::Core::Event]
10
26
  # @return [Yes::Core::Event]
11
27
  def append_and_reload_event(stream, event)
12
- PgEventstore.client.append_to_stream(stream, event)
28
+ append_event(stream, event)
13
29
  PgEventstore.client.read(stream, options: { max_count: 1, direction: :desc }).first
14
30
  end
15
31
 
16
32
  # Reads eventstore and returns events from the stream or an empty array if stream does not exist
33
+ #
17
34
  # @param stream [PgEventstore::Stream]
18
35
  # @return [Array<Yes::Core::Event>]
19
36
  def safe_read(stream)
@@ -21,6 +38,61 @@ module Yes
21
38
  rescue PgEventstore::StreamNotFoundError
22
39
  []
23
40
  end
41
+
42
+ alias read_events safe_read
43
+
44
+ # Creates events from a block and appends them to the eventstore
45
+ #
46
+ # @yield block that returns an array of event attribute hashes
47
+ # @yieldreturn [Array<Hash>] each hash should have :context, :aggregate, :event, :data keys
48
+ # @return [void]
49
+ #
50
+ # @example
51
+ # given_events do
52
+ # [{ context: 'MyContext', aggregate: 'MyAggregate', event: 'Created', data: { id: '123' } }]
53
+ # end
54
+ def given_events(&)
55
+ events_data = yield
56
+ events_data.each do |event_data|
57
+ event = event_instance(event_data)
58
+ stream = event_stream(event_data)
59
+ append_event(stream, event)
60
+ end
61
+ end
62
+
63
+ # Builds a Yes::Core::Event from a hash of event attributes
64
+ #
65
+ # @param event_attrs [Hash] event attributes with :context, :aggregate, :event, :data keys
66
+ # @return [Yes::Core::Event]
67
+ def event_instance(event_attrs)
68
+ Yes::Core::Event.new(
69
+ type: event_type(event_attrs),
70
+ data: (event_attrs[:data] || {}).with_indifferent_access
71
+ )
72
+ end
73
+
74
+ # Builds a PgEventstore::Stream from event attributes
75
+ #
76
+ # @param event_attrs [Hash] event attributes with :context, :aggregate, :data keys
77
+ # @return [PgEventstore::Stream]
78
+ def event_stream(event_attrs)
79
+ return event_attrs[:stream] if event_attrs[:stream]
80
+
81
+ PgEventstore::Stream.new(
82
+ context: event_attrs[:context],
83
+ stream_name: event_attrs[:aggregate],
84
+ stream_id: event_attrs[:data].first.last
85
+ )
86
+ end
87
+
88
+ # Constructs an event type string from event attributes
89
+ #
90
+ # @param event_attrs [Hash] event attributes with :context, :aggregate/:subject, :event keys
91
+ # @return [String]
92
+ def event_type(event_attrs)
93
+ aggregate_or_subject = event_attrs[:aggregate] || event_attrs[:subject]
94
+ "#{event_attrs[:context]}::#{aggregate_or_subject}#{event_attrs[:event]}"
95
+ end
24
96
  end
25
97
  end
26
98
  end
@@ -3,3 +3,6 @@
3
3
  require_relative 'test_support/event_helpers'
4
4
  require_relative 'test_support/jwt_helpers'
5
5
  require_relative 'test_support/test_helper'
6
+ require_relative 'test_support/aggregate/command_test_dsl'
7
+ require_relative 'test_support/aggregate/shared_examples'
8
+ require_relative 'test_support/aggregate/matchers'
@@ -3,14 +3,20 @@
3
3
  module Yes
4
4
  module Core
5
5
  module Utils
6
- # Provides convenient shortcuts for accessing aggregate classes in Rails console
7
- # @example
8
- # # Instead of: ApprenticeshipPresentation::Apprenticeship::Aggregate.new(id)
9
- # # Use: AP::Appr.new(id)
6
+ # Provides convenient shortcuts for accessing aggregate classes in Rails console.
7
+ # @example Multi-capital subjects use capitals-only abbreviations
8
+ # # Instead of: ApprenticeshipPresentation::ContactInfo::Aggregate.new(id)
9
+ # # Use: AP::CI.new(id)
10
+ # @example Single-capital subjects keep the full name
11
+ # # Instead of: TaskFlow::Board::Aggregate.new(id)
12
+ # # Use: TF::Board.new(id)
10
13
  class AggregateShortcuts
11
14
  class << self
12
- # Load aggregate shortcuts in Rails console
13
- # Creates module aliases and constants for convenient access
15
+ # Load aggregate shortcuts in Rails console.
16
+ # Creates fresh shortcut modules (e.g. TF) and assigns aggregate classes
17
+ # as constants on them. Shortcut modules are NOT aliases of the real
18
+ # context modules, so shortcut constants cannot collide with the
19
+ # aggregates' own namespace modules.
14
20
  def load!
15
21
  return unless Yes::Core.configuration.aggregate_shortcuts
16
22
 
@@ -102,19 +108,22 @@ module Yes
102
108
  context_abbr = abbreviate_context(agg[:context])
103
109
  subject_abbr = abbreviate_subject(agg[:subject])
104
110
 
105
- # Create context module alias if not exists
111
+ # Build (or reuse) a fresh container module for the context shortcut.
112
+ # We deliberately do NOT alias the real context module: doing so would
113
+ # mean shortcut constants (e.g. TF::Board) collide with the real
114
+ # namespace modules of the aggregates themselves (TaskFlow::Board).
106
115
  unless context_modules[context_abbr]
107
116
  if Object.const_defined?(context_abbr)
108
117
  Rails.logger.warn("Shortcut conflict: #{context_abbr} already defined, skipping #{agg[:context]}")
109
118
  next
110
119
  end
111
120
 
112
- context_module = agg[:context].constantize
113
- Object.const_set(context_abbr, context_module)
114
- context_modules[context_abbr] = context_module
121
+ shortcut_module = Module.new
122
+ Object.const_set(context_abbr, shortcut_module)
123
+ context_modules[context_abbr] = shortcut_module
115
124
  end
116
125
 
117
- # Create subject constant within context module
126
+ # Create subject constant within the shortcut container.
118
127
  context_mod = context_modules[context_abbr]
119
128
  if context_mod.const_defined?(subject_abbr)
120
129
  Rails.logger.warn("Shortcut conflict: #{context_abbr}::#{subject_abbr} already defined")
@@ -140,12 +149,15 @@ module Yes
140
149
  def abbreviate_subject(subject)
141
150
  return @subject_overrides[subject] if @subject_overrides[subject]
142
151
 
143
- # First try capital letters
152
+ # Multi-capital CamelCase names get a capitals-only abbreviation
153
+ # (ContactInfo → CI). Single-capital names (Task, Board, Location)
154
+ # use the full subject name to avoid awkward truncations like
155
+ # "Boar" or shortcut collisions when the truncation matches the
156
+ # subject's own namespace module (e.g. TaskFlow::Task).
144
157
  capitals = subject.scan(/[A-Z]/).join
145
158
  return capitals if capitals.length > 1
146
159
 
147
- # Otherwise use first 4 characters
148
- subject[0..3]
160
+ subject
149
161
  end
150
162
 
151
163
  def define_helper_method
@@ -68,7 +68,7 @@ module Yes
68
68
  def build_event(command_name:, payload:, metadata: {})
69
69
  event_class = Yes::Core.configuration.event_classes_for_command(context, aggregate, command_name).first
70
70
  event_class.new(
71
- type: "#{context}::#{aggregate_name_with_draft_suffix(aggregate, metadata)}#{event_class.name.demodulize}",
71
+ type: "#{context}::#{aggregate_name_with_draft_suffix(aggregate, metadata, context:)}#{event_class.name.demodulize}",
72
72
  data: payload,
73
73
  metadata:
74
74
  )
@@ -83,7 +83,7 @@ module Yes
83
83
  def build_stream(context: @context, name: @aggregate, id: @aggregate_id, metadata: {})
84
84
  PgEventstore::Stream.new(
85
85
  context:,
86
- stream_name: aggregate_name_with_draft_suffix(name, metadata),
86
+ stream_name: aggregate_name_with_draft_suffix(name, metadata, context:),
87
87
  stream_id: id
88
88
  )
89
89
  end
@@ -209,16 +209,33 @@ module Yes
209
209
  payload.merge(locale: I18n.locale.to_s)
210
210
  end
211
211
 
212
- # Builds the aggregate name with the draft suffix
212
+ # Builds the aggregate name with the draft suffix.
213
+ #
214
+ # When the aggregate class is configured with `draftable changes_read_model:` (explicitly),
215
+ # the camelized changes_read_model is used as the stream / event-type prefix so the DSL
216
+ # config decides where draft events land. This lets an aggregate share an edit-template
217
+ # stream with a sibling (e.g. `Recruiter` writes to `UserEditTemplate` via
218
+ # `changes_read_model: :user_edit_template`).
219
+ #
220
+ # For non-draftable aggregates, and for draftable aggregates that rely on the default
221
+ # `<read_model>_change` changes_read_model name, the legacy hard-coded `<Aggregate>Draft`
222
+ # / `<Aggregate>EditTemplate` suffix is used.
213
223
  #
214
224
  # @param aggregate_name [String] The name of the aggregate
215
225
  # @param metadata [Hash] The command metadata
216
- # @return [String] The stream name
217
- def aggregate_name_with_draft_suffix(aggregate_name, metadata = {})
218
- return "#{aggregate_name}Draft" if metadata&.dig(:draft)
219
- return "#{aggregate_name}EditTemplate" if metadata&.dig(:edit_template_command)
220
-
221
- aggregate_name
226
+ # @param context [String] The aggregate's context (defaults to @context)
227
+ # @return [String] The stream / event-type name component
228
+ def aggregate_name_with_draft_suffix(aggregate_name, metadata = {}, context: @context)
229
+ return aggregate_name unless metadata&.dig(:draft) || metadata&.dig(:edit_template_command)
230
+
231
+ klass = "#{context}::#{aggregate_name}::Aggregate".safe_constantize
232
+ if klass.respond_to?(:_changes_read_model_explicit) && klass._changes_read_model_explicit
233
+ klass.changes_read_model_name.camelize
234
+ elsif metadata&.dig(:edit_template_command)
235
+ "#{aggregate_name}EditTemplate"
236
+ else
237
+ "#{aggregate_name}Draft"
238
+ end
222
239
  end
223
240
  end
224
241
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Yes
4
4
  module Core
5
- VERSION = '1.0.0'
5
+ VERSION = '1.1.0'
6
6
  end
7
7
  end
data/lib/yes/core.rb CHANGED
@@ -19,6 +19,7 @@ module Yes
19
19
  loader.push_dir(File.expand_path('..', __dir__))
20
20
  loader.ignore("#{__dir__}/core/version.rb")
21
21
  loader.ignore("#{__dir__}/core/test_support")
22
+ loader.ignore("#{__dir__}/core/test_support.rb")
22
23
  loader.collapse("#{__dir__}/core/models")
23
24
  loader.setup
24
25
  loader
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yes-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nico Ritsche
@@ -259,6 +259,7 @@ files:
259
259
  - lib/yes/core/generators/read_models/templates/migration.rb.erb
260
260
  - lib/yes/core/generators/read_models/update_generator.rb
261
261
  - lib/yes/core/jobs/read_model_recovery_job.rb
262
+ - lib/yes/core/middlewares.rb
262
263
  - lib/yes/core/middlewares/encryptor.rb
263
264
  - lib/yes/core/middlewares/timestamp.rb
264
265
  - lib/yes/core/middlewares/with_indifferent_access.rb
@@ -281,6 +282,9 @@ files:
281
282
  - lib/yes/core/serializer.rb
282
283
  - lib/yes/core/subscriptions.rb
283
284
  - lib/yes/core/test_support.rb
285
+ - lib/yes/core/test_support/aggregate/command_test_dsl.rb
286
+ - lib/yes/core/test_support/aggregate/matchers.rb
287
+ - lib/yes/core/test_support/aggregate/shared_examples.rb
284
288
  - lib/yes/core/test_support/event_helpers.rb
285
289
  - lib/yes/core/test_support/jwt_helpers.rb
286
290
  - lib/yes/core/test_support/subscriptions_helper.rb