Skip to content

Commit d5be3f4

Browse files
committed
feat: adopt zeitwerk-backed web boot
1 parent 4701231 commit d5be3f4

66 files changed

Lines changed: 375 additions & 266 deletions

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

app.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
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')
1012

1113
module Html2rss
1214
module Web
@@ -85,6 +87,8 @@ def development? = self.class.development?
8587

8688
plugin :json_parser
8789
plugin :public
90+
plugin :head
91+
plugin :not_allowed
8892
plugin :exception_page
8993
plugin :error_handler do |error|
9094
next exception_page(error) if development?

app/errors/exceptions.rb

Lines changed: 0 additions & 70 deletions
This file was deleted.
Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
# frozen_string_literal: true
22

3-
require_relative '../config/local_config'
4-
require_relative 'security_logger'
5-
63
module Html2rss
74
module Web
85
##
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
# frozen_string_literal: true
22

3-
require_relative '../../errors/exceptions'
4-
53
module Html2rss
64
module Web
75
module Api
86
module V1
97
module Contract
108
CODES = {
11-
unauthorized: UnauthorizedError::CODE,
12-
forbidden: ForbiddenError::CODE,
13-
internal_server_error: InternalServerError::CODE
9+
unauthorized: Html2rss::Web::UnauthorizedError::CODE,
10+
forbidden: Html2rss::Web::ForbiddenError::CODE,
11+
internal_server_error: Html2rss::Web::InternalServerError::CODE
1412
}.freeze
1513

1614
MESSAGES = {
Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,16 @@
33
require 'time'
44
require 'json'
55

6-
require_relative '../../security/auth'
7-
require_relative '../../domain/auto_source'
8-
require_relative '../../errors/exceptions'
9-
require_relative '../../security/url_validator'
10-
require_relative '../../telemetry/observability'
11-
require_relative 'feed_metadata'
12-
require_relative 'response'
13-
146
module Html2rss
157
module Web
168
module Api
179
module V1
1810
##
1911
# Creates stable feed records from authenticated API requests.
20-
module CreateFeed
12+
module CreateFeed # rubocop:disable Metrics/ModuleLength
2113
FEED_ATTRIBUTE_KEYS =
2214
%i[id name url strategy feed_token public_url json_public_url created_at updated_at].freeze
23-
class << self
15+
class << self # rubocop:disable Metrics/ClassLength
2416
# Creates a feed and returns a normalized API success payload.
2517
#
2618
# @param request [Rack::Request] HTTP request with auth context.
@@ -41,14 +33,14 @@ def call(request)
4133

4234
# @return [void]
4335
def ensure_auto_source_enabled!
44-
raise ForbiddenError, Contract::MESSAGES[:auto_source_disabled] unless AutoSource.enabled?
36+
raise Html2rss::Web::ForbiddenError, Contract::MESSAGES[:auto_source_disabled] unless AutoSource.enabled?
4537
end
4638

4739
# @param request [Rack::Request]
4840
# @return [Hash]
4941
def require_account(request)
5042
account = Auth.authenticate(request)
51-
raise UnauthorizedError, 'Authentication required' unless account
43+
raise Html2rss::Web::UnauthorizedError, 'Authentication required' unless account
5244

5345
account
5446
end
@@ -57,25 +49,35 @@ def require_account(request)
5749
# @param account [Hash]
5850
# @return [Html2rss::Web::Api::V1::FeedMetadata::CreateParams]
5951
def build_create_params(params, account)
60-
url = params['url'].to_s.strip
61-
raise BadRequestError, 'URL parameter is required' if url.empty?
62-
raise BadRequestError, 'Invalid URL format' unless UrlValidator.valid_url?(url)
63-
raise ForbiddenError, 'URL not allowed for this account' unless UrlValidator.url_allowed?(account, url)
64-
52+
url = validated_url(params['url'], account)
6553
FeedMetadata::CreateParams.new(
6654
url: url,
6755
name: FeedMetadata.site_title_for(url),
6856
strategy: normalize_strategy(params['strategy'])
6957
)
7058
end
7159

60+
# @param raw_url [String, nil]
61+
# @param account [Hash]
62+
# @return [String]
63+
def validated_url(raw_url, account)
64+
url = raw_url.to_s.strip
65+
raise Html2rss::Web::BadRequestError, 'URL parameter is required' if url.empty?
66+
raise Html2rss::Web::BadRequestError, 'Invalid URL format' unless UrlValidator.valid_url?(url)
67+
unless UrlValidator.url_allowed?(account, url)
68+
raise Html2rss::Web::ForbiddenError, 'URL not allowed for this account'
69+
end
70+
71+
url
72+
end
73+
7274
# @param raw_strategy [String, nil]
7375
# @return [String]
7476
def normalize_strategy(raw_strategy)
7577
strategy = raw_strategy.to_s.strip
7678
strategy = default_strategy if strategy.empty?
7779

78-
raise BadRequestError, 'Unsupported strategy' unless supported_strategies.include?(strategy)
80+
raise Html2rss::Web::BadRequestError, 'Unsupported strategy' unless supported_strategy?(strategy)
7981

8082
strategy
8183
end
@@ -85,6 +87,12 @@ def supported_strategies
8587
Html2rss::RequestService.strategy_names.map(&:to_s)
8688
end
8789

90+
# @param strategy [String]
91+
# @return [Boolean]
92+
def supported_strategy?(strategy)
93+
supported_strategies.include?(strategy)
94+
end
95+
8896
# @return [String] default strategy identifier.
8997
def default_strategy
9098
Html2rss::RequestService.default_strategy_name.to_s
@@ -95,7 +103,6 @@ def default_strategy
95103
def feed_attributes(feed_data)
96104
timestamp = Time.now.iso8601
97105
typed_feed = feed_metadata(feed_data)
98-
99106
typed_feed_attributes(typed_feed, timestamp).slice(*FEED_ATTRIBUTE_KEYS)
100107
end
101108

@@ -109,11 +116,11 @@ def request_params(request)
109116
return request.params if raw_body.strip.empty?
110117

111118
parsed = JSON.parse(raw_body)
112-
raise BadRequestError, 'Invalid JSON payload' unless parsed.is_a?(Hash)
119+
raise Html2rss::Web::BadRequestError, 'Invalid JSON payload' unless parsed.is_a?(Hash)
113120

114121
request.params.merge(parsed)
115122
rescue JSON::ParserError
116-
raise BadRequestError, 'Invalid JSON payload'
123+
raise Html2rss::Web::BadRequestError, 'Invalid JSON payload'
117124
end
118125

119126
# @param request [Rack::Request]
@@ -131,7 +138,7 @@ def build_feed_from_request(request)
131138
params = build_create_params(request_params(request), account)
132139

133140
feed_data = AutoSource.create_stable_feed(params.name, params.url, account, params.strategy)
134-
raise InternalServerError, 'Failed to create feed' unless feed_data
141+
raise Html2rss::Web::InternalServerError, 'Failed to create feed' unless feed_data
135142

136143
[params, feed_data]
137144
end
Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@
22

33
require 'time'
44

5-
require_relative '../../security/auth'
6-
require_relative '../../errors/exceptions'
7-
require_relative '../../config/local_config'
8-
require_relative 'contract'
9-
require_relative 'response'
10-
115
module Html2rss
126
module Web
137
module Api
@@ -70,14 +64,14 @@ def authorize_health_check!(request)
7064
account = Auth.authenticate(request)
7165
return if account && account[:username] == 'health-check'
7266

73-
raise UnauthorizedError, 'Health check authentication required'
67+
raise Html2rss::Web::UnauthorizedError, 'Health check authentication required'
7468
end
7569

7670
# @return [void]
7771
def verify_configuration!
7872
LocalConfig.yaml
7973
rescue StandardError
80-
raise InternalServerError, Contract::MESSAGES[:health_check_failed]
74+
raise Html2rss::Web::InternalServerError, Contract::MESSAGES[:health_check_failed]
8175
end
8276
end
8377
end

0 commit comments

Comments
 (0)