Skip to content

Commit 85ab667

Browse files
authored
feat: migrate to zeitwerk (#878)
This pull request introduces a major refactor of the backend code structure to adopt the Zeitwerk autoloader, improves backend maintainability, and updates development and linting workflows. It removes manual requires and directory-specific namespace wiring, reorganizes files under the `app/web/**` directory, and ensures that all code is autoloaded in a consistent, Rails-like fashion. Additionally, it adds a Zeitwerk eager-load check to the linting process and updates documentation accordingly. Several files are renamed or removed to reflect the new structure, and some error-handling and validation logic is refactored for clarity and consistency. **Backend autoloading and structure improvements:** - Introduced the `zeitwerk` gem and removed the `rack-unreloader` gem, enabling Zeitwerk-based autoloading for backend code under the `Html2rss::Web` namespace, and updated `app.rb` to use `Html2rss::Web::Boot` for setup and eager loading. [[1]](diffhunk://#diff-d09ea66f8227784ff4393d88a19836f321c915ae10031d16c93d67e6283ab55fR20-L25) [[2]](diffhunk://#diff-f965f92b425fb2f75d38b491b2625fe21b8af20b7666217546bce8a42b198ea4L9-R12) - Moved backend files into the `app/web/**` directory to match Zeitwerk's expectations, and removed manual `require` statements in favor of autoloading. [[1]](diffhunk://#diff-f965f92b425fb2f75d38b491b2625fe21b8af20b7666217546bce8a42b198ea4L9-R12) [[2]](diffhunk://#diff-17e1b7bf4af1869b4be3173dde8b6bffaff1e1e2a6e767b728c9a9f3685d490fL1-L52) [[3]](diffhunk://#diff-02e7c9c52be0b12bd7c0a8910a098e50de253b9654172526667dec2bfdba4970L1-L57) [[4]](diffhunk://#diff-884604050285643eb6fb29195c6342cd73a3abae7f3acba5b1bbc41a196d31e3L1-L70) [[5]](diffhunk://#diff-9df4f6ed17ac1a74c68891a27d537de8e617e0b99e7803fbf42ab523a7043bb6L1-L158) [[6]](diffhunk://#diff-f2b0bbe2a08fb1204f3985f426ee5aeda21d24f8d4b598e4162b43d87649b674L3-R11) [[7]](diffhunk://#diff-0764627d99177e329667ae8f5c98c756ac3d8ae43bdf768b3af26403f713e281L6-R15) **Development and linting workflow updates:** - Added a Zeitwerk eager-load verification task (`rake zeitwerk:verify`) to the `Makefile` linting process and as part of `make ready`, ensuring loader drift is caught early in development. [[1]](diffhunk://#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52R61-R62) [[2]](diffhunk://#diff-ee98e028c59b193d58fde56ab4daf54d43c486ae674e63d50ddf300b07943e0fR142-R152) [[3]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R69-R72) **Error handling and validation refactoring:** - Updated error handling in API modules to reference error classes via the fully qualified `Html2rss::Web` namespace, and refactored validation logic in `CreateFeed` for clarity and maintainability. [[1]](diffhunk://#diff-0764627d99177e329667ae8f5c98c756ac3d8ae43bdf768b3af26403f713e281L44-R43) [[2]](diffhunk://#diff-0764627d99177e329667ae8f5c98c756ac3d8ae43bdf768b3af26403f713e281L60-R80) [[3]](diffhunk://#diff-0764627d99177e329667ae8f5c98c756ac3d8ae43bdf768b3af26403f713e281R90-R95) [[4]](diffhunk://#diff-0764627d99177e329667ae8f5c98c756ac3d8ae43bdf768b3af26403f713e281L112-R123) [[5]](diffhunk://#diff-0764627d99177e329667ae8f5c98c756ac3d8ae43bdf768b3af26403f713e281L134-R141) **Roda plugin and middleware changes:** - Added the `:head` and `:not_allowed` plugins to Roda configuration, improving HTTP method handling. **Documentation updates:** - Updated `README.md` to explain the new backend file structure, Zeitwerk integration, and contributor guidelines for backend code placement.
1 parent 4701231 commit 85ab667

87 files changed

Lines changed: 1030 additions & 582 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Gemfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ gem 'rack-cache'
1717
gem 'rack-timeout'
1818
gem 'roda'
1919
gem 'ssrf_filter'
20+
gem 'zeitwerk'
2021

2122
gem 'puma', require: false
2223

2324
group :development do
2425
gem 'byebug'
25-
gem 'rack-unreloader'
2626
gem 'rake', require: false
2727
gem 'rubocop', require: false
2828
gem 'rubocop-performance', require: false

Gemfile.lock

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,6 @@ GEM
246246
rack-test (2.2.0)
247247
rack (>= 1.3)
248248
rack-timeout (0.7.0)
249-
rack-unreloader (2.1.0)
250249
rails-dom-testing (2.3.0)
251250
activesupport (>= 5.0.0)
252251
minitest
@@ -375,7 +374,6 @@ DEPENDENCIES
375374
rack-cache
376375
rack-test
377376
rack-timeout
378-
rack-unreloader
379377
rake
380378
roda
381379
rspec
@@ -393,6 +391,7 @@ DEPENDENCIES
393391
vcr
394392
webmock
395393
yard
394+
zeitwerk
396395

397396
CHECKSUMS
398397
actionpack (8.1.2) sha256=ced74147a1f0daafaa4bab7f677513fd4d3add574c7839958f7b4f1de44f8423
@@ -483,7 +482,6 @@ CHECKSUMS
483482
rack-session (2.1.1) sha256=0b6dc07dea7e4b583f58a48e8b806d4c9f1c6c9214ebc202ec94562cbea2e4e9
484483
rack-test (2.2.0) sha256=005a36692c306ac0b4a9350355ee080fd09ddef1148a5f8b2ac636c720f5c463
485484
rack-timeout (0.7.0) sha256=757337e9793cca999bb73a61fe2a7d4280aa9eefbaf787ce3b98d860749c87d9
486-
rack-unreloader (2.1.0) sha256=18879cf2ced8ca21a01836bca706f65cce6ebe3f7d9d8a5157ce68ca62c7263a
487485
rails-dom-testing (2.3.0) sha256=8acc7953a7b911ca44588bf08737bc16719f431a1cc3091a292bca7317925c1d
488486
rails-html-sanitizer (1.7.0) sha256=28b145cceaf9cc214a9874feaa183c3acba036c9592b19886e0e45efc62b1e89
489487
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ lint: lint-ruby lint-js ## Run all linters (Ruby + Frontend) - errors when issue
5858
lint-ruby: ## Run Ruby linter (RuboCop) - errors when issues found
5959
@echo "Running RuboCop linting..."
6060
bundle exec rubocop
61+
@echo "Running Zeitwerk eager-load check..."
62+
bundle exec rake zeitwerk:verify
6163
@echo "Running YARD public-method docs check..."
6264
bundle exec rake yard:verify_public_docs
6365
@echo "Ruby linting complete!"

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ make openapi
6666

6767
Dev URLs: Ruby app at `http://localhost:4000`, frontend dev server at `http://localhost:4001`.
6868

69+
Backend code under the `Html2rss::Web` namespace now lives under `app/web/**`, so Zeitwerk can mirror constant paths directly instead of relying on directory-specific namespace wiring.
70+
`make ready` also runs `rake zeitwerk:verify`, which eager-loads the app and fails on loader drift early.
71+
For contributors and AI agents changing backend structure, follow the placement rules in [docs/ai-agent-app-web.md](docs/ai-agent-app-web.md).
72+
6973
## Make Targets
7074

7175
| Command | Purpose |

Rakefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,14 @@ namespace :yard do
139139
puts 'YARD public method documentation check passed.'
140140
end
141141
end
142+
143+
namespace :zeitwerk do
144+
desc 'Fail when Zeitwerk cannot eager load the app tree cleanly'
145+
task :verify do
146+
ENV['RACK_ENV'] ||= 'test'
147+
require_relative 'app'
148+
149+
Html2rss::Web::Boot.eager_load!
150+
puts 'Zeitwerk eager load check passed.'
151+
end
152+
end

app.rb

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
require 'base64'
77

88
require 'html2rss'
9-
Dir[File.join(__dir__, 'app/**/*.rb')].each { |file| require file }
9+
require_relative 'app/web/boot'
10+
11+
Html2rss::Web::Boot.setup!(reloadable: ENV['RACK_ENV'] == 'development')
12+
Html2rss::Web::Boot::Setup.call!
1013

1114
module Html2rss
1215
module Web
@@ -31,13 +34,6 @@ class App < Roda
3134
def self.development? = EnvironmentValidator.development?
3235

3336
def development? = self.class.development?
34-
EnvironmentValidator.validate_environment!
35-
EnvironmentValidator.validate_production_security!
36-
Flags.validate!
37-
38-
Html2rss::RequestService.register_strategy(:ssrf_filter, SsrfFilterStrategy)
39-
Html2rss::RequestService.default_strategy_name = :ssrf_filter
40-
Html2rss::RequestService.unregister_strategy(:faraday)
4137
opts.merge!(check_dynamic_arity: false, check_arity: :warn)
4238
use RequestContextMiddleware
4339
use Rack::Cache, metastore: 'file:./tmp/rack-cache-meta', entitystore: 'file:./tmp/rack-cache-body',
@@ -85,6 +81,8 @@ def development? = self.class.development?
8581

8682
plugin :json_parser
8783
plugin :public
84+
plugin :head
85+
plugin :not_allowed
8886
plugin :exception_page
8987
plugin :error_handler do |error|
9088
next exception_page(error) if development?

app/api/v1/feed_metadata.rb

Lines changed: 0 additions & 52 deletions
This file was deleted.

app/domain/feed_identity.rb

Lines changed: 0 additions & 57 deletions
This file was deleted.

app/errors/exceptions.rb

Lines changed: 0 additions & 70 deletions
This file was deleted.

0 commit comments

Comments
 (0)