Skip to content

Commit

Permalink
Fix migration constraints (#18)
Browse files Browse the repository at this point in the history
* Fix migration name in generator template

* Add super_diff gem

* Update identation rubocop rules

* Support UUID and ID for Outbox aggregate_identifier

Generator has an option to set which type of identifier we want for our Outbox records.

* Refactor generators specs to use shared_examples

* Refactor specs to reflect new outboxable changes for dynamic primary key on the aggregate_identifier

- Created new testing models, one pair with IDs and another pair with UUIDs.
- Updated spec to use shared_examples so it a little bit more DRY
- Used modular testing config to be able to specify different classes and test that funcionality

* Update rubocop rules

* Refactor spec_helper setup

* Linting fixes

* Update README with --uuid instructions

* Fix rubocop linting

* Use dynamic uuid type in migrations for specs

* Update README copy

* Fix default inheritance lookup bug

* Increase method lines

* Test default outbox class with ID config instead of reduntant specific ID one

* Bump to 0.1.2
  • Loading branch information
guillermoap committed Nov 10, 2023
1 parent ca89cd4 commit 92db1ce
Show file tree
Hide file tree
Showing 13 changed files with 483 additions and 402 deletions.
15 changes: 12 additions & 3 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,22 @@ Gemspec/RequireMFA:
Enabled: false

Metrics/MethodLength:
Max: 12
Max: 50

Metrics/AbcSize:
Enabled: false

Layout/EndAlignment:
EnforcedStyleAlignWith: start_of_line

Layout/ArgumentAlignment:
EnforcedStyle: with_fixed_indentation

RSpec/MultipleMemoizedHelpers:
Max: 7
Max: 10

RSpec/NestedGroups:
Max: 4
Max: 7

Style/Documentation:
Enabled: false
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ gem 'rubocop', '~> 1.56.3', require: false
gem 'rubocop-rspec', '~> 2.24.1', require: false
gem 'simplecov', '~> 0.22.0'
gem 'sqlite3', '1.4.2'
gem 'super_diff', '~> 0.10.0'
11 changes: 10 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
active_outbox (0.1.1)
active_outbox (0.1.2)
dry-configurable (~> 1.0)
rails (>= 6.1)

Expand Down Expand Up @@ -68,6 +68,7 @@ GEM
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
ast (2.4.2)
attr_extras (7.1.0)
base64 (0.1.1)
builder (3.2.4)
byebug (11.1.3)
Expand Down Expand Up @@ -125,10 +126,13 @@ GEM
nokogiri (1.15.4)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
optimist (3.1.0)
parallel (1.23.0)
parser (3.2.2.4)
ast (~> 2.4.1)
racc
patience_diff (1.2.0)
optimist (~> 3.0)
pg (1.5.4)
pry (0.14.2)
coderay (~> 1.1)
Expand Down Expand Up @@ -225,6 +229,10 @@ GEM
activesupport (>= 5.2)
sprockets (>= 3.0.0)
sqlite3 (1.4.2)
super_diff (0.10.0)
attr_extras (>= 6.2.4)
diff-lcs
patience_diff
thor (1.3.0)
timeout (0.4.0)
tzinfo (2.0.6)
Expand Down Expand Up @@ -252,6 +260,7 @@ DEPENDENCIES
rubocop-rspec (~> 2.24.1)
simplecov (~> 0.22.0)
sqlite3 (= 1.4.2)
super_diff (~> 0.10.0)

BUNDLED WITH
2.4.6
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ gem install active_outbox

## Usage
### Setup
Create an initializer under `config/initializers/active_outbox.rb` and setup the default outbox class to the new `Outbox` model you just created.
Create an initializer under `config/initializers/active_outbox.rb` and setup the default outbox class to the `Outbox` model you will create in the next step.
```bash
rails g active_outbox:install
```
After creating the initializer, create an `Outbox` table using the provided generator and corresponding model. Any model name can be passed as an argument but if empty it will default to just `Outobx`. The generated table name will be `model_name_outboxes`.
After creating the initializer, create an `Outbox` table using the provided generator and corresponding model. Any model name can be passed as an argument but if empty it will default to just `outboxes`. The generated table name will be `model_name_outboxes`.
```bash
rails g active_outbox:model <optional model_name>
```
Expand All @@ -66,15 +66,21 @@ If you want to persist a custom event other than the provided base events, you c
user.save(outbox_event: 'YOUR_CUSTOM_EVENT')
```
## Advanced Usage
### Supporting UUIDs
By default our Outbox migration has an `aggregate_identifier` field which serves the purpose of identifying which record was involved in the event emission. We default to integer IDs, but if you're using UUIDs as a primary key for your records you have to adjust the migrations accordingly. To do so just run the model generator with the `--uuid` flag.
```bash
rails g active_outbox:model <optional model_name> --uuid
```
### Multiple Outbox mappings
If more granularity is desired multiple `Outbox` classes can be configured. After creating the needed `Outbox` classes for each module you can specify multiple mappings in the initializer.
```ruby
# frozen_string_literal: true

Rails.application.reloader.to_prepare do
ActiveOutbox.configure do |config|
config.outbox_mapping = {
'Member' => 'Member::Outbox',
'UserAccess' => 'UserAccess::Outbox'
'member' => 'Member::Outbox',
'user_access' => 'UserAccess::Outbox'
}
end
end
Expand Down
2 changes: 1 addition & 1 deletion active_outbox.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Gem::Specification.new do |spec|
spec.files = Dir['LICENSE.txt', 'README.md', 'lib/**/*', 'lib/active_outbox.rb']
spec.name = 'active_outbox'
spec.summary = 'A Transactional Outbox implementation for ActiveRecord'
spec.version = '0.1.1'
spec.version = '0.1.2'

spec.email = 'guillermoaguirre1@gmail.com'
spec.executables = ['outbox']
Expand Down
17 changes: 9 additions & 8 deletions lib/active_outbox/outboxable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,20 @@ def assign_outbox_event(options)
def create_outbox!(action, event_name)
outbox = outbox_model.new(
aggregate: self.class.name,
aggregate_identifier: try(:identifier) || id,
aggregate_identifier: send(self.class.primary_key),
event: @outbox_event || event_name,
identifier: SecureRandom.uuid,
payload: formatted_payload(action)
)
@outbox_event = nil

handle_outbox_errors(outbox) if outbox.invalid?
outbox.save!
end

def outbox_model
module_parent = self.class.module_parent

unless module_parent.const_defined?('OUTBOX_MODEL')
# sets _inherit_ option to false so it doesn't lookup in ancestors for the constant
unless module_parent.const_defined?('OUTBOX_MODEL', false)
outbox_model = outbox_model_name!.safe_constantize
module_parent.const_set('OUTBOX_MODEL', outbox_model)
end
Expand All @@ -71,9 +70,9 @@ def outbox_model_name!
end

def namespace_outbox_mapping
namespace = self.class.name.split('/').first
namespace = self.class.module_parent.name.underscore

ActiveOutbox.config.outbox_mapping[namespace&.underscore]
ActiveOutbox.config.outbox_mapping[namespace]
end

def default_outbox_mapping
Expand Down Expand Up @@ -101,8 +100,10 @@ def construct_payload(action)
when :destroy
{ before: as_json, after: nil }
else
raise ActiveRecord::RecordNotSaved.new("Failed to create Outbox payload for #{self.class.name}: #{identifier}",
self)
raise ActiveRecord::RecordNotSaved.new(
"Failed to create Outbox payload for #{self.class.name}: #{send(self.class.primary_key)}",
self
)
end
end
end
Expand Down
12 changes: 11 additions & 1 deletion lib/generators/active_outbox/model/model_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ class << self
desc 'Creates the Outbox model migration'

argument :model_name, type: :string, default: ''
class_option :component_path, type: :string, desc: 'Indicates where to create the outbox migration'
class_option :component_path,
type: :string,
desc: 'Indicates where to create the outbox migration'
class_option :uuid,
type: :boolean,
default: false,
desc: 'Use UUID to identify aggregate records in events. Defaults to ID'

def create_migration_file
migration_path = "#{root_path}/db/migrate"
Expand All @@ -42,6 +48,10 @@ def migration_version
def table_name
model_name.blank? ? 'outboxes' : "#{model_name}_outboxes"
end

def aggregate_identifier_type
options['uuid'].present? ? ActiveOutbox::AdapterHelper.uuid_type : 'integer'
end
end
end
end
2 changes: 1 addition & 1 deletion lib/generators/active_outbox/templates/migration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def change
t.string :event, null: false
t.<%= ActiveOutbox::AdapterHelper.json_type %> :payload
t.string :aggregate, null: false
t.<%= ActiveOutbox::AdapterHelper.uuid_type %> :aggregate_identifier, null: false, index: true
t.<%= aggregate_identifier_type %> :aggregate_identifier, null: false, index: true

t.timestamps
end
Expand Down

0 comments on commit 92db1ce

Please sign in to comment.