Compare commits

...

21 Commits
v1.0.3 ... main

Author SHA1 Message Date
Brusk Awat
8a64179e38
Merge pull request #8 from muhammadnawzad/main
Updates Dependancies
2026-01-18 10:39:04 +03:00
Muhammad Nawzad
f9bfeac427
refactor: remove metadata field from outboxable templates. 2026-01-18 10:06:03 +03:00
Muhammad Nawzad
46f56a4a08
refactor: remove metadata argument from outbox creation. 2026-01-18 10:04:30 +03:00
Muhammad Nawzad
4a6952f767
Upgrade Ruby to 4.0.1 in CI, update gem dependencies, and apply RuboCop configuration and style fixes. 2026-01-18 10:02:40 +03:00
Muhammad Nawzad
61767b40c2
Adds metadata to outboxes 2024-08-19 11:06:17 +03:00
Brusk Awat
08ef9242e5
Update README.md 2024-08-04 14:01:35 +03:00
40cbf3342a
Merge pull request #4 from muhammadnawzad/main
Indexes columns & adds `content_type` field
2024-05-28 14:22:41 +03:00
Muhammad Nawzad
816465eced
Refactors code for better readablity 2024-05-28 13:51:17 +03:00
Muhammad Nawzad
035a9822ee
Adds content_type configuration 2024-05-28 13:44:54 +03:00
Muhammad Nawzad
e5ebfb84d0
Add index on outboxes table for status and last_attempted_at 2024-04-02 14:50:42 +03:00
Muhammad Nawzad
de7c304524
Fixes initialize 2024-04-02 14:50:27 +03:00
faa35a5791
Merge pull request #3 from muhammadnawzad/main
Updates the variable rabbitmq_event_bus_exchange's name to rabbitmq_e…
2023-12-14 10:59:53 +03:00
Muhammad Nawzad
a8765e15f6
Updates .gitignore 2023-12-06 13:45:57 +03:00
Muhammad Nawzad
664adbb401
Bumps gem version to 1.0.6 2023-12-06 13:45:25 +03:00
Muhammad Nawzad
13b2013f3f
Removes extra argument 2023-12-06 13:45:08 +03:00
Muhammad Nawzad
4de6891945
Updates required gems' versions 2023-12-06 13:44:50 +03:00
Muhammad Nawzad
eb6de394bc
Merge branch 'ditkrg:main' into main 2023-12-06 13:34:18 +03:00
e486de9bb1
Bumps version 2023-04-25 12:39:34 +03:00
873f23ba59
Runs publish inline after commit 2023-04-25 12:39:20 +03:00
Muhammad Nawzad
5ef8d6a51d
Updates the variable rabbitmq_event_bus_exchange's name to rabbitmq_exchange_name 2023-04-18 10:42:55 +03:00
6f598f40d4
Fixes typo 2023-04-16 16:55:39 +03:00
18 changed files with 157 additions and 96 deletions

View File

@ -14,14 +14,14 @@ jobs:
strategy: strategy:
matrix: matrix:
ruby: ruby:
- '3.1.2' - "4.0.1"
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Set up Ruby - name: Set up Ruby
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
with: with:
ruby-version: ${{ matrix.ruby }} ruby-version: ${{ matrix.ruby }}
bundler-cache: true bundler-cache: true
- name: Run the default task - name: Run the default task
run: bundle exec rake run: bundle exec rake

1
.gitignore vendored
View File

@ -11,3 +11,4 @@
.rspec_status .rspec_status
*.gem *.gem
.idea

View File

@ -1,4 +1,5 @@
require: rubocop-rails plugins:
- rubocop-rails
AllCops: AllCops:
NewCops: enable NewCops: enable

12
Gemfile
View File

@ -5,12 +5,12 @@ source 'https://rubygems.org'
# Specify your gem's dependencies in outboxable.gemspec # Specify your gem's dependencies in outboxable.gemspec
gemspec gemspec
gem 'rake', '~> 13.0' gem 'rake', '~> 13.3.1'
gem 'rspec', '~> 3.0' gem 'rspec', '~> 3.13.2'
gem 'rubocop-rails', '~> 2.18' gem 'rubocop-rails', '~> 2.34.3'
group :development, :test do group :development, :test do
gem 'activesupport', '~> 7.0' gem 'activesupport', '~> 8.1.2'
gem 'sidekiq', '~> 7.0' gem 'sidekiq', '~> 8.1.0'
gem 'sidekiq-cron', '~> 1.10' gem 'sidekiq-cron', '~> 2.3.1'
end end

View File

@ -1,107 +1,139 @@
PATH PATH
remote: . remote: .
specs: specs:
outboxable (1.0.3) outboxable (1.0.6)
bunny (>= 2.19.0) bunny (>= 2.22)
connection_pool (~> 2.3.0) connection_pool (>= 2.4)
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
activesupport (7.0.4.3) activesupport (8.1.2)
concurrent-ruby (~> 1.0, >= 1.0.2) base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
json
logger (>= 1.4.2)
minitest (>= 5.1) minitest (>= 5.1)
tzinfo (~> 2.0) securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
uri (>= 0.13.1)
amq-protocol (2.3.2) amq-protocol (2.3.2)
ast (2.4.2) ast (2.4.3)
bunny (2.20.3) base64 (0.3.0)
bigdecimal (4.0.1)
bunny (2.22.0)
amq-protocol (~> 2.3, >= 2.3.1) amq-protocol (~> 2.3, >= 2.3.1)
sorted_set (~> 1, >= 1.0.2) sorted_set (~> 1, >= 1.0.2)
concurrent-ruby (1.2.2) concurrent-ruby (1.3.6)
connection_pool (2.3.0) connection_pool (3.0.2)
diff-lcs (1.5.0) cronex (0.15.0)
et-orbi (1.2.7)
tzinfo tzinfo
fugit (1.8.1) unicode (>= 0.4.4.5)
et-orbi (~> 1, >= 1.2.7) diff-lcs (1.6.2)
drb (2.2.3)
et-orbi (1.4.0)
tzinfo
fugit (1.12.1)
et-orbi (~> 1.4)
raabro (~> 1.4) raabro (~> 1.4)
globalid (1.1.0) globalid (1.3.0)
activesupport (>= 5.0) activesupport (>= 6.1)
i18n (1.12.0) i18n (1.14.8)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
json (2.6.3) json (2.18.0)
minitest (5.18.0) language_server-protocol (3.17.0.5)
parallel (1.22.1) lint_roller (1.1.0)
parser (3.2.2.0) logger (1.7.0)
minitest (6.0.1)
prism (~> 1.5)
parallel (1.27.0)
parser (3.3.10.1)
ast (~> 2.4.1) ast (~> 2.4.1)
racc
prism (1.8.0)
raabro (1.4.0) raabro (1.4.0)
rack (2.2.6.4) racc (1.8.1)
rack (3.2.4)
rainbow (3.1.1) rainbow (3.1.1)
rake (13.0.6) rake (13.3.1)
rbtree (0.4.6) rbtree (0.4.6)
redis-client (0.14.1) redis-client (0.26.3)
connection_pool connection_pool
regexp_parser (2.7.0) regexp_parser (2.11.3)
rexml (3.2.5) rspec (3.13.2)
rspec (3.12.0) rspec-core (~> 3.13.0)
rspec-core (~> 3.12.0) rspec-expectations (~> 3.13.0)
rspec-expectations (~> 3.12.0) rspec-mocks (~> 3.13.0)
rspec-mocks (~> 3.12.0) rspec-core (3.13.6)
rspec-core (3.12.1) rspec-support (~> 3.13.0)
rspec-support (~> 3.12.0) rspec-expectations (3.13.5)
rspec-expectations (3.12.2)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0) rspec-support (~> 3.13.0)
rspec-mocks (3.12.5) rspec-mocks (3.13.7)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0) rspec-support (~> 3.13.0)
rspec-support (3.12.0) rspec-support (3.13.6)
rubocop (1.49.0) rubocop (1.82.1)
json (~> 2.3) json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (~> 1.10) parallel (~> 1.10)
parser (>= 3.2.0.0) parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0) regexp_parser (>= 2.9.3, < 3.0)
rexml (>= 3.2.5, < 4.0) rubocop-ast (>= 1.48.0, < 2.0)
rubocop-ast (>= 1.28.0, < 2.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0) unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.28.0) rubocop-ast (1.49.0)
parser (>= 3.2.1.0) parser (>= 3.3.7.2)
rubocop-rails (2.18.0) prism (~> 1.7)
rubocop-rails (2.34.3)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
lint_roller (~> 1.1)
rack (>= 1.1) rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0) rubocop (>= 1.75.0, < 2.0)
rubocop-ast (>= 1.44.0, < 2.0)
ruby-progressbar (1.13.0) ruby-progressbar (1.13.0)
securerandom (0.4.1)
set (1.0.3) set (1.0.3)
sidekiq (7.0.7) sidekiq (8.1.0)
concurrent-ruby (< 2) connection_pool (>= 3.0.0)
connection_pool (>= 2.3.0) json (>= 2.16.0)
rack (>= 2.2.4) logger (>= 1.7.0)
redis-client (>= 0.11.0) rack (>= 3.2.0)
sidekiq-cron (1.10.0) redis-client (>= 0.26.0)
fugit (~> 1.8) sidekiq-cron (2.3.1)
cronex (>= 0.13.0)
fugit (~> 1.8, >= 1.11.1)
globalid (>= 1.0.1) globalid (>= 1.0.1)
sidekiq (>= 6) sidekiq (>= 6.5.0)
sorted_set (1.0.3) sorted_set (1.0.3)
rbtree rbtree
set (~> 1.0) set (~> 1.0)
tzinfo (2.0.6) tzinfo (2.0.6)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
unicode-display_width (2.4.2) unicode (0.4.4.5)
unicode-display_width (3.2.0)
unicode-emoji (~> 4.1)
unicode-emoji (4.2.0)
uri (1.1.1)
PLATFORMS PLATFORMS
arm64-darwin-25
x86_64-linux x86_64-linux
DEPENDENCIES DEPENDENCIES
activesupport (~> 7.0) activesupport (~> 8.1.2)
outboxable! outboxable!
rake (~> 13.0) rake (~> 13.3.1)
rspec (~> 3.0) rspec (~> 3.13.2)
rubocop-rails (~> 2.18) rubocop-rails (~> 2.34.3)
sidekiq (~> 7.0) sidekiq (~> 8.1.0)
sidekiq-cron (~> 1.10) sidekiq-cron (~> 2.3.1)
BUNDLED WITH BUNDLED WITH
2.4.7 2.4.17

View File

@ -1,3 +1,16 @@
# 🚨 Discontinuation Notice for ActiveRecord 🚨
**Effective Date: August 4, 2024**
Please be aware that we are no longer maintaing the part related to **ActiveRecord** in this gem. We are dropping support for ActiveRecord in favor of [Solid Queue](https://github.com/rails/solid_queue).
In the meantime, we commit to continously support the Mongoid part of the gem.
### New Recommended Gem: `Solid Queue`
For ActiveRecord users, we recommend transitioning to the `Solid Queue` gem, which provides enhanced functionality, improved performance, and better support for modern application requirements. `Solid Queue` is designed to seamlessly integrate with your existing infrastructure while offering robust features to handle your queuing needs efficiently.
# Outboxable # Outboxable
The Outboxable Gem is tailored for Rails applications to implement the transactional outbox pattern. It supports both ActiveRecord and Mongoid. The Outboxable Gem is tailored for Rails applications to implement the transactional outbox pattern. It supports both ActiveRecord and Mongoid.
@ -82,7 +95,7 @@ Outboxable.configure do |config|
config.rabbitmq_user = ENV.fetch('RABBITMQ__USERNAME') config.rabbitmq_user = ENV.fetch('RABBITMQ__USERNAME')
config.rabbitmq_password = ENV.fetch('RABBITMQ__PASSWORD') config.rabbitmq_password = ENV.fetch('RABBITMQ__PASSWORD')
config.rabbitmq_vhost = ENV.fetch('RABBITMQ__VHOST') config.rabbitmq_vhost = ENV.fetch('RABBITMQ__VHOST')
config.rabbitmq_event_bus_exchange = ENV.fetch('EVENTBUS__EXCHANGE_NAME') config.rabbitmq_exchange_name = ENV.fetch('RABBITMQ__EXCHANGE_NAME')
end end
``` ```

View File

@ -5,8 +5,8 @@ module Outboxable
source_root File.expand_path('../../templates', __dir__) source_root File.expand_path('../../templates', __dir__)
class_option :orm, type: :string, default: 'activerecord' class_option :orm, type: :string, default: 'activerecord'
def initialize(*) def initialize(*args)
super(*) super(*args) # rubocop:disable Style/SuperArguments
@orm = options[:orm] || 'activerecord' @orm = options[:orm] || 'activerecord'
%w[activerecord mongoid].include?(@orm) || raise(ArgumentError, 'Invalid ORM. Only ActiveRecord and Mongoid are supported.') %w[activerecord mongoid].include?(@orm) || raise(ArgumentError, 'Invalid ORM. Only ActiveRecord and Mongoid are supported.')
@ -14,7 +14,7 @@ module Outboxable
# Copy initializer into user app # Copy initializer into user app
def copy_initializer def copy_initializer
copy_file('activerecod_initializer.rb', 'config/initializers/z_outboxable.rb') if @orm == 'activerecord' copy_file('activerecord_initializer.rb', 'config/initializers/z_outboxable.rb') if @orm == 'activerecord'
copy_file('mongoid_initializer.rb', 'config/initializers/z_outboxable.rb') if @orm == 'mongoid' copy_file('mongoid_initializer.rb', 'config/initializers/z_outboxable.rb') if @orm == 'mongoid'
end end

View File

@ -26,7 +26,7 @@ module Outboxable
def instantiate_outbox(routing_key:) def instantiate_outbox(routing_key:)
outboxes.new( outboxes.new(
routing_key:, routing_key:,
exchange: Outboxable.configuration.rabbitmq_event_bus_exchange, exchange: Outboxable.configuration.rabbitmq_exchange_name,
payload: as_json payload: as_json
) )
end end

View File

@ -17,7 +17,7 @@ module Outboxable
:rabbitmq_user, :rabbitmq_user,
:rabbitmq_password, :rabbitmq_password,
:rabbitmq_vhost, :rabbitmq_vhost,
:rabbitmq_event_bus_exchange, :rabbitmq_exchange_name,
:message_broker, :message_broker,
:orm :orm

View File

@ -3,6 +3,7 @@ require 'singleton'
module Outboxable module Outboxable
class Connection class Connection
include ::Singleton include ::Singleton
attr_reader :connection attr_reader :connection
def initialize def initialize

View File

@ -20,7 +20,12 @@ module Outboxable
exchange = channel.topic(@resource.exchange, durable: true) exchange = channel.topic(@resource.exchange, durable: true)
# Publish the CloudEvent resource to the exchange # Publish the CloudEvent resource to the exchange
exchange.publish(to_envelope(resource: @resource), routing_key: @resource.routing_key, headers: @resource.try(:headers) || {}) exchange.publish(
to_envelope(resource: @resource),
routing_key: @resource.routing_key,
headers: @resource.try(:headers) || {},
content_type: @resource.try(:content_type) || 'application/json'
)
# Wait for confirmation # Wait for confirmation
confirmed = channel.wait_for_confirms confirmed = channel.wait_for_confirms

View File

@ -1,5 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
module Outboxable module Outboxable
VERSION = '1.0.3' VERSION = '1.0.6'
end end

View File

@ -32,5 +32,5 @@ Outboxable.configure do |config|
config.rabbitmq_user = ENV.fetch('RABBITMQ__USERNAME') config.rabbitmq_user = ENV.fetch('RABBITMQ__USERNAME')
config.rabbitmq_password = ENV.fetch('RABBITMQ__PASSWORD') config.rabbitmq_password = ENV.fetch('RABBITMQ__PASSWORD')
config.rabbitmq_vhost = ENV.fetch('RABBITMQ__VHOST') config.rabbitmq_vhost = ENV.fetch('RABBITMQ__VHOST')
config.rabbitmq_event_bus_exchange = ENV.fetch('EVENTBUS__EXCHANGE_NAME') config.rabbitmq_exchange_name = ENV.fetch('RABBITMQ__EXCHANGE_NAME')
end end

View File

@ -4,7 +4,7 @@ class Outbox < ApplicationRecord
before_save :check_publishing before_save :check_publishing
# Callbacks # Callbacks
before_create :set_last_attempted_at before_create :set_last_attempted_at
after_save :publish, if: :allow_publish after_commit :publish, if: :allow_publish?
# Enums # Enums
enum status: { pending: 0, processing: 1, published: 2, failed: 3 } enum status: { pending: 0, processing: 1, published: 2, failed: 3 }
enum size: { single: 0, batch: 1 } enum size: { single: 0, batch: 1 }
@ -20,8 +20,12 @@ class Outbox < ApplicationRecord
end end
def publish def publish
Outboxable::Worker.perform_async(id) # Run this in own thread
update(status: :processing, last_attempted_at: 1.minute.from_now, allow_publish: false) threaded = Thread.new do
Outboxable::Worker.perform_inline(id)
update(status: :processing, last_attempted_at: 1.minute.from_now, allow_publish: false)
end
threaded.join
end end
def check_publishing def check_publishing

View File

@ -7,6 +7,7 @@ class CreateOutboxableOutboxes < ActiveRecord::Migration[7.0]
t.string :exchange, null: false, default: '' t.string :exchange, null: false, default: ''
t.string :routing_key, null: false, default: '' t.string :routing_key, null: false, default: ''
t.string :content_type, null: false, default: 'application/json'
t.integer :attempts, null: false, default: 0 t.integer :attempts, null: false, default: 0
t.datetime :last_attempted_at, null: true t.datetime :last_attempted_at, null: true
@ -21,5 +22,7 @@ class CreateOutboxableOutboxes < ActiveRecord::Migration[7.0]
t.timestamps t.timestamps
end end
add_index :outboxes, %i[status last_attempted_at], name: 'index_outboxes_on_outboxable_status_and_last_attempted_at'
end end
end end

View File

@ -32,5 +32,5 @@ Outboxable.configure do |config|
config.rabbitmq_user = ENV.fetch('RABBITMQ__USERNAME') config.rabbitmq_user = ENV.fetch('RABBITMQ__USERNAME')
config.rabbitmq_password = ENV.fetch('RABBITMQ__PASSWORD') config.rabbitmq_password = ENV.fetch('RABBITMQ__PASSWORD')
config.rabbitmq_vhost = ENV.fetch('RABBITMQ__VHOST') config.rabbitmq_vhost = ENV.fetch('RABBITMQ__VHOST')
config.rabbitmq_event_bus_exchange = ENV.fetch('EVENTBUS__EXCHANGE_NAME') config.rabbitmq_exchange_name = ENV.fetch('RABBITMQ__EXCHANGE_NAME')
end end

View File

@ -10,6 +10,7 @@ class Outbox
field :exchange, type: String, default: '' field :exchange, type: String, default: ''
field :routing_key, type: String, default: '' field :routing_key, type: String, default: ''
field :content_type, type: String, default: 'application/json'
field :attempts, type: Integer, default: 0 field :attempts, type: Integer, default: 0

View File

@ -31,8 +31,8 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
spec.require_paths = ['lib'] spec.require_paths = ['lib']
spec.add_dependency 'bunny', '>= 2.19.0' spec.add_dependency 'bunny', '>= 2.22'
spec.add_dependency 'connection_pool', '~> 2.3.0' spec.add_dependency 'connection_pool', '>= 2.4'
spec.metadata['rubygems_mfa_required'] = 'true' spec.metadata['rubygems_mfa_required'] = 'true'
end end