-
Notifications
You must be signed in to change notification settings - Fork 1
Add ClientStub, setup/client_for API, and Railtie #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
883a8cf
52a1b99
969adbe
f363014
7ac8824
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,2 +1,37 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| require "active_support/core_ext/hash/keys" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| require "rspamd/client" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| require "rspamd/client_stub" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| require "rspamd/errors" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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 ||= {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+25
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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 ||= {} | |
| require "concurrent/map" | |
| module Rspamd | |
| class << self | |
| def setup(config) | |
| @config = config.deep_symbolize_keys | |
| @clients = Concurrent::Map.new | |
| end | |
| def client_for(name) | |
| clients.fetch_or_store(name) { enabled? ? build_client(name) : ClientStub.new } | |
| end | |
| def reset! | |
| @config = nil | |
| @clients = Concurrent::Map.new | |
| end | |
| private | |
| def clients | |
| @clients ||= Concurrent::Map.new |
Copilot
AI
Mar 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
build_client only passes host, port, and password to Client.new, but Configuration supports additional options: scheme, open_timeout, read_timeout, and user_agent. If any of these are specified in the YAML config, they'll be silently ignored. Consider forwarding all recognized options, or at minimum documenting that only these three are supported via the YAML config.
| Client.new(**settings.slice(:host, :port, :password)) | |
| Client.new(**settings.slice(:host, :port, :password, :scheme, :open_timeout, :read_timeout, :user_agent)) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| require "rspamd/check/result" | ||
|
|
||
| module Rspamd | ||
| class ClientStub | ||
| HAM_RESULT = Check::Result.new( | ||
| "score" => 0.0, | ||
rosa marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| "required_score" => 15.0, | ||
| "action" => "no action", | ||
| "is_skipped" => false, | ||
| "symbols" => {}, | ||
| "urls" => [], | ||
| "emails" => [] | ||
| ).freeze | ||
|
Comment on lines
+5
to
+13
|
||
|
|
||
| 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 | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,11 @@ | ||||||||||||||||||||||||||||||||||||||
| module Rspamd | ||||||||||||||||||||||||||||||||||||||
| class Railtie < ::Rails::Railtie | ||||||||||||||||||||||||||||||||||||||
| initializer "rspamd.setup" do | ||||||||||||||||||||||||||||||||||||||
| config_path = ::Rails.root.join("config/rspamd.yml") | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if config_path.exist? | ||||||||||||||||||||||||||||||||||||||
| Rspamd.setup(::Rails.application.config_for(:rspamd)) | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+3
to
+7
|
||||||||||||||||||||||||||||||||||||||
| initializer "rspamd.setup" do | |
| config_path = ::Rails.root.join("config/rspamd.yml") | |
| if config_path.exist? | |
| Rspamd.setup(::Rails.application.config_for(:rspamd)) | |
| initializer "rspamd.setup" do |app| | |
| config_path = ::Rails.root.join("config/rspamd.yml") | |
| if config_path.exist? | |
| app.config.after_initialize do | |
| begin | |
| Rspamd.setup(app.config_for(:rspamd)) | |
| rescue KeyError => e | |
| if defined?(::Rails) && ::Rails.respond_to?(:logger) && ::Rails.logger | |
| ::Rails.logger.warn("Rspamd configuration for environment #{::Rails.env} is missing in config/rspamd.yml: #{e.message}") | |
| end | |
| end | |
| end |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -8,7 +8,9 @@ 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" | ||||||
|
||||||
| s.add_dependency "activesupport", ">= 6.0" | |
| s.add_development_dependency "activesupport", ">= 6.0" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
enabled?is false, every call toclient_forwith the same name will create a newClientStubinstance only if caching works — but whensetupwas never called (@configis nil),enabled?returnsnil(falsy) andbuild_clientis skipped, yet a newClientStubis created and cached. However, whenenabled?is explicitlyfalse, a newClientStubis still created per unique name. Consider caching a single sharedClientStubinstance (e.g.,STUB = ClientStub.new) to avoid unnecessary allocations ifclient_foris called with many different names while disabled.