active_outbox 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2cd8cbc9b8aae5d2d62bdb50a0a9e8680f5eecee05009b26c9690853a71680a6
4
+ data.tar.gz: a12cf4a81586d3a1cae47f38d1685d47cf7f96b655bd39b3abbdc9411448981f
5
+ SHA512:
6
+ metadata.gz: cbcbd18628600a8d67e7fae2fa4d6ecdff185d0c038bca08c21d84bc2532a82afefe512a18a61dd7bba868d02c2032b6ed65e045b069dbf5169764018a462ac7
7
+ data.tar.gz: 4990897cf50199b4267ba8df57541570d3e644ea8c3c71f51898c4b19be16dacffff9c03596c0c4c12c706dc9ffeb15332934f8c273b6cf8efa60339e3a54f95
data/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # active-outbox
2
+ A Transactional Outbox implementation for ActiveRecord
@@ -0,0 +1,5 @@
1
+ module ActiveOutbox
2
+ class Base < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,25 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ class OutboxGenerator < ActiveRecord::Generators::Base
4
+ source_root File.expand_path("../templates", __FILE__)
5
+
6
+ argument :domains, type: :array, default: [], banner: "domain domain"
7
+ class_option :root_components_path, type: :string, default: Rails.root
8
+
9
+ def create_migration_files
10
+ migration_path = "#{options['root_components_path']}/db/migrate"
11
+ migration_template(
12
+ "migration.rb",
13
+ "#{migration_path}/outbox_create_#{table_name}.rb",
14
+ migration_version: migration_version
15
+ )
16
+ end
17
+
18
+ def migration_version
19
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
20
+ end
21
+
22
+ def table_name
23
+ "#{name}_outboxes"
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ class OutboxCreate<%= table_name.camelize.singularize %> < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :<%= table_name %> do |t|
4
+ t.uuid :identifier, null: false, index: { unique: true }
5
+ t.string :event, null: false
6
+ t.jsonb :payload
7
+ t.string :aggregate, null: false
8
+ t.uuid :aggregate_identifier, null: false, index: true
9
+
10
+ t.timestamps
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveOutbox
4
+ module Outboxable
5
+ extend ActiveSupport::Concern
6
+
7
+ included do |base|
8
+ base.extend(ClassMethods)
9
+ *namespace, klass = name.underscore.upcase.split('/')
10
+ namespace = namespace.reverse.join('.')
11
+
12
+ module_parent.const_set('Events', Module.new) unless module_parent.const_defined?('Events')
13
+
14
+ { create: 'CREATED', update: 'UPDATED', destroy: 'DESTROYED' }.each do |key, value|
15
+ const_name = "#{klass}_#{value}"
16
+
17
+ unless module_parent::Events.const_defined?(const_name)
18
+ module_parent::Events.const_set(const_name, "#{const_name}#{namespace.blank? ? '' : '.'}#{namespace}")
19
+ end
20
+
21
+ event_name = module_parent::Events.const_get(const_name)
22
+
23
+ send("after_#{key}") { create_outbox!(key, event_name) }
24
+ end
25
+ end
26
+
27
+ def save(**options, &block)
28
+ if options[:outbox_event].present?
29
+ @outbox_event = options[:outbox_event].underscore.upcase
30
+ end
31
+
32
+ super(**options, &block)
33
+ end
34
+
35
+ def save!(**options, &block)
36
+ if options[:outbox_event].present?
37
+ @outbox_event = options[:outbox_event].underscore.upcase
38
+ end
39
+
40
+ super(**options, &block)
41
+ end
42
+
43
+ module ClassMethods
44
+ def parent_module_name
45
+ module_parent == Object ? '' : module_parent.name
46
+ end
47
+
48
+ def outbox_model
49
+ @outbox_model ||= ActiveOutbox::Base.subclasses.find do |klass|
50
+ klass.name.include?(parent_module_name)
51
+ end
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def create_outbox!(action, event_name)
58
+ outbox = self.class.outbox_model.new(
59
+ aggregate: self.class.name,
60
+ aggregate_identifier: try(:identifier) || id,
61
+ event: @outbox_event || event_name,
62
+ identifier: SecureRandom.uuid,
63
+ payload: payload(action)
64
+ )
65
+ @outbox_event = nil
66
+
67
+ if outbox.invalid?
68
+ outbox.errors.each do |error|
69
+ self.errors.import(error, attribute: "outbox.#{error.attribute}")
70
+ end
71
+ end
72
+
73
+ outbox.save!
74
+ end
75
+
76
+ def payload(action)
77
+ payload = { before: nil, after: nil }
78
+ case action
79
+ when :create
80
+ payload[:after] = as_json
81
+ when :update
82
+ # previous_changes => { 'name' => ['bob', 'robert'] }
83
+ changes = previous_changes.transform_values(&:first)
84
+ payload[:before] = as_json.merge(changes)
85
+ payload[:after] = as_json
86
+ when :destroy
87
+ payload[:before] = as_json
88
+ else
89
+ raise ActiveRecord::RecordNotSaved.new("Failed to create Outbox payload for #{self.class.name}: #{identifier}", self)
90
+ end
91
+ payload
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,2 @@
1
+ require 'active_outbox/base'
2
+ require 'active_outbox/outboxable'
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_outbox
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Guillermo Aguirre
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-03-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '7.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '7.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry-rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.3.6
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.3.6
41
+ - !ruby/object:Gem::Dependency
42
+ name: reek
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 6.0.6
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 6.0.6
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 3.8.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.8.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.22.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.22.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.17.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.17.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: sqlite3
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '='
102
+ - !ruby/object:Gem::Version
103
+ version: 1.4.2
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '='
109
+ - !ruby/object:Gem::Version
110
+ version: 1.4.2
111
+ - !ruby/object:Gem::Dependency
112
+ name: byebug
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 11.1.3
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 11.1.3
125
+ description: A Transactional Outbox implementation for ActiveRecord
126
+ email: guillermoaguirre1@gmail.com
127
+ executables: []
128
+ extensions: []
129
+ extra_rdoc_files: []
130
+ files:
131
+ - README.md
132
+ - lib/active_outbox.rb
133
+ - lib/active_outbox/base.rb
134
+ - lib/active_outbox/generators/outbox_generator.rb
135
+ - lib/active_outbox/generators/templates/migration.rb
136
+ - lib/active_outbox/outboxable.rb
137
+ homepage: https://rubygems.org/gems/active_outbox
138
+ licenses:
139
+ - MIT
140
+ metadata: {}
141
+ post_install_message:
142
+ rdoc_options: []
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ requirements: []
156
+ rubygems_version: 3.4.1
157
+ signing_key:
158
+ specification_version: 4
159
+ summary: ActiveOutbox
160
+ test_files: []