Add dav_verbs Roda plugin for CardDAV routing
Checks REQUEST_METHOD directly since Rack lacks WebDAV verb helpers.
Assisted-by: Claude Opus 4.5 via Claude Code
diff --git a/lib/roda/plugins/dav_verbs.rb b/lib/roda/plugins/dav_verbs.rb
new file mode 100644
index 0000000..2a810cd
--- /dev/null
+++ b/lib/roda/plugins/dav_verbs.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require "roda"
+
+class Roda
+ module RodaPlugins
+ # Adds routing methods for WebDAV/CardDAV HTTP verbs.
+ #
+ # Only PROPFIND and REPORT are implemented since this is a read-only
+ # CardDAV server. PROPFIND handles discovery and property retrieval,
+ # REPORT handles addressbook-query and addressbook-multiget requests.
+ #
+ # Other WebDAV verbs (PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK)
+ # would be needed for write support.
+ module DavVerbs
+ module RequestMethods
+ %w[propfind report].each do |verb|
+ class_eval(<<-END, __FILE__, __LINE__ + 1)
+ def #{verb}(*args, &block)
+ _verb(args, &block) if env["REQUEST_METHOD"] == "#{verb.upcase}"
+ end
+ END
+ end
+ end
+ end
+
+ register_plugin(:dav_verbs, DavVerbs)
+ end
+end
diff --git a/test/roda/plugins/test_dav_verbs.rb b/test/roda/plugins/test_dav_verbs.rb
new file mode 100644
index 0000000..7b8ac0c
--- /dev/null
+++ b/test/roda/plugins/test_dav_verbs.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require "minitest/autorun"
+require "rack/test"
+
+require "roda/plugins/dav_verbs"
+
+class DavVerbsTest < Minitest::Test
+ include Rack::Test::Methods
+
+ class App < Roda
+ plugin :dav_verbs
+
+ route do |r|
+ r.propfind do
+ "propfind"
+ end
+
+ r.report do
+ "report"
+ end
+ end
+ end
+
+ def app
+ App
+ end
+
+ def test_propfind
+ request "/", method: "PROPFIND"
+
+ assert_equal 200, last_response.status
+ assert_equal "propfind", last_response.body
+ end
+
+ def test_report
+ request "/", method: "REPORT"
+
+ assert_equal 200, last_response.status
+ assert_equal "report", last_response.body
+ end
+end