diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 35f619c..461657e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,7 +34,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: ["2.7", "3.0", "3.1", "3.2"] + ruby: ["3.0", "3.1", "3.2", "3.3"] steps: - uses: actions/checkout@v2 diff --git a/.rubocop.yml b/.rubocop.yml index aaf56e0..29b895a 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -3,7 +3,7 @@ inherit_gem: AllCops: DisplayCopNames: true - TargetRubyVersion: 2.7 + TargetRubyVersion: 3.0 SuggestExtensions: false Include: - lib/**/*.rb diff --git a/Gemfile.lock b/Gemfile.lock index 4a15418..3bbc2e0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - rabbit_messaging (0.14.0) + rabbit_messaging (0.15.0) bunny (~> 2.0) lamian rails (>= 5.2) @@ -11,73 +11,84 @@ PATH GEM remote: https://rubygems.org/ specs: - actioncable (7.0.4.2) - actionpack (= 7.0.4.2) - activesupport (= 7.0.4.2) + actioncable (7.1.3.3) + actionpack (= 7.1.3.3) + activesupport (= 7.1.3.3) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.4.2) - actionpack (= 7.0.4.2) - activejob (= 7.0.4.2) - activerecord (= 7.0.4.2) - activestorage (= 7.0.4.2) - activesupport (= 7.0.4.2) + zeitwerk (~> 2.6) + actionmailbox (7.1.3.3) + actionpack (= 7.1.3.3) + activejob (= 7.1.3.3) + activerecord (= 7.1.3.3) + activestorage (= 7.1.3.3) + activesupport (= 7.1.3.3) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.4.2) - actionpack (= 7.0.4.2) - actionview (= 7.0.4.2) - activejob (= 7.0.4.2) - activesupport (= 7.0.4.2) + actionmailer (7.1.3.3) + actionpack (= 7.1.3.3) + actionview (= 7.1.3.3) + activejob (= 7.1.3.3) + activesupport (= 7.1.3.3) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp - rails-dom-testing (~> 2.0) - actionpack (7.0.4.2) - actionview (= 7.0.4.2) - activesupport (= 7.0.4.2) - rack (~> 2.0, >= 2.2.0) + rails-dom-testing (~> 2.2) + actionpack (7.1.3.3) + actionview (= 7.1.3.3) + activesupport (= 7.1.3.3) + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4) + rack-session (>= 1.0.1) rack-test (>= 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.4.2) - actionpack (= 7.0.4.2) - activerecord (= 7.0.4.2) - activestorage (= 7.0.4.2) - activesupport (= 7.0.4.2) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + actiontext (7.1.3.3) + actionpack (= 7.1.3.3) + activerecord (= 7.1.3.3) + activestorage (= 7.1.3.3) + activesupport (= 7.1.3.3) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.4.2) - activesupport (= 7.0.4.2) + actionview (7.1.3.3) + activesupport (= 7.1.3.3) builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (7.0.4.2) - activesupport (= 7.0.4.2) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (7.1.3.3) + activesupport (= 7.1.3.3) globalid (>= 0.3.6) - activemodel (7.0.4.2) - activesupport (= 7.0.4.2) - activerecord (7.0.4.2) - activemodel (= 7.0.4.2) - activesupport (= 7.0.4.2) - activestorage (7.0.4.2) - actionpack (= 7.0.4.2) - activejob (= 7.0.4.2) - activerecord (= 7.0.4.2) - activesupport (= 7.0.4.2) + activemodel (7.1.3.3) + activesupport (= 7.1.3.3) + activerecord (7.1.3.3) + activemodel (= 7.1.3.3) + activesupport (= 7.1.3.3) + timeout (>= 0.4.0) + activestorage (7.1.3.3) + actionpack (= 7.1.3.3) + activejob (= 7.1.3.3) + activerecord (= 7.1.3.3) + activesupport (= 7.1.3.3) marcel (~> 1.0) - mini_mime (>= 1.1.0) - activesupport (7.0.4.2) + activesupport (7.1.3.3) + base64 + bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) minitest (>= 5.1) + mutex_m tzinfo (~> 2.0) amq-protocol (2.3.2) ast (2.4.2) + base64 (0.2.0) + bigdecimal (3.1.8) builder (3.2.4) bundler-audit (0.9.1) bundler (>= 1.2.0, < 3) @@ -86,44 +97,51 @@ GEM amq-protocol (~> 2.3, >= 2.3.1) sorted_set (~> 1, >= 1.0.2) coderay (1.1.3) - concurrent-ruby (1.2.0) + concurrent-ruby (1.2.3) + connection_pool (2.4.1) crass (1.0.6) - date (3.3.3) - diff-lcs (1.5.0) + date (3.3.4) + diff-lcs (1.5.1) docile (1.4.0) + drb (2.2.1) erubi (1.12.0) - globalid (1.1.0) - activesupport (>= 5.0) - i18n (1.12.0) + globalid (1.2.1) + activesupport (>= 6.1) + i18n (1.14.5) concurrent-ruby (~> 1.0) + io-console (0.7.2) + irb (1.13.1) + rdoc (>= 4.0.0) + reline (>= 0.4.2) json (2.6.3) lamian (1.7.0) rails (>= 4.2) - loofah (2.19.1) + loofah (2.22.0) crass (~> 1.0.2) - nokogiri (>= 1.5.9) + nokogiri (>= 1.12.0) mail (2.8.1) mini_mime (>= 0.1.1) net-imap net-pop net-smtp - marcel (1.0.2) - method_source (1.0.0) - mini_mime (1.1.2) - mini_portile2 (2.8.1) - minitest (5.17.0) - net-imap (0.3.4) + marcel (1.0.4) + method_source (1.1.0) + mini_mime (1.1.5) + mini_portile2 (2.8.6) + minitest (5.23.1) + mutex_m (0.2.0) + net-imap (0.4.11) date net-protocol net-pop (0.1.2) net-protocol - net-protocol (0.2.1) + net-protocol (0.2.2) timeout - net-smtp (0.3.3) + net-smtp (0.5.0) net-protocol - nio4r (2.5.8) - nokogiri (1.14.2) - mini_portile2 (~> 2.8.0) + nio4r (2.7.3) + nokogiri (1.16.5) + mini_portile2 (~> 2.8.2) racc (~> 1.4) parallel (1.22.1) parser (3.2.1.0) @@ -131,41 +149,56 @@ GEM pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) - racc (1.6.2) - rack (2.2.6.2) - rack-test (2.0.2) + psych (5.1.2) + stringio + racc (1.8.0) + rack (3.0.11) + rack-session (2.0.0) + rack (>= 3.0.0) + rack-test (2.1.0) rack (>= 1.3) - rails (7.0.4.2) - actioncable (= 7.0.4.2) - actionmailbox (= 7.0.4.2) - actionmailer (= 7.0.4.2) - actionpack (= 7.0.4.2) - actiontext (= 7.0.4.2) - actionview (= 7.0.4.2) - activejob (= 7.0.4.2) - activemodel (= 7.0.4.2) - activerecord (= 7.0.4.2) - activestorage (= 7.0.4.2) - activesupport (= 7.0.4.2) + rackup (2.1.0) + rack (>= 3) + webrick (~> 1.8) + rails (7.1.3.3) + actioncable (= 7.1.3.3) + actionmailbox (= 7.1.3.3) + actionmailer (= 7.1.3.3) + actionpack (= 7.1.3.3) + actiontext (= 7.1.3.3) + actionview (= 7.1.3.3) + activejob (= 7.1.3.3) + activemodel (= 7.1.3.3) + activerecord (= 7.1.3.3) + activestorage (= 7.1.3.3) + activesupport (= 7.1.3.3) bundler (>= 1.15.0) - railties (= 7.0.4.2) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) + railties (= 7.1.3.3) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.5.0) - loofah (~> 2.19, >= 2.19.1) - railties (7.0.4.2) - actionpack (= 7.0.4.2) - activesupport (= 7.0.4.2) - method_source + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + railties (7.1.3.3) + actionpack (= 7.1.3.3) + activesupport (= 7.1.3.3) + irb + rackup (>= 1.0.0) rake (>= 12.2) - thor (~> 1.0) - zeitwerk (~> 2.5) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.0.6) + rake (13.2.1) rbtree (0.4.6) + rdoc (6.7.0) + psych (>= 4.0.0) regexp_parser (2.7.0) - rexml (3.2.5) + reline (0.5.7) + io-console (~> 0.5) + rexml (3.2.8) + strscan (>= 3.0.9) rspec (3.12.0) rspec-core (~> 3.12.0) rspec-expectations (~> 3.12.0) @@ -178,10 +211,10 @@ GEM rspec-its (1.3.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.12.3) + rspec-mocks (3.12.7) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) - rspec-support (3.12.0) + rspec-support (3.12.2) rubocop (1.43.0) json (~> 2.3) parallel (~> 1.10) @@ -235,17 +268,20 @@ GEM sorted_set (1.0.3) rbtree set (~> 1.0) + stringio (3.1.0) + strscan (3.1.0) tainbox (2.1.2) activesupport - thor (1.2.1) - timeout (0.3.2) + thor (1.3.1) + timeout (0.4.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) - websocket-driver (0.7.5) + webrick (1.8.1) + websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - zeitwerk (2.6.7) + zeitwerk (2.6.15) PLATFORMS ruby diff --git a/README.md b/README.md index e7a4f0d..cd39a21 100644 --- a/README.md +++ b/README.md @@ -148,11 +148,15 @@ Rabbit.publish( `Rabbit::Daemon.run`. `before_fork` and `after_fork` procs in `Rabbit.config` are used to teardown and setup external connections between daemon restarts, for example ORM connections -- After the server runs, received messages are handled by `Rabbit::EventHandler` subclasses. - Subclasses are selected by following code: - ```ruby - "rabbit/handler/#{group_id}/#{event}".camelize.constantize - ``` +- After the server runs, received messages are handled by `Rabbit::EventHandler` subclasses in two possible ways: + a) Subclasses are selected by following code(by default): + ```ruby + "rabbit/handler/#{group_id}/#{event}".camelize.constantize + ``` + b) you can change default behaviour to your own logic by setting the `handler_resolver_callable` config option with a `Proc` that should return the handler class: + ```ruby + Rabbit.config.handler_resolver_callable = -> (group_id, event) { "recivers/#{group_id}/#{event}".camelize.constantize } + ``` They use powerful `Tainbox` api to handle message data. Project_id also passed to them. diff --git a/lib/rabbit.rb b/lib/rabbit.rb index 8a9fa31..d1dc755 100644 --- a/lib/rabbit.rb +++ b/lib/rabbit.rb @@ -22,6 +22,7 @@ class Config attribute :environment, Symbol, default: :production attribute :queue_name_conversion attribute :receiving_job_class_callable + attribute :handler_resolver_callable attribute :exception_notifier attribute :before_receiving_hooks, default: [] attribute :after_receiving_hooks, default: [] diff --git a/lib/rabbit/receiving/handler_resolver.rb b/lib/rabbit/receiving/handler_resolver.rb index 03cf9ea..71106ca 100644 --- a/lib/rabbit/receiving/handler_resolver.rb +++ b/lib/rabbit/receiving/handler_resolver.rb @@ -21,8 +21,13 @@ def handler_for(message) private def unmemoized_handler_for(group_id, event) - name = "rabbit/handler/#{group_id}/#{event}".camelize - handler = name.safe_constantize + handler = if Rabbit.config.handler_resolver_callable.is_a?(Proc) + Rabbit.config.handler_resolver_callable.call(group_id, event) + else + name = "rabbit/handler/#{group_id}/#{event}".camelize + name.safe_constantize + end + if handler && handler < Rabbit::EventHandler handler else diff --git a/lib/rabbit/version.rb b/lib/rabbit/version.rb index ed79da5..83c6f43 100644 --- a/lib/rabbit/version.rb +++ b/lib/rabbit/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Rabbit - VERSION = "0.14.0" + VERSION = "0.15.0" end diff --git a/rabbit_messaging.gemspec b/rabbit_messaging.gemspec index 924b39a..b73accf 100644 --- a/rabbit_messaging.gemspec +++ b/rabbit_messaging.gemspec @@ -5,7 +5,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "rabbit/version" Gem::Specification.new do |spec| - spec.required_ruby_version = ">= 2.7" + spec.required_ruby_version = ">= 3.0" spec.name = "rabbit_messaging" spec.version = Rabbit::VERSION diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4bbff88..67adfbf 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -20,6 +20,9 @@ SimpleCov.add_filter "spec" SimpleCov.start +require "active_support/deprecation" +require "active_support/deprecator" + require "rabbit_messaging" require "rspec/its" diff --git a/spec/units/rabbit/receiving_spec.rb b/spec/units/rabbit/receiving_spec.rb index 1f40fc0..fbf077b 100644 --- a/spec/units/rabbit/receiving_spec.rb +++ b/spec/units/rabbit/receiving_spec.rb @@ -47,6 +47,8 @@ def expect_hooks_to_be_called before do Rabbit.config.queue_name_conversion = -> (queue) { "#{queue}_prepared" } + Rabbit.config.handler_resolver_callable = nil + Rabbit.config.before_receiving_hooks = [before_hook] Rabbit.config.after_receiving_hooks = [after_hook] @@ -58,20 +60,68 @@ def expect_hooks_to_be_called handler.ignore_queue_conversion = conversion end - after do - worker.work_with_params(message, delivery_info, arguments) - end + subject(:run_receive) { worker.work_with_params(message, delivery_info, arguments) } shared_examples "check job queue and some handler" do specify do expect_job_queue_to_be_set expect_some_handler_to_be_called expect_hooks_to_be_called + + run_receive end end context "job enqueued successfully" do context "message is valid" do + context "handler resolver represent by config" do + before { Rabbit.config.handler_resolver_callable = -> (_m, _g) { test_handler } } + after { Rabbit.config.handler_resolver_callable = nil } + + let(:test_handler) do + Class.new(Rabbit::EventHandler) do + def self.queue + :test + end + + def call; end + end + end + let(:queue) { "test_prepared" } + let(:event) { "magic_event" } + + it "perform our handler" do + expect(test_handler).to receive(:new).and_call_original + expect_any_instance_of(test_handler).to receive(:call) do |inst| + expect(inst.message.data).to eq(message) + end + expect(handler).not_to receive(:new) + + run_receive + end + + context "but resolved handler is invalid" do + let(:event) { "magic_evnet2" } + let(:test_handler) do + Class.new do + def self.queue + :test + end + + def call; end + end + end + + it "notifies about exception" do + expect_notification do |exception| + expect(exception.message).to eq(error_msg) + end + + run_receive + end + end + end + context "handler is found" do let(:queue) { "world_some_successful_event_prepared" } @@ -80,6 +130,8 @@ def expect_hooks_to_be_called expect_job_queue_to_be_set expect_some_handler_to_be_called + + run_receive end context "job performs unsuccessfully" do @@ -92,6 +144,8 @@ def expect_hooks_to_be_called expect_notification do |exception| expect(exception.message).to eq("Unsuccessful event error") end + + run_receive end end @@ -114,6 +168,8 @@ def expect_hooks_to_be_called expect_job_queue_to_be_set expect_empty_handler_to_be_called expect_hooks_to_be_called + + run_receive end end end @@ -133,6 +189,8 @@ def expect_hooks_to_be_called expect_job_queue_to_be_set expect_empty_handler_to_be_called expect_hooks_to_be_called + + run_receive end end end @@ -162,6 +220,8 @@ def expect_hooks_to_be_called expect_notification do |exception| expect(exception.message).to eq(error_msg) end + + run_receive end end end @@ -173,6 +233,7 @@ def expect_hooks_to_be_called # can't set job, raises malformed message when tries to determine queue name it "notifies about exception" do expect_notification.with(Rabbit::Receiving::MalformedMessage) + run_receive end end @@ -193,6 +254,8 @@ def expect_hooks_to_be_called expect(job_class).not_to receive(:set).with(queue: queue) expect(custom_job_class).to receive(:set).with(queue: queue) expect(custom_job).to receive(:perform_later) + + run_receive end it "receiving_job_class_callable receives the full message context" do @@ -201,6 +264,8 @@ def expect_hooks_to_be_called delivery_info: delivery_info, arguments: arguments, ) + + run_receive end end end @@ -218,6 +283,8 @@ def expect_hooks_to_be_called specify do expect_notification.with(error) expect(worker).to receive(:requeue!) + + run_receive end end end