From 883a8cfc5dc02a789c16843ebb0d9d02218b4f97 Mon Sep 17 00:00:00 2001 From: Rosa Gutierrez Date: Wed, 4 Mar 2026 20:49:58 +0000 Subject: [PATCH 1/5] Add ClientStub, Rails module, and Railtie for multi-app support Adds three new components for Rails integration: - Rspamd::ClientStub: null-object client that returns ham for check and no-ops for training, used when rspamd is disabled (dev/test) - Rspamd::Rails: thread-safe client management with config loading via setup(config) and client_for(:outbound) - Rspamd::Railtie: auto-loads config/rspamd.yml on Rails boot Also adds activesupport as a runtime dependency for deep_symbolize_keys. Co-Authored-By: Claude Opus 4.6 --- Gemfile.lock | 1 + lib/rspamd-ruby.rb | 3 ++ lib/rspamd/client_stub.rb | 37 ++++++++++++++++++++++ lib/rspamd/rails.rb | 35 +++++++++++++++++++++ lib/rspamd/railtie.rb | 13 ++++++++ rspamd-ruby.gemspec | 2 ++ test/client_stub_test.rb | 46 ++++++++++++++++++++++++++++ test/rails_test.rb | 64 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 201 insertions(+) create mode 100644 lib/rspamd/client_stub.rb create mode 100644 lib/rspamd/rails.rb create mode 100644 lib/rspamd/railtie.rb create mode 100644 test/client_stub_test.rb create mode 100644 test/rails_test.rb diff --git a/Gemfile.lock b/Gemfile.lock index d9df3cd..b76936f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -13,6 +13,7 @@ PATH remote: . specs: rspamd-ruby (1.0.0) + activesupport (>= 6.0) GEM remote: https://rubygems.org/ diff --git a/lib/rspamd-ruby.rb b/lib/rspamd-ruby.rb index 4617b9b..9b8d50f 100644 --- a/lib/rspamd-ruby.rb +++ b/lib/rspamd-ruby.rb @@ -1,2 +1,5 @@ require "rspamd/client" +require "rspamd/client_stub" require "rspamd/errors" +require "rspamd/rails" +require "rspamd/railtie" if defined?(::Rails::Railtie) diff --git a/lib/rspamd/client_stub.rb b/lib/rspamd/client_stub.rb new file mode 100644 index 0000000..a8bacae --- /dev/null +++ b/lib/rspamd/client_stub.rb @@ -0,0 +1,37 @@ +module Rspamd + class ClientStub + HAM_RESULT = Check::Result.new( + "score" => 0.0, + "required_score" => 15.0, + "action" => "no action", + "is_skipped" => false, + "symbols" => {}, + "urls" => [], + "emails" => [] + ).freeze + + def ping + true + end + + def check(message, headers: {}) + HAM_RESULT + end + + def spam!(message) + true + end + + def ham!(message) + true + end + + def add_fuzzy(message, flag: 1, weight: 1) + true + end + + def delete_fuzzy(message, flag: 1) + true + end + end +end diff --git a/lib/rspamd/rails.rb b/lib/rspamd/rails.rb new file mode 100644 index 0000000..11ebde2 --- /dev/null +++ b/lib/rspamd/rails.rb @@ -0,0 +1,35 @@ +require "active_support/core_ext/hash/keys" +require "rspamd/client" +require "rspamd/client_stub" + +module Rspamd + module Rails + class << self + def setup(config) + @config = config.deep_symbolize_keys + @clients = {} + end + + def client_for(name) + return ClientStub.new unless enabled? + + @clients[name] ||= build_client(name) + end + + def reset! + @config = nil + @clients = {} + end + + private + def enabled? + @config&.dig(:enabled) + end + + def build_client(name) + settings = @config.fetch(name) { raise ArgumentError, "No rspamd configuration for #{name.inspect}" } + Client.new(**settings.slice(:host, :port, :password)) + end + end + end +end diff --git a/lib/rspamd/railtie.rb b/lib/rspamd/railtie.rb new file mode 100644 index 0000000..ad6f9d8 --- /dev/null +++ b/lib/rspamd/railtie.rb @@ -0,0 +1,13 @@ +require "rspamd/rails" + +module Rspamd + class Railtie < ::Rails::Railtie + initializer "rspamd.setup" do + config_path = ::Rails.root.join("config/rspamd.yml") + + if config_path.exist? + Rspamd::Rails.setup(::Rails.application.config_for(:rspamd)) + end + end + end +end diff --git a/rspamd-ruby.gemspec b/rspamd-ruby.gemspec index e7074f3..4d06fbd 100644 --- a/rspamd-ruby.gemspec +++ b/rspamd-ruby.gemspec @@ -10,6 +10,8 @@ Gem::Specification.new do |s| s.required_ruby_version = ">= 2.7.8" + s.add_dependency "activesupport", ">= 6.0" + s.add_development_dependency "rake", "~> 13.0" s.add_development_dependency "minitest", "> 5.11" s.add_development_dependency "webmock", "~> 3.0" diff --git a/test/client_stub_test.rb b/test/client_stub_test.rb new file mode 100644 index 0000000..e5dc8ef --- /dev/null +++ b/test/client_stub_test.rb @@ -0,0 +1,46 @@ +require "test_helper" + +class Rspamd::ClientStubTest < Minitest::Test + def setup + @stub = Rspamd::ClientStub.new + end + + def test_ping_returns_true + assert @stub.ping + end + + def test_check_returns_ham_result + result = @stub.check("message body") + assert result.ham? + assert_not result.spam? + assert_equal 0.0, result.score + assert_equal 15.0, result.required_score + assert_equal "no action", result.action + end + + def test_check_accepts_headers + result = @stub.check("message body", headers: { "Settings-Id" => "outbound" }) + assert result.ham? + end + + def test_spam_returns_true + assert @stub.spam!("message body") + end + + def test_ham_returns_true + assert @stub.ham!("message body") + end + + def test_add_fuzzy_returns_true + assert @stub.add_fuzzy("message body") + end + + def test_delete_fuzzy_returns_true + assert @stub.delete_fuzzy("message body") + end + + private + def assert_not(value) + assert !value + end +end diff --git a/test/rails_test.rb b/test/rails_test.rb new file mode 100644 index 0000000..5e24345 --- /dev/null +++ b/test/rails_test.rb @@ -0,0 +1,64 @@ +require "test_helper" + +class Rspamd::RailsTest < Minitest::Test + def teardown + Rspamd::Rails.reset! + end + + def test_returns_stub_when_disabled + Rspamd::Rails.setup("enabled" => false, "outbound" => { "host" => "localhost", "port" => 11334 }) + + client = Rspamd::Rails.client_for(:outbound) + assert_instance_of Rspamd::ClientStub, client + end + + def test_returns_client_when_enabled + Rspamd::Rails.setup("enabled" => true, "outbound" => { "host" => "rspamd.example.com", "port" => 11334 }) + + client = Rspamd::Rails.client_for(:outbound) + assert_instance_of Rspamd::Client, client + end + + def test_caches_client_instances + Rspamd::Rails.setup("enabled" => true, "outbound" => { "host" => "localhost", "port" => 11334 }) + + client1 = Rspamd::Rails.client_for(:outbound) + client2 = Rspamd::Rails.client_for(:outbound) + assert_same client1, client2 + end + + def test_returns_stub_when_not_configured + client = Rspamd::Rails.client_for(:outbound) + assert_instance_of Rspamd::ClientStub, client + end + + def test_raises_for_unknown_client_name + Rspamd::Rails.setup("enabled" => true, "outbound" => { "host" => "localhost", "port" => 11334 }) + + assert_raises(ArgumentError) { Rspamd::Rails.client_for(:nonexistent) } + end + + def test_reset_clears_config_and_clients + Rspamd::Rails.setup("enabled" => true, "outbound" => { "host" => "localhost", "port" => 11334 }) + Rspamd::Rails.client_for(:outbound) + Rspamd::Rails.reset! + + # After reset, should return stub (no config = disabled) + client = Rspamd::Rails.client_for(:outbound) + assert_instance_of Rspamd::ClientStub, client + end + + def test_accepts_string_keys + Rspamd::Rails.setup("enabled" => true, "outbound" => { "host" => "localhost", "port" => 11334 }) + + client = Rspamd::Rails.client_for(:outbound) + assert_instance_of Rspamd::Client, client + end + + def test_passes_password_to_client + Rspamd::Rails.setup("enabled" => true, "outbound" => { "host" => "localhost", "port" => 11334, "password" => "secret" }) + + client = Rspamd::Rails.client_for(:outbound) + assert_equal "secret", client.configuration.password + end +end From 52a1b99cb5b3dbcbc1a73ec7eed2fd5331b8dd08 Mon Sep 17 00:00:00 2001 From: Rosa Gutierrez Date: Wed, 4 Mar 2026 21:13:14 +0000 Subject: [PATCH 2/5] Address review feedback: add missing require, fix redundant test - Add explicit require for check/result in client_stub.rb so the file is standalone-loadable - Rename test_accepts_string_keys to test_accepts_symbol_keys and actually pass symbol keys to provide distinct coverage Co-Authored-By: Claude Opus 4.6 --- lib/rspamd/client_stub.rb | 2 ++ test/rails_test.rb | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/rspamd/client_stub.rb b/lib/rspamd/client_stub.rb index a8bacae..2eaa72c 100644 --- a/lib/rspamd/client_stub.rb +++ b/lib/rspamd/client_stub.rb @@ -1,3 +1,5 @@ +require "rspamd/check/result" + module Rspamd class ClientStub HAM_RESULT = Check::Result.new( diff --git a/test/rails_test.rb b/test/rails_test.rb index 5e24345..1acfb43 100644 --- a/test/rails_test.rb +++ b/test/rails_test.rb @@ -48,8 +48,8 @@ def test_reset_clears_config_and_clients assert_instance_of Rspamd::ClientStub, client end - def test_accepts_string_keys - Rspamd::Rails.setup("enabled" => true, "outbound" => { "host" => "localhost", "port" => 11334 }) + def test_accepts_symbol_keys + Rspamd::Rails.setup(enabled: true, outbound: { host: "localhost", port: 11334 }) client = Rspamd::Rails.client_for(:outbound) assert_instance_of Rspamd::Client, client From 969adbecc475588433789b3d29be909d216e35c9 Mon Sep 17 00:00:00 2001 From: Rosa Gutierrez Date: Wed, 4 Mar 2026 21:22:16 +0000 Subject: [PATCH 3/5] Cache ClientStub instances per name like real clients When disabled, client_for now caches the ClientStub per name instead of returning a new instance each call. This ensures the same object is returned on repeated calls, which is needed for test expectations (e.g. Rspamd.outbound_client.expects(:check)). Co-Authored-By: Claude Opus 4.6 --- lib/rspamd/rails.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/rspamd/rails.rb b/lib/rspamd/rails.rb index 11ebde2..58b1dd1 100644 --- a/lib/rspamd/rails.rb +++ b/lib/rspamd/rails.rb @@ -11,9 +11,7 @@ def setup(config) end def client_for(name) - return ClientStub.new unless enabled? - - @clients[name] ||= build_client(name) + clients[name] ||= enabled? ? build_client(name) : ClientStub.new end def reset! @@ -22,6 +20,10 @@ def reset! end private + def clients + @clients ||= {} + end + def enabled? @config&.dig(:enabled) end From f3630149bee44245bbecab47b8f7f93f27e361b4 Mon Sep 17 00:00:00 2001 From: Rosa Gutierrez Date: Wed, 4 Mar 2026 21:52:30 +0000 Subject: [PATCH 4/5] Simplify API: move setup/client_for from Rspamd::Rails to Rspamd module The Rails module name was misleading since this code works outside Rails. Moving it to the top-level Rspamd module simplifies the API: Rspamd.setup(config) Rspamd.client_for(:outbound) Co-Authored-By: Claude Opus 4.6 --- lib/rspamd-ruby.rb | 34 ++++++++++++++++++++++- lib/rspamd/rails.rb | 37 ------------------------- lib/rspamd/railtie.rb | 4 +-- test/rails_test.rb | 64 ------------------------------------------- test/setup_test.rb | 64 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 98 insertions(+), 105 deletions(-) delete mode 100644 lib/rspamd/rails.rb delete mode 100644 test/rails_test.rb create mode 100644 test/setup_test.rb diff --git a/lib/rspamd-ruby.rb b/lib/rspamd-ruby.rb index 9b8d50f..7787779 100644 --- a/lib/rspamd-ruby.rb +++ b/lib/rspamd-ruby.rb @@ -1,5 +1,37 @@ +require "active_support/core_ext/hash/keys" require "rspamd/client" require "rspamd/client_stub" require "rspamd/errors" -require "rspamd/rails" require "rspamd/railtie" if defined?(::Rails::Railtie) + +module Rspamd + class << self + def setup(config) + @config = config.deep_symbolize_keys + @clients = {} + end + + def client_for(name) + clients[name] ||= enabled? ? build_client(name) : ClientStub.new + end + + def reset! + @config = nil + @clients = {} + end + + private + def clients + @clients ||= {} + end + + def enabled? + @config&.dig(:enabled) + end + + def build_client(name) + settings = @config.fetch(name) { raise ArgumentError, "No rspamd configuration for #{name.inspect}" } + Client.new(**settings.slice(:host, :port, :password)) + end + end +end diff --git a/lib/rspamd/rails.rb b/lib/rspamd/rails.rb deleted file mode 100644 index 58b1dd1..0000000 --- a/lib/rspamd/rails.rb +++ /dev/null @@ -1,37 +0,0 @@ -require "active_support/core_ext/hash/keys" -require "rspamd/client" -require "rspamd/client_stub" - -module Rspamd - module Rails - class << self - def setup(config) - @config = config.deep_symbolize_keys - @clients = {} - end - - def client_for(name) - clients[name] ||= enabled? ? build_client(name) : ClientStub.new - end - - def reset! - @config = nil - @clients = {} - end - - private - def clients - @clients ||= {} - end - - def enabled? - @config&.dig(:enabled) - end - - def build_client(name) - settings = @config.fetch(name) { raise ArgumentError, "No rspamd configuration for #{name.inspect}" } - Client.new(**settings.slice(:host, :port, :password)) - end - end - end -end diff --git a/lib/rspamd/railtie.rb b/lib/rspamd/railtie.rb index ad6f9d8..f5b711e 100644 --- a/lib/rspamd/railtie.rb +++ b/lib/rspamd/railtie.rb @@ -1,12 +1,10 @@ -require "rspamd/rails" - module Rspamd class Railtie < ::Rails::Railtie initializer "rspamd.setup" do config_path = ::Rails.root.join("config/rspamd.yml") if config_path.exist? - Rspamd::Rails.setup(::Rails.application.config_for(:rspamd)) + Rspamd.setup(::Rails.application.config_for(:rspamd)) end end end diff --git a/test/rails_test.rb b/test/rails_test.rb deleted file mode 100644 index 1acfb43..0000000 --- a/test/rails_test.rb +++ /dev/null @@ -1,64 +0,0 @@ -require "test_helper" - -class Rspamd::RailsTest < Minitest::Test - def teardown - Rspamd::Rails.reset! - end - - def test_returns_stub_when_disabled - Rspamd::Rails.setup("enabled" => false, "outbound" => { "host" => "localhost", "port" => 11334 }) - - client = Rspamd::Rails.client_for(:outbound) - assert_instance_of Rspamd::ClientStub, client - end - - def test_returns_client_when_enabled - Rspamd::Rails.setup("enabled" => true, "outbound" => { "host" => "rspamd.example.com", "port" => 11334 }) - - client = Rspamd::Rails.client_for(:outbound) - assert_instance_of Rspamd::Client, client - end - - def test_caches_client_instances - Rspamd::Rails.setup("enabled" => true, "outbound" => { "host" => "localhost", "port" => 11334 }) - - client1 = Rspamd::Rails.client_for(:outbound) - client2 = Rspamd::Rails.client_for(:outbound) - assert_same client1, client2 - end - - def test_returns_stub_when_not_configured - client = Rspamd::Rails.client_for(:outbound) - assert_instance_of Rspamd::ClientStub, client - end - - def test_raises_for_unknown_client_name - Rspamd::Rails.setup("enabled" => true, "outbound" => { "host" => "localhost", "port" => 11334 }) - - assert_raises(ArgumentError) { Rspamd::Rails.client_for(:nonexistent) } - end - - def test_reset_clears_config_and_clients - Rspamd::Rails.setup("enabled" => true, "outbound" => { "host" => "localhost", "port" => 11334 }) - Rspamd::Rails.client_for(:outbound) - Rspamd::Rails.reset! - - # After reset, should return stub (no config = disabled) - client = Rspamd::Rails.client_for(:outbound) - assert_instance_of Rspamd::ClientStub, client - end - - def test_accepts_symbol_keys - Rspamd::Rails.setup(enabled: true, outbound: { host: "localhost", port: 11334 }) - - client = Rspamd::Rails.client_for(:outbound) - assert_instance_of Rspamd::Client, client - end - - def test_passes_password_to_client - Rspamd::Rails.setup("enabled" => true, "outbound" => { "host" => "localhost", "port" => 11334, "password" => "secret" }) - - client = Rspamd::Rails.client_for(:outbound) - assert_equal "secret", client.configuration.password - end -end diff --git a/test/setup_test.rb b/test/setup_test.rb new file mode 100644 index 0000000..a47ec44 --- /dev/null +++ b/test/setup_test.rb @@ -0,0 +1,64 @@ +require "test_helper" + +class Rspamd::SetupTest < Minitest::Test + def teardown + Rspamd.reset! + end + + def test_returns_stub_when_disabled + Rspamd.setup("enabled" => false, "outbound" => { "host" => "localhost", "port" => 11334 }) + + client = Rspamd.client_for(:outbound) + assert_instance_of Rspamd::ClientStub, client + end + + def test_returns_client_when_enabled + Rspamd.setup("enabled" => true, "outbound" => { "host" => "rspamd.example.com", "port" => 11334 }) + + client = Rspamd.client_for(:outbound) + assert_instance_of Rspamd::Client, client + end + + def test_caches_client_instances + Rspamd.setup("enabled" => true, "outbound" => { "host" => "localhost", "port" => 11334 }) + + client1 = Rspamd.client_for(:outbound) + client2 = Rspamd.client_for(:outbound) + assert_same client1, client2 + end + + def test_returns_stub_when_not_configured + client = Rspamd.client_for(:outbound) + assert_instance_of Rspamd::ClientStub, client + end + + def test_raises_for_unknown_client_name + Rspamd.setup("enabled" => true, "outbound" => { "host" => "localhost", "port" => 11334 }) + + assert_raises(ArgumentError) { Rspamd.client_for(:nonexistent) } + end + + def test_reset_clears_config_and_clients + Rspamd.setup("enabled" => true, "outbound" => { "host" => "localhost", "port" => 11334 }) + Rspamd.client_for(:outbound) + Rspamd.reset! + + # After reset, should return stub (no config = disabled) + client = Rspamd.client_for(:outbound) + assert_instance_of Rspamd::ClientStub, client + end + + def test_accepts_symbol_keys + Rspamd.setup(enabled: true, outbound: { host: "localhost", port: 11334 }) + + client = Rspamd.client_for(:outbound) + assert_instance_of Rspamd::Client, client + end + + def test_passes_password_to_client + Rspamd.setup("enabled" => true, "outbound" => { "host" => "localhost", "port" => 11334, "password" => "secret" }) + + client = Rspamd.client_for(:outbound) + assert_equal "secret", client.configuration.password + end +end From 7ac882446a275be6ce5422e1834bc6ae0d4df4a0 Mon Sep 17 00:00:00 2001 From: Rosa Gutierrez Date: Thu, 5 Mar 2026 09:42:31 +0000 Subject: [PATCH 5/5] Upgrade to Ruby 3.2+ and modernize CI workflow Ruby 2.7 is EOL and incompatible with recent bundler versions. Also update actions/checkout to v4 and use bundler-cache. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/ci.yml | 16 +--- Gemfile.lock | 160 ++++++++++++++++++++++++++------------- rspamd-ruby.gemspec | 2 +- 3 files changed, 111 insertions(+), 67 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 03ca12e..7f7241a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,23 +5,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: 2.7.8 - rubygems: latest - - - name: Cache gem dependencies - uses: actions/cache@v1 - with: - path: vendor/bundle - key: ${{ runner.os }}-bundler-${{ hashFiles('**/Gemfile.lock') }} - restore-keys: ${{ runner.os }}-bundler- - - - name: Install dependencies - run: gem install bundler && bundle install --jobs 4 --retry 3 --path vendor/bundle + ruby-version: 3.2 + bundler-cache: true - name: Run tests run: bundle exec rake test diff --git a/Gemfile.lock b/Gemfile.lock index b76936f..aa887a1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,13 +1,13 @@ GIT remote: https://github.com/basecamp/house-style.git - revision: b3ad65254828e8e8019a0d9a6205aff9ad206a77 + revision: 6f73ca5c3fd5002b48aeede2d980f9b8fe047d55 branch: main specs: - rubocop-37signals (1.0.0) - rubocop - rubocop-minitest - rubocop-performance - rubocop-rails + rubocop-37signals (1.2.1) + rubocop (>= 1.72) + rubocop-minitest (>= 0.37.0) + rubocop-performance (>= 1.24) + rubocop-rails (>= 2.30) PATH remote: . @@ -18,67 +18,121 @@ PATH GEM remote: https://rubygems.org/ specs: - activesupport (7.0.7.2) - concurrent-ruby (~> 1.0, >= 1.0.2) + activesupport (8.1.2) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) + json + logger (>= 1.4.2) minitest (>= 5.1) - tzinfo (~> 2.0) - addressable (2.8.1) - public_suffix (>= 2.0.2, < 6.0) - ast (2.4.2) - concurrent-ruby (1.2.2) - crack (0.4.3) - safe_yaml (~> 1.0.0) - debug (1.7.1) - irb (>= 1.5.0) - reline (>= 0.3.1) - hashdiff (1.0.0) - i18n (1.14.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + addressable (2.8.9) + public_suffix (>= 2.0.2, < 8.0) + ast (2.4.3) + base64 (0.3.0) + bigdecimal (4.0.1) + concurrent-ruby (1.3.6) + connection_pool (3.0.2) + crack (1.0.1) + bigdecimal + rexml + date (3.5.1) + debug (1.11.1) + irb (~> 1.10) + reline (>= 0.3.8) + drb (2.2.3) + erb (6.0.2) + hashdiff (1.2.1) + i18n (1.14.8) concurrent-ruby (~> 1.0) - io-console (0.6.0) - irb (1.6.2) - reline (>= 0.3.0) - json (2.6.3) - minitest (5.17.0) - parallel (1.22.1) - parser (3.2.1.0) + io-console (0.8.2) + irb (1.17.0) + pp (>= 0.6.0) + prism (>= 1.3.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + json (2.18.1) + json-schema (6.1.0) + addressable (~> 2.8) + bigdecimal (>= 3.1, < 5) + language_server-protocol (3.17.0.5) + lint_roller (1.1.0) + logger (1.7.0) + mcp (0.8.0) + json-schema (>= 4.1) + minitest (6.0.2) + drb (~> 2.0) + prism (~> 1.5) + parallel (1.27.0) + parser (3.3.10.2) ast (~> 2.4.1) - public_suffix (5.0.0) - rack (3.0.8) + racc + pp (0.6.3) + prettyprint + prettyprint (0.2.0) + prism (1.9.0) + psych (5.3.1) + date + stringio + public_suffix (7.0.5) + racc (1.8.1) + rack (3.2.5) rainbow (3.1.1) - rake (13.0.1) - regexp_parser (2.7.0) - reline (0.3.2) + rake (13.3.1) + rdoc (7.2.0) + erb + psych (>= 4.0.0) + tsort + regexp_parser (2.11.3) + reline (0.6.3) io-console (~> 0.5) - rexml (3.2.5) - rubocop (1.45.1) + rexml (3.4.4) + rubocop (1.85.1) json (~> 2.3) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + mcp (~> 0.6) parallel (~> 1.10) - parser (>= 3.2.0.0) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.24.1, < 2.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.49.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.26.0) - parser (>= 3.2.1.0) - rubocop-minitest (0.27.0) - rubocop (>= 0.90, < 2.0) - rubocop-performance (1.16.0) - rubocop (>= 1.7.0, < 2.0) - rubocop-ast (>= 0.4.0) - rubocop-rails (2.19.1) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.49.0) + parser (>= 3.3.7.2) + prism (~> 1.7) + rubocop-minitest (0.39.1) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.38.0, < 2.0) + rubocop-performance (1.26.1) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.47.1, < 2.0) + rubocop-rails (2.34.3) activesupport (>= 4.2.0) + lint_roller (~> 1.1) rack (>= 1.1) - rubocop (>= 1.33.0, < 2.0) - ruby-progressbar (1.11.0) - safe_yaml (1.0.5) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.44.0, < 2.0) + ruby-progressbar (1.13.0) + securerandom (0.4.1) + stringio (3.2.0) + tsort (0.2.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.4.2) - webmock (3.8.0) - addressable (>= 2.3.6) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.2.0) + uri (1.1.1) + webmock (3.26.1) + addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) diff --git a/rspamd-ruby.gemspec b/rspamd-ruby.gemspec index 4d06fbd..7512866 100644 --- a/rspamd-ruby.gemspec +++ b/rspamd-ruby.gemspec @@ -8,7 +8,7 @@ Gem::Specification.new do |s| s.summary = "Client for Rspamd's HTTP API" s.homepage = "https://github.com/basecamp/rspamd-ruby" - s.required_ruby_version = ">= 2.7.8" + s.required_ruby_version = ">= 3.2" s.add_dependency "activesupport", ">= 6.0"