lennarb 1.3.0 → 1.4.1
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/.devcontainer/Dockerfile +8 -0
- data/.devcontainer/devcontainer.json +20 -0
- data/.editorconfig +9 -0
- data/.github/workflows/coverage.yaml +58 -0
- data/.github/workflows/documentation.yaml +47 -0
- data/.github/workflows/main.yaml +27 -0
- data/.github/workflows/test.yaml +50 -0
- data/.gitignore +12 -0
- data/.standard.yml +23 -0
- data/.tool-versions +1 -0
- data/LICENCE +24 -0
- data/Rakefile +12 -0
- data/benchmark/memory.png +0 -0
- data/benchmark/rps.png +0 -0
- data/benchmark/runtime_with_startup.png +0 -0
- data/bin/console +8 -0
- data/bin/release +15 -0
- data/bin/setup +8 -0
- data/changelog.md +78 -7
- data/exe/lenna +2 -3
- data/gems.rb +29 -0
- data/guides/getting-started/readme.md +215 -0
- data/guides/links.yaml +6 -0
- data/guides/performance/readme.md +120 -0
- data/guides/response/readme.md +83 -0
- data/lennarb.gemspec +47 -0
- data/lib/lennarb/app.rb +172 -0
- data/lib/lennarb/config.rb +34 -0
- data/lib/lennarb/constansts.rb +6 -0
- data/lib/lennarb/environment.rb +76 -0
- data/lib/lennarb/request.rb +202 -31
- data/lib/lennarb/request_handler.rb +31 -0
- data/lib/lennarb/response.rb +16 -19
- data/lib/lennarb/route_node.rb +50 -13
- data/lib/lennarb/routes.rb +71 -0
- data/lib/lennarb/version.rb +2 -9
- data/lib/lennarb.rb +17 -155
- data/logo/lennarb.png +0 -0
- data/readme.md +32 -13
- metadata +147 -60
- data/lib/lennarb/plugin.rb +0 -49
- data/lib/lennarb/plugins/hooks.rb +0 -117
- data/lib/lennarb/plugins/mount.rb +0 -66
@@ -0,0 +1,76 @@
|
|
1
|
+
module Lennarb
|
2
|
+
class Environment
|
3
|
+
NAMES = %i[development test production local]
|
4
|
+
|
5
|
+
# Returns the name of the environment.
|
6
|
+
# @parameter name [Symbol]
|
7
|
+
#
|
8
|
+
attr_reader :name
|
9
|
+
|
10
|
+
# Initialize the environment.
|
11
|
+
# @parameter name [String, Symbol] The name of the environment.
|
12
|
+
#
|
13
|
+
def initialize(name)
|
14
|
+
@name = name.to_sym
|
15
|
+
|
16
|
+
return if NAMES.include?(@name)
|
17
|
+
|
18
|
+
raise ArgumentError, "Invalid environment: #{@name.inspect}"
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns true if the environment is development.
|
22
|
+
#
|
23
|
+
def development? = name == :development
|
24
|
+
|
25
|
+
# Returns true if the environment is test.
|
26
|
+
#
|
27
|
+
def test? = name == :test
|
28
|
+
|
29
|
+
# Returns true if the environment is production.
|
30
|
+
#
|
31
|
+
def production? = name == :production
|
32
|
+
|
33
|
+
# Returns true if the environment is local (either `test` or `development`).
|
34
|
+
#
|
35
|
+
def local? = test? || development?
|
36
|
+
|
37
|
+
# Implements equality for the environment.
|
38
|
+
#
|
39
|
+
def ==(other) = name == other || name.to_s == other
|
40
|
+
alias_method :eql?, :==
|
41
|
+
alias_method :equal?, :==
|
42
|
+
alias_method :===, :==
|
43
|
+
|
44
|
+
# Returns the name of the environment as a symbol.
|
45
|
+
# @returns [Symbol]
|
46
|
+
#
|
47
|
+
def to_sym = name
|
48
|
+
|
49
|
+
# Returns the name of the environment as a string.
|
50
|
+
# @returns [String]
|
51
|
+
#
|
52
|
+
def to_s = name.to_s
|
53
|
+
|
54
|
+
# Returns the name of the environment as a string.
|
55
|
+
# @returns [String]
|
56
|
+
def inspect = to_s.inspect
|
57
|
+
|
58
|
+
# Yields a block if the environment is the same as the given environment.
|
59
|
+
# - To match all environments use `:any` or `:all`.
|
60
|
+
# - To match local environments use `:local`.
|
61
|
+
# @param envs [Array<Symbol>] The environment(s) to check.
|
62
|
+
#
|
63
|
+
# @example
|
64
|
+
# app.env.on(:development) do
|
65
|
+
# # Code to run in development
|
66
|
+
# end
|
67
|
+
def on(*envs)
|
68
|
+
matched = envs.include?(:any) ||
|
69
|
+
envs.include?(:all) ||
|
70
|
+
envs.include?(name) ||
|
71
|
+
(envs.include?(:local) && local?)
|
72
|
+
|
73
|
+
yield if matched
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/lennarb/request.rb
CHANGED
@@ -1,14 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2023-2024, by Aristóteles Coutinho.
|
5
|
-
|
6
|
-
class Lennarb
|
1
|
+
module Lennarb
|
7
2
|
class Request < Rack::Request
|
8
3
|
# The environment variables of the request
|
9
4
|
#
|
10
5
|
# @returns [Hash]
|
11
|
-
#
|
12
6
|
attr_reader :env
|
13
7
|
|
14
8
|
# Initialize the request object
|
@@ -20,64 +14,241 @@ class Lennarb
|
|
20
14
|
#
|
21
15
|
def initialize(env, route_params = {})
|
22
16
|
super(env)
|
23
|
-
@route_params = route_params
|
17
|
+
@route_params = route_params || {}
|
24
18
|
end
|
25
19
|
|
26
|
-
# Get the request
|
20
|
+
# Get the request parameters merged with route parameters
|
27
21
|
#
|
28
|
-
# @returns [
|
22
|
+
# @returns [Hash]
|
29
23
|
#
|
30
|
-
def params
|
24
|
+
def params
|
25
|
+
@params ||= super.merge(@route_params)&.transform_keys(&:to_sym)
|
26
|
+
end
|
31
27
|
|
32
|
-
# Get the request path
|
28
|
+
# Get the request path without query string
|
33
29
|
#
|
34
30
|
# @returns [String]
|
35
31
|
#
|
36
|
-
def path
|
32
|
+
def path
|
33
|
+
@path ||= super.split("?").first
|
34
|
+
end
|
37
35
|
|
38
36
|
# Read the body of the request
|
39
37
|
#
|
40
38
|
# @returns [String]
|
41
39
|
#
|
42
|
-
def body
|
40
|
+
def body
|
41
|
+
@body ||= super.read
|
42
|
+
end
|
43
43
|
|
44
44
|
# Get the query parameters
|
45
45
|
#
|
46
46
|
# @returns [Hash]
|
47
47
|
#
|
48
48
|
def query_params
|
49
|
-
@query_params ||= Rack::Utils.parse_nested_query(query_string).transform_keys(&:to_sym)
|
49
|
+
@query_params ||= Rack::Utils.parse_nested_query(query_string || "").transform_keys(&:to_sym)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Set a value in the environment
|
53
|
+
#
|
54
|
+
# @parameter [String] key
|
55
|
+
# @parameter [Object] value
|
56
|
+
# @returns [Object] the value
|
57
|
+
#
|
58
|
+
def []=(key, value)
|
59
|
+
env[key] = value
|
60
|
+
end
|
61
|
+
|
62
|
+
# Get a value from the environment
|
63
|
+
#
|
64
|
+
# @parameter [String] key
|
65
|
+
# @returns [Object]
|
66
|
+
#
|
67
|
+
def [](key)
|
68
|
+
env[key]
|
50
69
|
end
|
51
70
|
|
52
71
|
# Get the headers of the request
|
53
72
|
#
|
73
|
+
# @returns [Hash]
|
74
|
+
#
|
54
75
|
def headers
|
55
|
-
@headers ||= env.select { |key, _| key.start_with?(
|
76
|
+
@headers ||= env.select { |key, _| key.start_with?("HTTP_") }
|
77
|
+
end
|
78
|
+
|
79
|
+
# Get the client IP address
|
80
|
+
#
|
81
|
+
# @returns [String]
|
82
|
+
#
|
83
|
+
def ip
|
84
|
+
ip_address
|
56
85
|
end
|
57
86
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
def
|
63
|
-
|
64
|
-
|
65
|
-
def content_type = headers['HTTP_CONTENT_TYPE']
|
66
|
-
def xhr? = headers['HTTP_X_REQUESTED_WITH']&.casecmp('XMLHttpRequest')&.zero?
|
87
|
+
# Check if the request is secure (HTTPS)
|
88
|
+
#
|
89
|
+
# @returns [Boolean]
|
90
|
+
#
|
91
|
+
def secure?
|
92
|
+
scheme == "https"
|
93
|
+
end
|
67
94
|
|
68
|
-
|
69
|
-
|
95
|
+
# Shorthand methods for common headers
|
96
|
+
|
97
|
+
# Get the user agent
|
98
|
+
#
|
99
|
+
# @returns [String, nil]
|
100
|
+
#
|
101
|
+
def user_agent
|
102
|
+
env["HTTP_USER_AGENT"]
|
70
103
|
end
|
71
104
|
|
72
|
-
|
73
|
-
|
105
|
+
# Get the accept header
|
106
|
+
#
|
107
|
+
# @returns [String, nil]
|
108
|
+
#
|
109
|
+
def accept
|
110
|
+
env["HTTP_ACCEPT"]
|
111
|
+
end
|
112
|
+
|
113
|
+
# Get the referer header
|
114
|
+
#
|
115
|
+
# @returns [String, nil]
|
116
|
+
#
|
117
|
+
def referer
|
118
|
+
env["HTTP_REFERER"]
|
119
|
+
end
|
120
|
+
|
121
|
+
# Get the host header
|
122
|
+
#
|
123
|
+
# @returns [String, nil]
|
124
|
+
#
|
125
|
+
def host
|
126
|
+
env["HTTP_HOST"]
|
127
|
+
end
|
128
|
+
|
129
|
+
# Get the content length header
|
130
|
+
#
|
131
|
+
# @returns [String, nil]
|
132
|
+
#
|
133
|
+
def content_length
|
134
|
+
env["HTTP_CONTENT_LENGTH"]
|
135
|
+
end
|
136
|
+
|
137
|
+
# Get the content type header
|
138
|
+
#
|
139
|
+
# @returns [String, nil]
|
140
|
+
#
|
141
|
+
def content_type
|
142
|
+
env["HTTP_CONTENT_TYPE"]
|
143
|
+
end
|
144
|
+
|
145
|
+
# Check if the request is an XHR request
|
146
|
+
#
|
147
|
+
# @returns [Boolean]
|
148
|
+
#
|
149
|
+
def xhr?
|
150
|
+
env["HTTP_X_REQUESTED_WITH"]&.casecmp("XMLHttpRequest")&.zero? || false
|
151
|
+
end
|
152
|
+
|
153
|
+
# Check if the request is a JSON request
|
154
|
+
#
|
155
|
+
# @returns [Boolean]
|
156
|
+
#
|
157
|
+
def json?
|
158
|
+
content_type&.include?("application/json")
|
159
|
+
end
|
160
|
+
|
161
|
+
# Parse JSON body if content type is application/json
|
162
|
+
#
|
163
|
+
# @returns [Hash, nil]
|
164
|
+
#
|
165
|
+
def json_body
|
166
|
+
return nil unless json?
|
167
|
+
@json_body ||= begin
|
168
|
+
require "json"
|
169
|
+
JSON.parse(body, symbolize_names: true)
|
170
|
+
rescue JSON::ParserError
|
171
|
+
nil
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Check if the request is an AJAX request (alias for xhr?)
|
176
|
+
#
|
177
|
+
# @returns [Boolean]
|
178
|
+
#
|
179
|
+
def ajax?
|
180
|
+
xhr?
|
181
|
+
end
|
182
|
+
|
183
|
+
# Get the requested format (.html, .json, etc)
|
184
|
+
#
|
185
|
+
# @returns [Symbol, nil]
|
186
|
+
#
|
187
|
+
def format
|
188
|
+
path_info = env["PATH_INFO"]
|
189
|
+
return nil unless path_info.include?(".")
|
190
|
+
|
191
|
+
extension = File.extname(path_info).delete(".")
|
192
|
+
extension.empty? ? nil : extension.to_sym
|
193
|
+
end
|
194
|
+
|
195
|
+
# Check if the request is a GET request
|
196
|
+
#
|
197
|
+
# @returns [Boolean]
|
198
|
+
#
|
199
|
+
def get?
|
200
|
+
request_method == "GET"
|
201
|
+
end
|
202
|
+
|
203
|
+
# Check if the request is a POST request
|
204
|
+
#
|
205
|
+
# @returns [Boolean]
|
206
|
+
#
|
207
|
+
def post?
|
208
|
+
request_method == "POST"
|
209
|
+
end
|
210
|
+
|
211
|
+
# Check if the request is a PUT request
|
212
|
+
#
|
213
|
+
# @returns [Boolean]
|
214
|
+
#
|
215
|
+
def put?
|
216
|
+
request_method == "PUT"
|
217
|
+
end
|
218
|
+
|
219
|
+
# Check if the request is a DELETE request
|
220
|
+
#
|
221
|
+
# @returns [Boolean]
|
222
|
+
#
|
223
|
+
def delete?
|
224
|
+
request_method == "DELETE"
|
225
|
+
end
|
226
|
+
|
227
|
+
# Check if the request is a HEAD request
|
228
|
+
#
|
229
|
+
# @returns [Boolean]
|
230
|
+
#
|
231
|
+
def head?
|
232
|
+
request_method == "HEAD"
|
233
|
+
end
|
234
|
+
|
235
|
+
# Check if the request is a PATCH request
|
236
|
+
#
|
237
|
+
# @returns [Boolean]
|
238
|
+
#
|
239
|
+
def patch?
|
240
|
+
request_method == "PATCH"
|
74
241
|
end
|
75
242
|
|
76
243
|
private
|
77
244
|
|
245
|
+
# Get the client IP address
|
246
|
+
#
|
247
|
+
# @returns [String]
|
248
|
+
#
|
78
249
|
def ip_address
|
79
|
-
forwarded_for =
|
80
|
-
forwarded_for ? forwarded_for.split(
|
250
|
+
forwarded_for = env["HTTP_X_FORWARDED_FOR"]
|
251
|
+
forwarded_for ? forwarded_for.split(",").first.strip : env["REMOTE_ADDR"]
|
81
252
|
end
|
82
253
|
end
|
83
254
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Lennarb
|
2
|
+
class RequestHandler
|
3
|
+
Lennarb::Error = Class.new(StandardError)
|
4
|
+
|
5
|
+
attr_reader :app
|
6
|
+
|
7
|
+
def initialize(app)
|
8
|
+
@app = app
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
http_method = env[Rack::REQUEST_METHOD].to_sym
|
13
|
+
parts = env[Rack::PATH_INFO].split("/").reject(&:empty?)
|
14
|
+
block, params = app.routes.match_route(parts, http_method)
|
15
|
+
|
16
|
+
unless block
|
17
|
+
return [404, {"content-type" => CONTENT_TYPE[:TEXT]}, ["Not Found"]]
|
18
|
+
end
|
19
|
+
|
20
|
+
req = Request.new(env, params)
|
21
|
+
res = Response.new
|
22
|
+
|
23
|
+
catch(:halt) do
|
24
|
+
block.call(req, res)
|
25
|
+
res.finish
|
26
|
+
rescue Lennarb::Error => error
|
27
|
+
[500, {"content-type" => CONTENT_TYPE[:TEXT]}, ["Internal Server Error (#{error.message})"]]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/lennarb/response.rb
CHANGED
@@ -1,9 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2023-2024, by Aristóteles Coutinho.
|
5
|
-
|
6
|
-
class Lennarb
|
1
|
+
module Lennarb
|
7
2
|
class Response
|
8
3
|
# @!attribute [rw] status
|
9
4
|
# @returns [Integer]
|
@@ -27,27 +22,24 @@ class Lennarb
|
|
27
22
|
|
28
23
|
# Constants
|
29
24
|
#
|
30
|
-
LOCATION =
|
25
|
+
LOCATION = "location"
|
31
26
|
private_constant :LOCATION
|
32
27
|
|
33
|
-
CONTENT_TYPE =
|
28
|
+
CONTENT_TYPE = "content-type"
|
34
29
|
private_constant :CONTENT_TYPE
|
35
30
|
|
36
|
-
CONTENT_LENGTH =
|
31
|
+
CONTENT_LENGTH = "content-length"
|
37
32
|
private_constant :CONTENT_LENGTH
|
38
33
|
|
39
|
-
ContentType = { HTML: 'text/html', TEXT: 'text/plain', JSON: 'application/json' }.freeze
|
40
|
-
private_constant :ContentType
|
41
|
-
|
42
34
|
# Initialize the response object
|
43
35
|
#
|
44
36
|
# @returns [Response]
|
45
37
|
#
|
46
38
|
def initialize
|
47
|
-
@status
|
39
|
+
@status = 200
|
48
40
|
@headers = {}
|
49
|
-
@body
|
50
|
-
@length
|
41
|
+
@body = []
|
42
|
+
@length = 0
|
51
43
|
end
|
52
44
|
|
53
45
|
# Set the response header
|
@@ -91,7 +83,7 @@ class Lennarb
|
|
91
83
|
# @returns [String] str
|
92
84
|
#
|
93
85
|
def text(str)
|
94
|
-
@headers[CONTENT_TYPE] =
|
86
|
+
@headers[CONTENT_TYPE] = Lennarb::CONTENT_TYPE[:TEXT]
|
95
87
|
write(str)
|
96
88
|
end
|
97
89
|
|
@@ -102,7 +94,7 @@ class Lennarb
|
|
102
94
|
# @returns [String] str
|
103
95
|
#
|
104
96
|
def html(str)
|
105
|
-
@headers[CONTENT_TYPE] =
|
97
|
+
@headers[CONTENT_TYPE] = Lennarb::CONTENT_TYPE[:HTML]
|
106
98
|
write(str)
|
107
99
|
end
|
108
100
|
|
@@ -113,8 +105,13 @@ class Lennarb
|
|
113
105
|
# @returns [String] str
|
114
106
|
#
|
115
107
|
def json(str)
|
116
|
-
|
117
|
-
|
108
|
+
json_str = JSON.generate(str)
|
109
|
+
@headers[CONTENT_TYPE] = Lennarb::CONTENT_TYPE[:JSON]
|
110
|
+
write(json_str)
|
111
|
+
rescue JSON::GeneratorError => e
|
112
|
+
@status = 500
|
113
|
+
@headers[CONTENT_TYPE] = Lennarb::CONTENT_TYPE[:TEXT]
|
114
|
+
write("JSON generation error: #{e.message}")
|
118
115
|
end
|
119
116
|
|
120
117
|
# Redirect the response
|
data/lib/lennarb/route_node.rb
CHANGED
@@ -1,24 +1,28 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2023-2024, by Aristóteles Coutinho.
|
5
|
-
|
6
|
-
class Lennarb
|
1
|
+
module Lennarb
|
7
2
|
class RouteNode
|
3
|
+
DuplicateRouteError = Class.new(StandardError)
|
8
4
|
attr_accessor :static_children, :dynamic_children, :blocks, :param_key
|
9
5
|
|
10
6
|
def initialize
|
11
|
-
@blocks
|
12
|
-
@param_key
|
13
|
-
@static_children
|
7
|
+
@blocks = {}
|
8
|
+
@param_key = nil
|
9
|
+
@static_children = {}
|
14
10
|
@dynamic_children = {}
|
15
11
|
end
|
16
12
|
|
13
|
+
# Add a route to the route node.
|
14
|
+
#
|
15
|
+
# @param parts [Array<String>] The parts of the route.
|
16
|
+
# @param http_method [String] The HTTP method.
|
17
|
+
# @param block [Proc] The block to be executed when the route is matched.
|
18
|
+
#
|
19
|
+
# @returns [void]
|
20
|
+
#
|
17
21
|
def add_route(parts, http_method, block)
|
18
22
|
current_node = self
|
19
23
|
|
20
24
|
parts.each do |part|
|
21
|
-
if part.start_with?(
|
25
|
+
if part.start_with?(":")
|
22
26
|
param_sym = part[1..].to_sym
|
23
27
|
current_node.dynamic_children[param_sym] ||= RouteNode.new
|
24
28
|
dynamic_node = current_node.dynamic_children[param_sym]
|
@@ -33,6 +37,14 @@ class Lennarb
|
|
33
37
|
current_node.blocks[http_method] = block
|
34
38
|
end
|
35
39
|
|
40
|
+
# Match a route.
|
41
|
+
#
|
42
|
+
# @param parts [Array<String>] The parts of the route.
|
43
|
+
# @param http_method [String] The HTTP method.
|
44
|
+
# @param params [Hash] The parameters of the route.
|
45
|
+
#
|
46
|
+
# @returns [Array<Proc, Hash>]
|
47
|
+
#
|
36
48
|
def match_route(parts, http_method, params: {})
|
37
49
|
if parts.empty?
|
38
50
|
return [blocks[http_method], params] if blocks[http_method]
|
@@ -57,10 +69,35 @@ class Lennarb
|
|
57
69
|
[nil, nil]
|
58
70
|
end
|
59
71
|
|
72
|
+
# Merge another route node into this one.
|
73
|
+
#
|
74
|
+
# @param other [RouteNode] The other route node.
|
75
|
+
#
|
76
|
+
# @returns [void|DuplicateRouteError]
|
77
|
+
#
|
60
78
|
def merge!(other)
|
61
|
-
|
62
|
-
|
63
|
-
|
79
|
+
other.blocks.each do |http_method, block|
|
80
|
+
if @blocks[http_method]
|
81
|
+
raise DuplicateRouteError, "Duplicate route for HTTP method: #{http_method}"
|
82
|
+
end
|
83
|
+
@blocks[http_method] = block
|
84
|
+
end
|
85
|
+
|
86
|
+
other.static_children.each do |path, node|
|
87
|
+
if @static_children[path]
|
88
|
+
@static_children[path].merge!(node)
|
89
|
+
else
|
90
|
+
@static_children[path] = node
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
other.dynamic_children.each do |param, node|
|
95
|
+
if @dynamic_children[param]
|
96
|
+
@dynamic_children[param].merge!(node)
|
97
|
+
else
|
98
|
+
@dynamic_children[param] = node
|
99
|
+
end
|
100
|
+
end
|
64
101
|
end
|
65
102
|
end
|
66
103
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Lennarb
|
2
|
+
class Routes
|
3
|
+
attr_reader :store
|
4
|
+
# RouteNode is a trie data structure that stores routes.
|
5
|
+
# see {Lennarb::RouteNode} for more details.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# node = RouteNode.new
|
9
|
+
# node.add_route(["foo", "bar"], :GET, -> {})
|
10
|
+
#
|
11
|
+
def initialize(&)
|
12
|
+
@store = RouteNode.new
|
13
|
+
instance_eval(&) if block_given?
|
14
|
+
end
|
15
|
+
|
16
|
+
# Define the HTTP methods.
|
17
|
+
#
|
18
|
+
# get, post, put, delete, patch, options, head
|
19
|
+
#
|
20
|
+
HTTP_METHODS.each do |http_method|
|
21
|
+
define_method(http_method.downcase) do |path, &block|
|
22
|
+
register_route(http_method, path, &block)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Define the root route.
|
27
|
+
#
|
28
|
+
# @param [String] path
|
29
|
+
#
|
30
|
+
# @param [Proc] block
|
31
|
+
#
|
32
|
+
# @returns [void]
|
33
|
+
#
|
34
|
+
def root(&block) = register_route(:GET, "/", &block)
|
35
|
+
|
36
|
+
# Match the route.
|
37
|
+
#
|
38
|
+
# @param [Array<String>] parts
|
39
|
+
#
|
40
|
+
# @param [Symbol] http_method
|
41
|
+
#
|
42
|
+
def match_route(...) = @store.match_route(...)
|
43
|
+
|
44
|
+
# Freeze store object.
|
45
|
+
#
|
46
|
+
# @returns [void]
|
47
|
+
#
|
48
|
+
def freeze = @store.freeze
|
49
|
+
|
50
|
+
private def register_route(http_method, path, &block)
|
51
|
+
parts = path.split("/").reject(&:empty?)
|
52
|
+
@store.add_route(parts, http_method, block)
|
53
|
+
end
|
54
|
+
|
55
|
+
module Mixin
|
56
|
+
extend self
|
57
|
+
|
58
|
+
def routes(&block)
|
59
|
+
@routes ||= Routes.new(&block)
|
60
|
+
end
|
61
|
+
|
62
|
+
HTTP_METHODS.each do |http_method|
|
63
|
+
define_method(http_method.downcase) do |path, &block|
|
64
|
+
routes.send(http_method.downcase, path, &block)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def root(&) = routes.root(&)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/lennarb/version.rb
CHANGED