rails_simple_event_sourcing 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9789524ed9ede4a4a16561d587fbfc729fe825d0e4196390b6bd39d4196ed56d
4
+ data.tar.gz: 5bbc7f33e45dd59ce805b06cea72a84a4a8f63c852fc859270eef9f2b8d11cb7
5
+ SHA512:
6
+ metadata.gz: 4112017f8f52f621b401dc463be3673f635176bfcee2732a7a0502757d4472a2897328169a68ddea60e4e8ec9726068d45daa8f1f4d70e6b471d42efffbd0da7
7
+ data.tar.gz: cbe47f8933ff692665a019fce022e1f52bdc849067fb7ae10bbebef02a9fb3c5eb6222d28d3ce5205249e37466a6733544de7dbd388f4aae43fc4a1219cb96c2
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright Damian Baćkowski
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,268 @@
1
+ # RailsSimpleEventSourcing ![tests](https://github.com/dbackowski/rails_simple_event_sourcing/actions/workflows/minitest.yml/badge.svg) ![codecheck](https://github.com/dbackowski/rails_simple_event_sourcing/actions/workflows/codecheck.yml/badge.svg)
2
+
3
+ This is a very minimalist implementation of an event sourcing pattern, if you want a full-featured framework in ruby you can check out one of these:
4
+ - https://www.sequent.io
5
+ - https://railseventstore.org
6
+
7
+ I wanted to learn how to build this from scratch and also wanted to build something that would be very easy to use since most of the fully featured frameworks like the two above require a lot of configuration and learning.
8
+
9
+ ### Important notice
10
+
11
+ This plugin will only work with Postgres database because it uses JSONB data type which is only supported by this database.
12
+
13
+ ## Usage
14
+
15
+ So how does it all work?
16
+
17
+ Let's start with the directory structure:
18
+
19
+ ```
20
+ app/
21
+ ├─ domain/
22
+ │ ├─ customer/
23
+ │ │ ├─ command_handlers/
24
+ │ │ │ ├─ create.rb
25
+ │ │ ├─ events/
26
+ │ │ │ ├─ customer_created.rb
27
+ │ │ ├─ commands/
28
+ │ │ │ ├─ create.rb
29
+ ```
30
+
31
+ The name of the top directory can be different because Rails does not namespace it.
32
+
33
+ Based on the example above, the usage looks like this
34
+
35
+ Command -> Command Handler -> Create Event (which under the hood writes changes to the appropriate model)
36
+
37
+ Explanation of each of these blocks above:
38
+
39
+ - `Command` - is responsible for any action you want to take in your system, it is also responsible for validating the input parameters it takes (you can use the same validations you would normally use in models).
40
+
41
+ Example:
42
+
43
+ ```ruby
44
+ class Customer
45
+ module Commands
46
+ class Create < RailsSimpleEventSourcing::Commands::Base
47
+ attr_accessor :first_name, :last_name, :email
48
+
49
+ validates :first_name, presence: true
50
+ validates :last_name, presence: true
51
+ validates :email, presence: true
52
+ end
53
+ end
54
+ end
55
+ ```
56
+
57
+ - `CommandHandler` - is responsible for handling the passed command (it automatically checks if a command is valid), making additional API calls, doing additional business logic, and finally creating a proper event. This should always return the `RailsSimpleEventSourcing::Result` struct.
58
+
59
+ This struct has 3 keywords:
60
+ - `success?:` true/false (if everything went well, commands are automatically validated, but there might still be an API call here, etc., so you can return false if something went wrong)
61
+ - `data:` data that you want to return, eg. to the controller (in the example above the `event.aggregate` will return a proper instance of the Customer model)
62
+ - `errors:` in a scenario where you set `success?:false`, you can also return some related errors here (see: test/dummy/app/domain/customer/command_handlers/create.rb for an example)
63
+
64
+ Example:
65
+
66
+ ```ruby
67
+ class Customer
68
+ module CommandHandlers
69
+ class Create < RailsSimpleEventSourcing::CommandHandlers::Base
70
+ def call
71
+ event = Customer::Events::CustomerCreated.create(
72
+ first_name: @command.first_name,
73
+ last_name: @command.last_name,
74
+ email: @command.email,
75
+ created_at: Time.zone.now,
76
+ updated_at: Time.zone.now
77
+ )
78
+
79
+ RailsSimpleEventSourcing::Result.new(success?: true, data: event.aggregate)
80
+ end
81
+ end
82
+ end
83
+ end
84
+ ```
85
+
86
+ - `Event` - is responsible for storing immutable data of your actions, you should use past tense for naming events since an event is something that has already happened (e.g. customer was created)
87
+
88
+ Example:
89
+
90
+ ```ruby
91
+ class Customer
92
+ module Events
93
+ class CustomerCreated < RailsSimpleEventSourcing::Event
94
+ aggregate_model_name Customer
95
+ event_attributes :first_name, :last_name, :email, :created_at, :updated_at
96
+
97
+ def apply(aggregate)
98
+ aggregate.id = aggregate_id
99
+ aggregate.first_name = first_name
100
+ aggregate.last_name = last_name
101
+ aggregate.email = email
102
+ aggregate.created_at = created_at
103
+ aggregate.updated_at = updated_at
104
+ end
105
+ end
106
+ end
107
+ end
108
+ ```
109
+
110
+ In the example above:
111
+ - `aggregate_model_name` is used for the corresponding model (each model is normally set to read-only mode since the only way to modify it should be via events), this param is optional since you can have an event that is not applied to the model, e.g. UserLoginAlreadyTaken
112
+ - `event_attributes` - defines params that will be stored in the event and these params will be available to apply to the model via the `apply(aggregate)` method (where aggregate is an instance of your model passed in aggregate_model_name).
113
+
114
+ Here is an example of a custom controller that uses all the blocks described above:
115
+
116
+ ```ruby
117
+ class CustomersController < ApplicationController
118
+ def create
119
+ cmd = Customer::Commands::Create.new(
120
+ first_name: params[:first_name],
121
+ last_name: params[:last_name],
122
+ email: params[:email]
123
+ )
124
+ handler = RailsSimpleEventSourcing::CommandHandler.new(cmd).call
125
+
126
+ if handler.success?
127
+ render json: handler.data
128
+ else
129
+ render json: { errors: handler.errors }, status: :unprocessable_entity
130
+ end
131
+ end
132
+ end
133
+ ```
134
+
135
+ Now, if you make an API call using curl:
136
+
137
+ ```sh
138
+ curl -X POST http://localhost:3000/customers \
139
+ -H 'Content-Type: application/json' \
140
+ -d '{ "first_name": "John", "last_name": "Doe" }' | jq
141
+ ```
142
+
143
+ You will get the response:
144
+
145
+ ```json
146
+ {
147
+ "id": 1,
148
+ "first_name": "John",
149
+ "last_name": "Doe",
150
+ "created_at": "2024-08-03T16:52:30.829Z",
151
+ "updated_at": "2024-08-03T16:52:30.848Z"
152
+ }
153
+ ```
154
+
155
+ Run `rails c` and do the following:
156
+
157
+ ```ruby
158
+ Customer.last
159
+ =>
160
+ #<Customer:0x0000000107e20998
161
+ id: 1,
162
+ first_name: "John",
163
+ last_name: "Doe",
164
+ created_at: Sat, 03 Aug 2024 16:52:30.829043000 UTC +00:00,
165
+ updated_at: Sat, 03 Aug 2024 16:52:30.848243000 UTC +00:00>
166
+ Customer.last.events
167
+ [#<Customer::Events::CustomerCreated:0x0000000108dbcac8
168
+ id: 1,
169
+ type: "Customer::Events::CustomerCreated",
170
+ event_type: "Customer::Events::CustomerCreated",
171
+ aggregate_id: "1",
172
+ eventable_type: "Customer",
173
+ eventable_id: 1,
174
+ payload: {"last_name"=>"Doe", "created_at"=>"2024-08-03T16:58:59.952Z", "first_name"=>"John", "updated_at"=>"2024-08-03T16:58:59.952Z"},
175
+ metadata:
176
+ {"request_id"=>"2a40d4f9-509b-4b49-a39f-d978679fa5ef",
177
+ "request_ip"=>"::1",
178
+ "request_params"=>{"action"=>"create", "customer"=>{"last_name"=>"Doe", "first_name"=>"John"}, "last_name"=>"Doe", "controller"=>"customers", "first_name"=>"John"},
179
+ "request_user_agent"=>"curl/8.6.0"},
180
+ created_at: Sat, 03 Aug 2024 16:58:59.973815000 UTC +00:00,
181
+ updated_at: Sat, 03 Aug 2024 16:58:59.973815000 UTC +00:00>]
182
+ ```
183
+
184
+ As you can see, customer has been created and if you check its `.events` relationship, you should see an event that created it.
185
+ This event has the same attributes in the payload as you set using the `event_attributes` method of the `Customer::Events::CustomerCreated` class.
186
+ There is also a metadata field, which is also defined as JSON, and you can store additional things in this field (this is just for information).
187
+
188
+ To have these metadata fields populated automatically, you need to include `RailsSimpleEventSourcing::SetCurrentRequestDetails` in your ApplicationController.
189
+
190
+ Example:
191
+
192
+
193
+ ```ruby
194
+ class ApplicationController < ActionController::Base
195
+ include RailsSimpleEventSourcing::SetCurrentRequestDetails
196
+ end
197
+ ```
198
+
199
+ You can override metadata fields by defining the `event_metadata` method in the controller, this method should return a Hash which will be stored in the metadata field of the event.
200
+
201
+ By default, this method looks like this:
202
+
203
+ ```ruby
204
+ def event_metadata
205
+ parameter_filter = ActiveSupport::ParameterFilter.new(Rails.application.config.filter_parameters)
206
+
207
+ {
208
+ request_id: request.uuid,
209
+ request_user_agent: request.user_agent,
210
+ request_referer: request.referer,
211
+ request_ip: request.ip,
212
+ request_params: parameter_filter.filter(request.params)
213
+ }
214
+ end
215
+ ```
216
+
217
+ #### Important notice
218
+
219
+ The data stored in the events should be immutable (i.e., you shouldn't change it after it's created), so they have simple protection against accidental modification, which means that the model is marked as read-only.
220
+
221
+ The same goes for models, any model that should be updated by events should include `include RailsSimpleEventSourcing::Events`, this will give you access to the `.events` relation and you will have read-only protection as well (model should only be updated by creating an event).
222
+
223
+ Example:
224
+
225
+ ```ruby
226
+ class Customer < ApplicationRecord
227
+ include RailsSimpleEventSourcing::Events
228
+ end
229
+ ```
230
+
231
+ One thing to note here is that it would be better to do soft-deletes (mark record as deleted) instead of deleting records from the DB, since every record has relations called `events` when you have all the events that were applied to it.
232
+
233
+ #### More examples
234
+
235
+ There is a sample application in the `test/dummy/app` directory so you can see how updates and deletes are handled.
236
+
237
+ ## Installation
238
+ Add this line to your application's Gemfile:
239
+
240
+ ```ruby
241
+ gem "rails_simple_event_sourcing"
242
+ ```
243
+
244
+ And then execute:
245
+ ```bash
246
+ $ bundle
247
+ ```
248
+
249
+ Or install it yourself as:
250
+ ```bash
251
+ $ gem install rails_simple_event_sourcing
252
+ ```
253
+
254
+ Copy migration to your app:
255
+ ```ruby
256
+ rails rails_simple_event_sourcing:install:migrations
257
+ ```
258
+
259
+ And then run the migration in order to create the rails_simple_event_sourcing_events table (the table that will store the event log):
260
+ ```ruby
261
+ rake db:migrate
262
+ ```
263
+
264
+ ## Contributing
265
+ Contribution directions go here.
266
+
267
+ ## License
268
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleEventSourcing
4
+ module SetCurrentRequestDetails
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ before_action :set_event_metadata
9
+
10
+ private
11
+
12
+ def set_event_metadata
13
+ CurrentRequest.metadata = event_metadata
14
+ end
15
+
16
+ def event_metadata
17
+ parameter_filter = ActiveSupport::ParameterFilter.new(Rails.application.config.filter_parameters)
18
+
19
+ {
20
+ request_id: request.uuid,
21
+ request_user_agent: request.user_agent,
22
+ request_referer: request.referer,
23
+ request_ip: request.ip,
24
+ request_params: parameter_filter.filter(request.params)
25
+ }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleEventSourcing
4
+ module Events
5
+ extend ActiveSupport::Concern
6
+ include ReadOnly
7
+
8
+ included do
9
+ has_many :events, class_name: 'RailsSimpleEventSourcing::Event', as: :eventable, dependent: :nullify
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleEventSourcing
4
+ module ReadOnly
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ def readonly?
9
+ super || !@write_access_enabled
10
+ end
11
+
12
+ def enable_write_access!
13
+ @write_access_enabled = true
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleEventSourcing
4
+ class CurrentRequest < ActiveSupport::CurrentAttributes
5
+ attribute :metadata
6
+ end
7
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleEventSourcing
4
+ class Event < ApplicationRecord
5
+ include ReadOnly
6
+
7
+ belongs_to :eventable, polymorphic: true, optional: true
8
+
9
+ alias aggregate eventable
10
+
11
+ after_initialize :initialize_event
12
+ before_validation :enable_write_access_on_self, if: :new_record?
13
+ before_validation :apply_on_aggregate, if: :aggregate_defined?
14
+ before_save :add_metadata
15
+ before_save :assing_aggregate_id_and_persist_aggregate, if: :aggregate_defined?
16
+
17
+ def self.aggregate_model_name(name)
18
+ singleton_class.instance_variable_set(:@aggregate_model_name, name)
19
+ end
20
+
21
+ def aggregate_model_name
22
+ self.class.singleton_class.instance_variable_get(:@aggregate_model_name)
23
+ end
24
+
25
+ def self.event_attributes(*attributes)
26
+ @event_attributes ||= []
27
+
28
+ attributes.map(&:to_s).each do |attribute|
29
+ define_method attribute do
30
+ self.payload ||= {}
31
+ self.payload[attribute]
32
+ end
33
+
34
+ define_method "#{attribute}=" do |argument|
35
+ self.payload ||= {}
36
+ self.payload[attribute] = argument
37
+ end
38
+ end
39
+
40
+ @event_attributes
41
+ end
42
+
43
+ def apply(_aggregate)
44
+ raise NoMethodError, "You must implement #{self.class}#apply"
45
+ end
46
+
47
+ private
48
+
49
+ def aggregate_defined?
50
+ aggregate_model_name.present?
51
+ end
52
+
53
+ def initialize_event
54
+ self.class.prepend RailsSimpleEventSourcing::ApplyWithReturningAggregate
55
+ @aggregate = find_or_build_aggregate if aggregate_defined?
56
+ self.event_type = self.class
57
+ self.eventable = @aggregate
58
+ end
59
+
60
+ def enable_write_access_on_self
61
+ enable_write_access!
62
+ end
63
+
64
+ def apply_on_aggregate
65
+ @aggregate.enable_write_access!
66
+ apply(@aggregate)
67
+ end
68
+
69
+ def assing_aggregate_id_and_persist_aggregate
70
+ @aggregate.save! if aggregate_id.present?
71
+ self.aggregate_id = @aggregate.id
72
+ end
73
+
74
+ def add_metadata
75
+ return if CurrentRequest.metadata.blank?
76
+
77
+ self.metadata = CurrentRequest.metadata.compact.presence
78
+ end
79
+
80
+ def find_or_build_aggregate
81
+ return aggregate_model_name.find(aggregate_id).lock! if aggregate_id.present?
82
+
83
+ aggregate_model_name.new
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleEventSourcing
4
+ def self.table_name_prefix
5
+ 'rails_simple_event_sourcing_'
6
+ end
7
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ Rails.application.routes.draw do
2
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateRailsSimpleEventSourcingEvents < ActiveRecord::Migration[7.1]
4
+ def change
5
+ create_table :rails_simple_event_sourcing_events do |t|
6
+ t.references :eventable, polymorphic: true
7
+ t.string :type, null: false
8
+ t.string :event_type, null: false
9
+ t.string :aggregate_id
10
+ t.jsonb :payload
11
+ t.jsonb :metadata
12
+
13
+ t.timestamps
14
+
15
+ t.index :type
16
+ t.index :event_type
17
+ t.index :aggregate_id
18
+ t.index :payload, using: :gin
19
+ t.index :metadata, using: :gin
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleEventSourcing
4
+ module ApplyWithReturningAggregate
5
+ def apply(aggregate)
6
+ aggregate.tap { super }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleEventSourcing
4
+ class CommandHandler
5
+ def initialize(command)
6
+ @command = command
7
+ end
8
+
9
+ def call
10
+ return Result.new(success?: false, errors: @command.errors) unless @command.valid?
11
+
12
+ initialize_command_handler.call
13
+ end
14
+
15
+ private
16
+
17
+ def initialize_command_handler
18
+ @command.class.to_s.gsub('::Commands::', '::CommandHandlers::').constantize.new(command: @command)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleEventSourcing
4
+ module CommandHandlers
5
+ class Base
6
+ def initialize(command:)
7
+ @command = command
8
+ end
9
+
10
+ def call
11
+ raise NoMethodError, "You must implement #{self.class}#call"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleEventSourcing
4
+ module Commands
5
+ class Base
6
+ include ActiveModel::Model
7
+ include ActiveModel::Validations
8
+
9
+ attr_accessor :aggregate_id
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'command_handlers/base'
4
+ require_relative 'commands/base'
5
+ require_relative 'command_handler'
6
+ require_relative 'apply_with_returning_aggregate'
7
+ require_relative 'result'
8
+
9
+ module RailsSimpleEventSourcing
10
+ class Engine < ::Rails::Engine
11
+ isolate_namespace RailsSimpleEventSourcing
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleEventSourcing
4
+ Result = Struct.new(:success?, :data, :errors, keyword_init: true)
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleEventSourcing
4
+ VERSION = '1.0.0'
5
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_simple_event_sourcing/version'
4
+ require 'rails_simple_event_sourcing/engine'
5
+
6
+ module RailsSimpleEventSourcing
7
+ # Your code goes here...
8
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # desc "Explaining what the task does"
4
+ # task :rails_simple_event_sourcing do
5
+ # # Task goes here
6
+ # end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_simple_event_sourcing
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Damian Baćkowski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-10-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pg
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 7.1.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 7.1.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Rails simple event sourcing engine.
70
+ email:
71
+ - damianbackowski@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - MIT-LICENSE
77
+ - README.md
78
+ - Rakefile
79
+ - app/assets/config/rails_simple_event_sourcing_manifest.js
80
+ - app/controllers/concerns/rails_simple_event_sourcing/set_current_request_details.rb
81
+ - app/models/concerns/rails_simple_event_sourcing/events.rb
82
+ - app/models/concerns/rails_simple_event_sourcing/read_only.rb
83
+ - app/models/rails_simple_event_sourcing.rb
84
+ - app/models/rails_simple_event_sourcing/current_request.rb
85
+ - app/models/rails_simple_event_sourcing/event.rb
86
+ - config/routes.rb
87
+ - db/migrate/20231231133250_create_rails_simple_event_sourcing_events.rb
88
+ - lib/rails_simple_event_sourcing.rb
89
+ - lib/rails_simple_event_sourcing/apply_with_returning_aggregate.rb
90
+ - lib/rails_simple_event_sourcing/command_handler.rb
91
+ - lib/rails_simple_event_sourcing/command_handlers/base.rb
92
+ - lib/rails_simple_event_sourcing/commands/base.rb
93
+ - lib/rails_simple_event_sourcing/engine.rb
94
+ - lib/rails_simple_event_sourcing/result.rb
95
+ - lib/rails_simple_event_sourcing/version.rb
96
+ - lib/tasks/rails_simple_event_sourcing_tasks.rake
97
+ homepage: https://github.com/dbackowski/rails_simple_event_sourcing
98
+ licenses:
99
+ - MIT
100
+ metadata:
101
+ homepage_uri: https://github.com/dbackowski/rails_simple_event_sourcing
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubygems_version: 3.1.6
118
+ signing_key:
119
+ specification_version: 4
120
+ summary: Rails engine for simple event sourcing.
121
+ test_files: []