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
change moyqumlqxtwnqrtkyxwnvkloroplrzzv
commit f4eafddf4d24a8b8dbcc7c96ea0a29f7bf8fb110
author Alpha Chen <alpha@kejadlen.dev>
date
parent qkowsttp
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