syntropy 0.27.8 → 0.27.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 658bf9fbc1a5b8c52e1d1cb61caeee464cef251cde4c6d1a218a2f20e9593078
4
- data.tar.gz: 6bf485e047c873cab5b7bd1c4ff205a5873cc766fdc6341f1711430a5d00234d
3
+ metadata.gz: 65c26fb1d6424b5b628e3002cdfca2df7a01b33702111f33df20d3dc430181e5
4
+ data.tar.gz: 3f79c53dd131676541097ac806d8aeecf5c7b5a08ef17445ae6f1e57dca49d70
5
5
  SHA512:
6
- metadata.gz: ed08587be8ebdf9121b3d4c67478544cad0601b24579f98a9886dc7ebb9c4121534d5fd6a8493ccc249920d2358a30290f25249e2e9b5a72b4046a016eed7b5b
7
- data.tar.gz: d80138a4762272e738d0fd4cfdafa633527ad9112032a7eee16779c9ea41f4eedbb68a3c332a52e1d3d59ffc48d63150111e357643baed69751b82471e20c2b7
6
+ metadata.gz: 6405e81b4d65b01bca5cac90586bd184eebedb3120463862eb1db5e60437ece323b563f847902b12a6b7231bfc511d2ed8818c97b1feb179d97853506156453a
7
+ data.tar.gz: b290f23967e92db10addd23af8e712e305c7ff816d9ce02d438b9902810e79db93e57bb6c4b28e92d9e454dd27d7518b02e9d42320c4b1c64ca69e21b8347b85
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # 0.27.9 2025-12-11
2
+
3
+ - Fix hook invocation on 404
4
+
1
5
  # 0.27.8 2025-12-10
2
6
 
3
7
  - Update UringMachine, TP2
data/lib/syntropy/app.rb CHANGED
@@ -68,9 +68,10 @@ module Syntropy
68
68
  route = @router_proc.(path, req.route_params)
69
69
  if !route
70
70
  if (m = path.match(/^(.+)\/$/))
71
+ # redirect on trailing slash
71
72
  return req.redirect(m[1], Qeweney::Status::MOVED_PERMANENTLY)
72
73
  end
73
- raise Syntropy::Error.not_found('Not found')
74
+ handle_not_found(req)
74
75
  end
75
76
 
76
77
  req.route = route
@@ -104,6 +105,30 @@ module Syntropy
104
105
 
105
106
  private
106
107
 
108
+ # Handles a not found error, taking into account hooks up the tree from the
109
+ # request path.
110
+ #
111
+ # @param req [Qeweney::Reqest] request
112
+ # @return [void]
113
+ def handle_not_found(req)
114
+ closest_uptree_route = find_first_uptree_route(File.dirname(req.path))
115
+ if closest_uptree_route
116
+ proc = route_not_found_proc(closest_uptree_route)
117
+ proc.(req)
118
+ else
119
+ raise Syntropy::Error.not_found('Not found')
120
+ end
121
+ end
122
+
123
+ # Returns the find
124
+ def find_first_uptree_route(path)
125
+ route = @router_proc.(path, {})
126
+ if !route && path != '/'
127
+ route = @router_proc.(File.dirname(path), {})
128
+ end
129
+ route
130
+ end
131
+
107
132
  # Instantiates a routing tree with the app settings, and generates a router
108
133
  # proc.
109
134
  #
@@ -125,6 +150,12 @@ module Syntropy
125
150
  @routing_tree.mount_applet(path, @builtin_applet)
126
151
  end
127
152
 
153
+ def route_not_found_proc(route)
154
+ route[:not_found_proc] ||= compose_up_tree_hooks(route, ->(req) {
155
+ raise Syntropy::Error.not_found('Not found')
156
+ })
157
+ end
158
+
128
159
  # Computes the route proc for the given route, wrapping it in hooks found up
129
160
  # the routing tree.
130
161
  #
@@ -7,10 +7,10 @@ module Syntropy
7
7
  class Error < StandardError
8
8
  Status = Qeweney::Status
9
9
 
10
- # By default, the HTTP status for errors is 500 Internal Server Error
10
+ # By default, the HTTP status for errors is 500 Internal Server Error.
11
11
  DEFAULT_STATUS = Status::INTERNAL_SERVER_ERROR
12
12
 
13
- # Returns the HTTP status for the given exception
13
+ # Returns the HTTP status for the given exception.
14
14
  #
15
15
  # @param err [Exception] exception
16
16
  # @return [Integer, String] HTTP status
@@ -18,22 +18,30 @@ module Syntropy
18
18
  err.respond_to?(:http_status) ? err.http_status : DEFAULT_STATUS
19
19
  end
20
20
 
21
+ # Returns true if the error should be logged. Currently all errors are
22
+ # logged except for NOT FOUND errors.
23
+ #
24
+ # @param err [Exception] error
25
+ # @return [bool]
21
26
  def self.log_error?(err)
22
27
  http_status(err) != Status::NOT_FOUND
23
28
  end
24
29
 
25
- # Creates an error with status 404 Not Found
30
+ # Creates an error with status 404 Not Found.
26
31
  #
32
+ # @param msg [String] error message
27
33
  # @return [Syntropy::Error]
28
34
  def self.not_found(msg = 'Not found') = new(msg, Status::NOT_FOUND)
29
35
 
30
- # Creates an error with status 405 Method Not Allowed
36
+ # Creates an error with status 405 Method Not Allowed.
31
37
  #
38
+ # @param msg [String] error message
32
39
  # @return [Syntropy::Error]
33
40
  def self.method_not_allowed(msg = 'Method not allowed') = new(msg, Status::METHOD_NOT_ALLOWED)
34
41
 
35
- # Creates an error with status 418 I'm a teapot
42
+ # Creates an error with status 418 I'm a teapot.
36
43
  #
44
+ # @param msg [String] error message
37
45
  # @return [Syntropy::Error]
38
46
  def self.teapot(msg = 'I\'m a teapot') = new(msg, Status::TEAPOT)
39
47
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Syntropy
4
- VERSION = '0.27.8'
4
+ VERSION = '0.27.9'
5
5
  end
data/test/test_app.rb CHANGED
@@ -155,6 +155,16 @@ class AppTest < Minitest::Test
155
155
  assert_equal '<h1>Raised error</h1>', req.response_body
156
156
  assert_equal '43', req.ctx[:foo]
157
157
  end
158
+
159
+ def test_middleware_invocation_on_404
160
+ req = make_request(':method' => 'HEAD', ':path' => '/azerty?foo=bar')
161
+ assert_equal Status::NOT_FOUND, req.response_status
162
+ assert_nil req.ctx[:foo]
163
+
164
+ req = make_request(':method' => 'HEAD', ':path' => '/test/azerty?foo=bar')
165
+ assert_equal Status::NOT_FOUND, req.response_status
166
+ assert_equal 'bar', req.ctx[:foo]
167
+ end
158
168
  end
159
169
 
160
170
  class CustomAppTest < Minitest::Test
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syntropy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.27.8
4
+ version: 0.27.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner