lennarb 0.1.6 → 0.2.0
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 +4 -4
- data/changelog.md +76 -10
- data/exe/lenna +15 -0
- data/lib/lennarb/request.rb +37 -0
- data/lib/lennarb/response.rb +138 -0
- data/lib/lennarb/route_node.rb +73 -0
- data/lib/lennarb/version.rb +3 -3
- data/lib/lennarb.rb +87 -5
- data/license.md +2 -1
- data/readme.md +30 -13
- metadata +55 -20
- data/lib/lenna/application.rb +0 -53
- data/lib/lenna/middleware/app.rb +0 -118
- data/lib/lenna/middleware/default/error_handler.rb +0 -243
- data/lib/lenna/middleware/default/logging.rb +0 -93
- data/lib/lenna/router/builder.rb +0 -124
- data/lib/lenna/router/cache.rb +0 -52
- data/lib/lenna/router/namespace_stack.rb +0 -77
- data/lib/lenna/router/request.rb +0 -141
- data/lib/lenna/router/response.rb +0 -509
- data/lib/lenna/router/route_matcher.rb +0 -68
- data/lib/lenna/router.rb +0 -205
- data/lib/lennarb/array_extensions.rb +0 -31
@@ -1,68 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2023, by Aristóteles Coutinho.
|
5
|
-
|
6
|
-
module Lenna
|
7
|
-
class Router
|
8
|
-
# This class is responsible for matching the request path
|
9
|
-
# to an endpoint and executing the endpoint action.
|
10
|
-
#
|
11
|
-
# @private Since `v0.1.0`
|
12
|
-
#
|
13
|
-
# This will match the request path to an endpoint and execute
|
14
|
-
# the endpoint action.
|
15
|
-
#
|
16
|
-
class RouteMatcher
|
17
|
-
# @parameter root_node [Lenna::Node] The root node
|
18
|
-
#
|
19
|
-
def initialize(root_node) = @root_node = root_node
|
20
|
-
|
21
|
-
# This method will match the request path to an endpoint and execute
|
22
|
-
# the endpoint action.
|
23
|
-
#
|
24
|
-
# @parameter req [Lenna::Request] The request object
|
25
|
-
# @parameter res [Lenna::Response] The response object
|
26
|
-
# @return [Lenna::Response] The response object
|
27
|
-
#
|
28
|
-
def match_and_execute_route(req, res)
|
29
|
-
params = {}
|
30
|
-
path_parts = split_path(req.path_info)
|
31
|
-
endpoint = find_endpoint(@root_node, path_parts, params)
|
32
|
-
|
33
|
-
if endpoint && (action = endpoint[req.request_method])
|
34
|
-
req.assign_params(params)
|
35
|
-
action.call(req, res)
|
36
|
-
else
|
37
|
-
res.not_found
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def split_path(path) = path.split('/').reject(&:empty?)
|
44
|
-
|
45
|
-
# @parameter node [Lenna::Node] The node to search
|
46
|
-
# @parameter parts [Array] The path parts
|
47
|
-
# @parameter params [Hash] The params hash
|
48
|
-
#
|
49
|
-
# @return [Lenna::Node] The node that matches the path
|
50
|
-
#
|
51
|
-
# This method is recursive.
|
52
|
-
#
|
53
|
-
def find_endpoint(node, parts, params)
|
54
|
-
return node.endpoint if parts.empty?
|
55
|
-
|
56
|
-
part = parts.shift
|
57
|
-
child_node = node.children[part]
|
58
|
-
|
59
|
-
if child_node.nil? && (placeholder_node = node.children[:placeholder])
|
60
|
-
params[placeholder_node.placeholder_name] = part
|
61
|
-
child_node = placeholder_node
|
62
|
-
end
|
63
|
-
|
64
|
-
find_endpoint(child_node, parts, params) if child_node
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
data/lib/lenna/router.rb
DELETED
@@ -1,205 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2023, by Aristóteles Coutinho.
|
5
|
-
|
6
|
-
require 'erb'
|
7
|
-
require 'json'
|
8
|
-
|
9
|
-
# External dependencies
|
10
|
-
require 'rack'
|
11
|
-
|
12
|
-
# Internal dependencies
|
13
|
-
require 'lenna/middleware/app'
|
14
|
-
require 'lenna/middleware/default/error_handler'
|
15
|
-
require 'lenna/middleware/default/logging'
|
16
|
-
require 'lenna/router/builder'
|
17
|
-
require 'lenna/router/cache'
|
18
|
-
require 'lenna/router/namespace_stack'
|
19
|
-
require 'lenna/router/request'
|
20
|
-
require 'lenna/router/response'
|
21
|
-
require 'lenna/router/route_matcher'
|
22
|
-
|
23
|
-
module Lenna
|
24
|
-
# The Node struct is used to represent a node in the tree of routes.
|
25
|
-
#
|
26
|
-
# @attr children [Hash] the children of the node
|
27
|
-
# @attr endpoint [String] the endpoint of the node
|
28
|
-
# @attr placeholder_name [String] the name of the placeholder
|
29
|
-
# @attr placeholder [Bool] whether the node is a placeholder
|
30
|
-
#
|
31
|
-
Node =
|
32
|
-
::Struct.new(:children, :endpoint, :placeholder_name, :placeholder) do
|
33
|
-
def initialize(children = {}, endpoint = nil, placeholder_name = nil)
|
34
|
-
super(children, endpoint, placeholder_name, !placeholder_name.nil?)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
public_constant :Node
|
38
|
-
|
39
|
-
# The router class is responsible for adding routes and calling the
|
40
|
-
# middleware chain for each request.
|
41
|
-
#
|
42
|
-
# @private
|
43
|
-
#
|
44
|
-
class Router
|
45
|
-
# @return [Node] the root node of the tree of routes
|
46
|
-
#
|
47
|
-
attr_reader :root_node
|
48
|
-
|
49
|
-
# @return [Route::Cache] the cache of routes
|
50
|
-
#
|
51
|
-
attr_reader :cache
|
52
|
-
|
53
|
-
# @return [Route::NamespaceStack] the stack of namespaces
|
54
|
-
#
|
55
|
-
attr_reader :namespace_stack
|
56
|
-
|
57
|
-
# @return [MiddlewareManager] the middleware manager
|
58
|
-
#
|
59
|
-
attr_reader :middleware_manager
|
60
|
-
|
61
|
-
# @return [Route::Builder] the route builder
|
62
|
-
#
|
63
|
-
attr_reader :roter_builder
|
64
|
-
|
65
|
-
# This method is used to initialize the router.
|
66
|
-
# By default, it uses the App middleware and the Cache.
|
67
|
-
#
|
68
|
-
# @return [void]
|
69
|
-
#
|
70
|
-
def initialize(
|
71
|
-
middleware_manager: Middleware::App.instance,
|
72
|
-
cache: Cache.new
|
73
|
-
)
|
74
|
-
@cache = cache
|
75
|
-
@root_node = Node.new({}, nil)
|
76
|
-
@middleware_manager = middleware_manager
|
77
|
-
@namespace_stack = NamespaceStack.new
|
78
|
-
@roter_builder = Builder.new(@root_node)
|
79
|
-
end
|
80
|
-
|
81
|
-
# This method is used to add a namespace to the routes.
|
82
|
-
#
|
83
|
-
# @parameter prefix [String] the prefix to be used
|
84
|
-
# @parameter block [Proc] the block to be executed
|
85
|
-
#
|
86
|
-
# @return [void]
|
87
|
-
#
|
88
|
-
# @public `Since v0.1.0`
|
89
|
-
#
|
90
|
-
# see {Route::NamespaceStack#push}
|
91
|
-
#
|
92
|
-
# ex.
|
93
|
-
#
|
94
|
-
# namespace '/api' do |route|
|
95
|
-
# route.get '/users' do
|
96
|
-
# # ...
|
97
|
-
# end
|
98
|
-
# end
|
99
|
-
#
|
100
|
-
def namespace(prefix, &block)
|
101
|
-
@namespace_stack.push(prefix)
|
102
|
-
block.call(self)
|
103
|
-
@namespace_stack.pop
|
104
|
-
end
|
105
|
-
|
106
|
-
# This method is used to add a middleware to the middleware manager.
|
107
|
-
#
|
108
|
-
# See {MiddlewareManager#use}
|
109
|
-
#
|
110
|
-
# @public `Since v0.1.0`
|
111
|
-
#
|
112
|
-
#
|
113
|
-
# @parameter middlewares [Array] the middlewares to be used
|
114
|
-
#
|
115
|
-
# @return [void]
|
116
|
-
#
|
117
|
-
def use(*middlewares) = @middleware_manager.use(middlewares)
|
118
|
-
|
119
|
-
# Proxy methods to add routes
|
120
|
-
#
|
121
|
-
# @parameter path [String] the path to be matched
|
122
|
-
# @parameter * [Array] the middlewares to be used
|
123
|
-
# @yield { ... } the block to be executed
|
124
|
-
#
|
125
|
-
# @return [void]
|
126
|
-
#
|
127
|
-
# see {#add_route}
|
128
|
-
#
|
129
|
-
# @public `Since v0.1`
|
130
|
-
def get(path, *, &) = add_route(::Rack::GET, path, *, &)
|
131
|
-
def put(path, *, &) = add_route(::Rack::PUT, path, *, &)
|
132
|
-
def post(path, *, &) = add_route(::Rack::POST, path, *, &)
|
133
|
-
def delete(path, *, &) = add_route(::Rack::DELETE, path, *, &)
|
134
|
-
|
135
|
-
def call(env) = dup.call!(env)
|
136
|
-
|
137
|
-
# This method is used to call the middleware chain for each request.
|
138
|
-
# It also sets the RACK_ENV to development if it is not set.
|
139
|
-
#
|
140
|
-
# @parameter env [Hash] the Rack env
|
141
|
-
#
|
142
|
-
# @return [Array] the Lennarb::Response
|
143
|
-
#
|
144
|
-
def call!(env)
|
145
|
-
middleware_pipeline = @middleware_manager.fetch_or_build_middleware_chain(
|
146
|
-
method(:process_request),
|
147
|
-
[
|
148
|
-
Middleware::Default::Logging,
|
149
|
-
Middleware::Default::ErrorHandler
|
150
|
-
]
|
151
|
-
)
|
152
|
-
|
153
|
-
req = Request.new(env)
|
154
|
-
res = Response.new
|
155
|
-
|
156
|
-
middleware_pipeline.call(req, res)
|
157
|
-
|
158
|
-
res.finish
|
159
|
-
end
|
160
|
-
|
161
|
-
private
|
162
|
-
|
163
|
-
# This method is used to add a route to the tree of routes.
|
164
|
-
#
|
165
|
-
# @parameter http_method [Rack::Method] the http method to be used
|
166
|
-
# @parameter path [String] the path to be matched
|
167
|
-
# @parameter middlewares [Array] the middlewares to be used
|
168
|
-
# @parameter action [Proc] the action to be executed
|
169
|
-
#
|
170
|
-
# see {MiddlewareManager#build_middleware_chain}
|
171
|
-
# see {Route::Cache#add}
|
172
|
-
# see {Route::Builder#call}
|
173
|
-
# see {Route::NamespaceStack#current_prefix}
|
174
|
-
#
|
175
|
-
# @return [void]
|
176
|
-
#
|
177
|
-
def add_route(http_method, path, *middlewares, &action)
|
178
|
-
full_path = @namespace_stack.current_prefix + path
|
179
|
-
|
180
|
-
middleware_chain = @middleware_manager.fetch_or_build_middleware_chain(
|
181
|
-
action,
|
182
|
-
middlewares,
|
183
|
-
http_method:,
|
184
|
-
path:
|
185
|
-
)
|
186
|
-
|
187
|
-
@roter_builder.call(http_method, full_path, middleware_chain, @cache)
|
188
|
-
end
|
189
|
-
|
190
|
-
# This method is used to process the request.
|
191
|
-
#
|
192
|
-
# It uses the Route::Matcher to match the request to a route and
|
193
|
-
# execute the action.
|
194
|
-
#
|
195
|
-
# @parameter req [Request] the request
|
196
|
-
# @parameter res [Response] the response
|
197
|
-
#
|
198
|
-
# @return [void]
|
199
|
-
#
|
200
|
-
def process_request(req, res)
|
201
|
-
@route_matcher ||= RouteMatcher.new(@root_node)
|
202
|
-
@route_matcher.match_and_execute_route(req, res)
|
203
|
-
end
|
204
|
-
end
|
205
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2023, by Aristóteles Coutinho.
|
5
|
-
|
6
|
-
module Lennarb
|
7
|
-
# The ArrayExtensions module is used to add the wrap method to the Array
|
8
|
-
# class.
|
9
|
-
#
|
10
|
-
# @public Since `v0.1.0`
|
11
|
-
#
|
12
|
-
module ArrayExtensions
|
13
|
-
# Wraps the object in an array if it is not already an array.
|
14
|
-
#
|
15
|
-
# @param object [Object] the object to wrap
|
16
|
-
#
|
17
|
-
# @return [Array] the wrapped object
|
18
|
-
#
|
19
|
-
def wrap(object)
|
20
|
-
if object.nil?
|
21
|
-
[]
|
22
|
-
elsif object.respond_to?(:to_ary)
|
23
|
-
object.to_ary || [object]
|
24
|
-
else
|
25
|
-
[object]
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
Array.extend(Lennarb::ArrayExtensions)
|