lennarb 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2023, by Aristóteles Coutinho.
5
-
6
- # Internal dependencies
7
- #
8
- require 'lenna/middleware/default/error_handler'
9
- require 'lenna/middleware/default/logging'
10
- require 'lenna/router'
11
-
12
- # The Lenna module is used to namespace the framework.
13
- #
14
- # @public
15
- #
16
- module Lenna
17
- # The base class is used to start the server.
18
- #
19
- # @public
20
- #
21
- class Application < Router
22
- # Initialize the base class
23
- #
24
- # @yield { ... } the block to be evaluated in the context of the instance.
25
- #
26
- # @return [void | Application] Returns the instance if a block is given.
27
- #
28
- def initialize
29
- super
30
- yield self if block_given?
31
- end
32
- end
33
-
34
- # The base module is used to include the base class.
35
- #
36
- # @public
37
- #
38
- module Base
39
- def self.included(base)
40
- base.extend(ClassMethods)
41
- end
42
-
43
- module ClassMethods
44
- # Initialize the base module
45
- #
46
- # @return [Lenna::Application] Returns the instance.
47
- #
48
- # @public
49
- #
50
- def app = @app ||= Lenna::Application.new
51
- end
52
- end
53
- end
@@ -1,118 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2023, by Aristóteles Coutinho.
5
-
6
- require 'singleton'
7
-
8
- module Lenna
9
- module Middleware
10
- # The MiddlewareManager class is responsible for managing the middlewares.
11
- #
12
- # @attr global_middlewares [Array] the global middlewares
13
- # @attr middleware_chains_cache [Hash] the middleware chains cache
14
- #
15
- # Middleware chains are cached by action.
16
- # The middlewares that are added to a specific route are added to the
17
- # global middlewares.
18
- #
19
- # @private Since `v0.1.0`
20
- #
21
- class App
22
- include Singleton
23
-
24
- # @return [Array] the global middlewares.
25
- #
26
- attr_accessor :global_middlewares
27
- # @return [Hash] the middleware chains cache.
28
- #
29
- attr_accessor :middleware_chains_cache
30
-
31
- # This method will initialize the global middlewares and the
32
- # middleware chains cache.
33
- #
34
- # @return [void]
35
- #
36
- def initialize
37
- @global_middlewares = []
38
- @middleware_chains_cache = {}
39
- end
40
-
41
- # This method is used to reset the global middlewares and the middleware
42
- # chains cache.
43
- #
44
- # @return [void]
45
- #
46
- def reset!
47
- @global_middlewares = []
48
- @middleware_chains_cache = {}
49
- end
50
-
51
- # This method is used to add a middleware to the global middlewares.
52
- # @parameter middlewares [Array] the middlewares to be used
53
- # @return [void]
54
- #
55
- def use(middlewares)
56
- @global_middlewares += Array(middlewares)
57
- @middleware_chains_cache = {}
58
- end
59
-
60
- # This method is used to fetch or build the middleware chain for the given
61
- # action and route middlewares.
62
- #
63
- # @parameter action [Proc] the action to be executed
64
- # @parameter route_middlewares [Array] the middlewares to be used
65
- # @return [Proc] the middleware chain
66
- #
67
- #
68
- def fetch_or_build_middleware_chain(
69
- action,
70
- route_middlewares,
71
- http_method: nil,
72
- path: nil
73
- )
74
- signature =
75
- if http_method && path
76
- [http_method, path, route_middlewares].hash.to_s
77
- else
78
- ['global', route_middlewares].hash.to_s
79
- end
80
-
81
- @middleware_chains_cache[signature] ||=
82
- build_middleware_chain(action, route_middlewares)
83
- end
84
-
85
- # This method is used to build the middleware chain for the given action
86
- # and middlewares.
87
- #
88
- # @parameter action [Proc] the action to be executed
89
- # @parameter middlewares [Array] the middlewares to be used
90
- # @return [Proc] the middleware chain
91
- #
92
- # ex.
93
- # Given the action:
94
- # `->(req, res) { res << 'Hello' }` and the
95
- # middlewares [mw1, mw2], the middleware
96
- # chain will be:
97
- # mw1 -> mw2 -> action
98
- # The action will be the last middleware in the
99
- # chain.
100
- #
101
- def build_middleware_chain(action, middlewares)
102
- all_middlewares = (@global_middlewares + Array(middlewares))
103
-
104
- all_middlewares.reverse.reduce(action) do |next_middleware, middleware|
105
- ->(req, res) {
106
- middleware.call(
107
- req,
108
- res,
109
- -> {
110
- next_middleware.call(req, res)
111
- }
112
- )
113
- }
114
- end
115
- end
116
- end
117
- end
118
- end
@@ -1,243 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2023, by Aristóteles Coutinho.
5
-
6
- require 'cgi'
7
-
8
- module Lenna
9
- module Middleware
10
- module Default
11
- # This middleware will handle errors.
12
- #
13
- # @private Since `v0.1.0`
14
- #
15
- module ErrorHandler
16
- extend self
17
-
18
- # This method will be called by the server.
19
- #
20
- # @parameter req [Rack::Request] The request object
21
- # @parameter res [Rack::Response] The response object
22
- # @parameter next_middleware [Proc] The next middleware in the stack
23
- #
24
- # @return [void]
25
- #
26
- def call(req, res, next_middleware)
27
- next_middleware.call
28
- rescue StandardError => e
29
- env = req.env
30
- log_error(env, e)
31
-
32
- render_error_page(e, req, res)
33
- end
34
-
35
- private
36
-
37
- # This method will render the error page.
38
- #
39
- # @parameter error [StandardError] The error object
40
- # @parameter env [Hash] The environment variables
41
- # @parameter res [Rack::Response] The response object
42
- #
43
- # @return [void]
44
- #
45
- def render_error_page(error, req, res)
46
- case req.headers['Content-Type']
47
- in 'application/json' then render_json(error, res)
48
- else render_html(error, res)
49
- end
50
- end
51
-
52
- # This method will render the JSON error page.
53
- #
54
- # @parameter error [StandardError] The error object
55
- # @parameter env [Hash] The environment variables
56
- # @parameter res [Rack::Response] The response object
57
- #
58
- # @return [void]
59
- #
60
- def render_json(error, res)
61
- case ENV.fetch('RACK_ENV', 'development')
62
- in 'development' | 'test' then res.json(
63
- data: {
64
- error: error.message,
65
- status: 500
66
- }
67
- )
68
- else res.json(data: { error: 'Internal Server Error', status: 500 })
69
- end
70
- end
71
-
72
- # This method will render the HTML error page.
73
- #
74
- # @parameter error [StandardError] The error object
75
- # @parameter env [Hash] The environment variables
76
- # @parameter res [Rack::Response] The response object
77
- #
78
- # @return [void]
79
- #
80
- def render_html(error, res)
81
- res.html(error_page(error), status: 500)
82
- end
83
-
84
- # This method will log the error.
85
- #
86
- # @parameter env [Hash] The environment variables
87
- # @parameter error [StandardError] The error object
88
- #
89
- # @return [void]
90
- #
91
- def log_error(env, error)
92
- env['rack.errors'].puts error.message
93
- env['rack.errors'].puts error.backtrace.join("\n")
94
- env['rack.errors'].flush
95
- end
96
-
97
- # This method will render the error page.
98
- #
99
- def error_page(error)
100
- style = <<-STYLE
101
- <style>
102
- body { font-family: 'Helvetica Neue', sans-serif; background-color: #F7F7F7; color: #333; margin: 0; padding: 0; }
103
- .error-container { max-width: 600px; margin: 20px auto; padding: 20px; box-shadow: 0 4px 8px 0 rgba(0,0,0,0.1); background: white; border-radius: 4px; }
104
- h1 { color: #c0392b }
105
- h2 { padding: 0; margin: 0; font-size: 1.2em; }
106
- .error-details, .backtrace { text-align: left; }
107
- .error-details strong { color: #e74c3c; }
108
- pre { background: #ecf0f1; padding: 15px; overflow: auto; border-left: 5px solid #e74c3c; font-size: 0.9em; }
109
- .backtrace { margin-top: 20px; }
110
- .backtrace pre { border-color: #3498db; }
111
- svg { fill: #e74c3c; width: 50px; height: auto; }
112
- .container { display: flex; justify-content: space-between; align-items: center; align-content: center;}
113
- </style>
114
- STYLE
115
-
116
- # SVG logo
117
- svg_logo = <<~SVG
118
- <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="100" height="100" viewBox="0 0 100 100">
119
- <path fill="#f1bc19" d="M77 12A1 1 0 1 0 77 14A1 1 0 1 0 77 12Z"></path><path fill="#e4e4f9" d="M50 13A37 37 0 1 0 50 87A37 37 0 1 0 50 13Z"></path><path fill="#f1bc19" d="M83 11A4 4 0 1 0 83 19A4 4 0 1 0 83 11Z"></path><path fill="#8889b9" d="M87 22A2 2 0 1 0 87 26A2 2 0 1 0 87 22Z"></path><path fill="#fbcd59" d="M81 74A2 2 0 1 0 81 78 2 2 0 1 0 81 74zM15 59A4 4 0 1 0 15 67 4 4 0 1 0 15 59z"></path><path fill="#8889b9" d="M25 85A2 2 0 1 0 25 89A2 2 0 1 0 25 85Z"></path><path fill="#fff" d="M18.5 49A2.5 2.5 0 1 0 18.5 54 2.5 2.5 0 1 0 18.5 49zM79.5 32A1.5 1.5 0 1 0 79.5 35 1.5 1.5 0 1 0 79.5 32z"></path><g><path fill="#fdfcee" d="M50 25.599999999999998A24.3 24.3 0 1 0 50 74.2A24.3 24.3 0 1 0 50 25.599999999999998Z"></path><path fill="#472b29" d="M50,74.8c-13.8,0-25-11.2-25-25c0-13.8,11.2-25,25-25c13.8,0,25,11.2,25,25C75,63.6,63.8,74.8,50,74.8z M50,26.3c-13,0-23.5,10.6-23.5,23.5S37,73.4,50,73.4s23.5-10.6,23.5-23.5S63,26.3,50,26.3z"></path></g><g><path fill="#ea5167" d="M49.9 29.6A20.4 20.4 0 1 0 49.9 70.4A20.4 20.4 0 1 0 49.9 29.6Z"></path></g><g><path fill="#ef7d99" d="M50.2,32.9c10.6,0,19.3,8.2,20.1,18.5c0-0.5,0.1-1,0.1-1.5c0-11-9-20-20.2-20c-11.1,0-20.2,9-20.2,20 c0,0.5,0,1,0.1,1.5C30.9,41,39.5,32.9,50.2,32.9z"></path></g><g><path fill="#472b29" d="M69.4,44.6c-0.2,0-0.4-0.1-0.5-0.4c-0.1-0.3-0.2-0.6-0.3-0.9c-0.4-1.1-0.9-2.2-1.5-3.2 c-0.1-0.2-0.1-0.5,0.2-0.7c0.2-0.1,0.5-0.1,0.7,0.2c0.6,1.1,1.1,2.2,1.5,3.3c0.1,0.3,0.2,0.6,0.3,0.9c0.1,0.3-0.1,0.5-0.3,0.6 C69.5,44.6,69.5,44.6,69.4,44.6z"></path></g><g><path fill="#472b29" d="M50,70.8c-11.5,0-20.9-9.3-20.9-20.8c0-11.5,9.4-20.8,20.9-20.8c6,0,11.7,2.6,15.6,7c0.3,0.3,0.6,0.7,0.9,1 c0.2,0.2,0.1,0.5-0.1,0.7c-0.2,0.2-0.5,0.1-0.7-0.1c-0.3-0.3-0.5-0.7-0.8-1c-3.8-4.2-9.2-6.7-14.9-6.7c-11,0-19.9,8.9-19.9,19.8 c0,10.9,8.9,19.8,19.9,19.8s19.9-8.9,19.9-19.8c0-1-0.1-2-0.2-3c0-0.3,0.1-0.5,0.4-0.6c0.3,0,0.5,0.1,0.6,0.4 c0.2,1,0.2,2.1,0.2,3.1C70.9,61.4,61.5,70.8,50,70.8z"></path></g><g><path fill="#fdfcee" d="M56,57.1c-0.3,0-0.6-0.1-0.9-0.4l-5.2-5.2l-5.2,5.2c-0.2,0.2-0.5,0.4-0.9,0.4s-0.6-0.1-0.9-0.4 c-0.5-0.5-0.5-1.2,0-1.7l5.2-5.2L43,44.6c-0.5-0.5-0.5-1.2,0-1.7c0.2-0.2,0.5-0.4,0.9-0.4s0.6,0.1,0.9,0.4l5.2,5.2l5.2-5.2 c0.2-0.2,0.5-0.4,0.9-0.4c0.3,0,0.6,0.1,0.9,0.4s0.4,0.5,0.4,0.9s-0.1,0.6-0.4,0.9l-5.2,5.2l5.2,5.2c0.2,0.2,0.4,0.5,0.4,0.9 c0,0.3-0.1,0.6-0.4,0.9S56.3,57.1,56,57.1z"></path><path fill="#472b29" d="M56,43.1c0.2,0,0.4,0.1,0.5,0.2c0.3,0.3,0.3,0.7,0,1l-5.5,5.5l5.5,5.5c0.3,0.3,0.3,0.7,0,1 c-0.1,0.1-0.3,0.2-0.5,0.2s-0.4-0.1-0.5-0.2l-5.5-5.5l-5.5,5.5c-0.1,0.1-0.3,0.2-0.5,0.2s-0.4-0.1-0.5-0.2c-0.3-0.3-0.3-0.7,0-1 l5.5-5.5l-5.5-5.5c-0.3-0.3-0.3-0.7,0-1c0.1-0.1,0.3-0.2,0.5-0.2s0.4,0.1,0.5,0.2l5.5,5.5l5.5-5.5C55.6,43.1,55.8,43.1,56,43.1 M56,42.1c-0.5,0-0.9,0.2-1.2,0.5l-4.8,4.8l-4.8-4.8c-0.3-0.3-0.8-0.5-1.2-0.5s-0.9,0.2-1.2,0.5c-0.3,0.3-0.5,0.8-0.5,1.2 s0.2,0.9,0.5,1.2l4.8,4.8l-4.8,4.8c-0.3,0.3-0.5,0.8-0.5,1.2s0.2,0.9,0.5,1.2c0.3,0.3,0.8,0.5,1.2,0.5s0.9-0.2,1.2-0.5l4.8-4.8 l4.8,4.8c0.3,0.3,0.8,0.5,1.2,0.5s0.9-0.2,1.2-0.5c0.3-0.3,0.5-0.8,0.5-1.2s-0.2-0.9-0.5-1.2l-4.8-4.8l4.8-4.8 c0.3-0.3,0.5-0.8,0.5-1.2s-0.2-0.9-0.5-1.2C56.9,42.2,56.4,42.1,56,42.1L56,42.1z"></path></g>
120
- </svg>
121
- #{' '}
122
- SVG
123
-
124
- # HTML page
125
- <<-HTML
126
- <!DOCTYPE html>
127
- <html lang="en">
128
- <head>
129
- <meta charset="UTF-8">
130
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
131
- #{' '}
132
- <title>System Error</title>
133
- #{style}
134
- </head>
135
- <body>
136
- <div class="error-container">
137
- <div class="container">
138
- <div class="item-left">
139
- <h1>Oops! An error has occurred.</h1>
140
- </div>
141
- <div class="item-right">#{svg_logo}</div>
142
- </div>
143
- #{' '}
144
- #{error_message_and_backtrace(error)}
145
- </div>
146
- </body>
147
- </html>
148
- HTML
149
- end
150
-
151
- # This method will render the error message and backtrace.
152
- #
153
- # @parameter error [StandardError] The error object
154
- # @parameter env [Hash] The environment variables
155
- #
156
- # @return [String] The HTML string
157
- #
158
- def error_message_and_backtrace(error)
159
- if ENV['RACK_ENV'] == 'development'
160
- truncated_message =
161
- error.message[0..500] + (error.message.length > 500 ? '...' : '')
162
-
163
- file, line = error.backtrace.first.split(':')
164
- line_number = Integer(line)
165
- <<-DETAILS
166
- <div class="error-details">
167
- <h2>Error Details:</h2>
168
- <p>
169
- <strong>Message:</strong> <span id="error-message">#{CGI.escapeHTML(truncated_message)}</span>
170
- </p>
171
- <div>
172
- <p><strong>Location:</strong> #{CGI.escapeHTML(file)}:#{line_number}</p>
173
- <pre>#{extract_source(file, line_number)}</pre>
174
- </div>
175
-
176
- <details>
177
- <summary>Details</summary>
178
- <p id="full-message" style="display: none;">
179
- <h3>Full Backtrace:</h3>
180
- <p><strong>Full Message:</strong> #{CGI.escapeHTML(error.message)}</p>
181
- <pre>#{error.backtrace.join("\n")}</pre>
182
- </p>
183
- </details>
184
- </div>
185
- DETAILS
186
- else
187
- "<p>We're sorry, but something went wrong. We've been notified " \
188
- 'about this issue and will take a look at it shortly.</p>'
189
- end
190
- end
191
-
192
- # This method will extract the source code.
193
- #
194
- # @parameter file [String] The file path
195
- # @parameter line_number [Integer] The line number
196
- #
197
- # @return [String] The HTML string
198
- #
199
- # ex.
200
- # extract_source('/path/to/file.rb', 10)
201
- # # => "<strong style='color: red;'> 7: </strong> =>
202
- # def foo\n<strong style='color: red;'> 8: </strong>
203
- # puts 'bar'\n<strong style='color: red;'> 9: </strong>
204
- # end\n<strong style='color: red;'> 10: </strong> foo\n<strong
205
- # style='color: red;'> 11: </strong> "
206
- #
207
- def extract_source(file, line_number)
208
- lines = ::File.readlines(file)
209
- start_line = [line_number - 3, 0].max
210
- end_line = [line_number + 3, lines.size].min
211
-
212
- line_ranger = lines[start_line...end_line]
213
-
214
- format_lines(line_ranger, line_number).join
215
- end
216
-
217
- # This method will format the lines.
218
- #
219
- # ex.
220
- # format_lines(line_ranger, line_number)
221
- # # => ["<strong style='color: red;'> 7: </strong> =>\n",
222
- # "<strong style='color: red;'> 8: </strong> puts 'bar'\n",
223
- # "<strong style='color: red;'> 9: </strong> end\n",
224
- # "<strong style='color: red;'> 10: </strong> foo\n",
225
- # "<strong style='color: red;'> 11: </strong> "]
226
- #
227
- def format_lines(lines, highlight_line)
228
- lines.map.with_index(highlight_line - 3 + 1) do |line, line_num|
229
- line_number_text = "#{line_num.to_s.rjust(6)}: "
230
- formatted_line = ::CGI.escapeHTML(line)
231
-
232
- if line_num == highlight_line
233
- "<strong style='color: red;'>#{line_number_text}</strong> " \
234
- "#{formatted_line}"
235
- else
236
- "#{line_number_text}#{formatted_line}"
237
- end
238
- end
239
- end
240
- end
241
- end
242
- end
243
- end
@@ -1,93 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2023, by Aristóteles Coutinho.
5
-
6
- require 'colorize'
7
-
8
- module Lenna
9
- module Middleware
10
- module Default
11
- # The Logging module is responsible for logging the requests.
12
- #
13
- # @private
14
- #
15
- module Logging
16
- extend self
17
-
18
- # This method is used to log the request.
19
- #
20
- # @parameter req [Rack::Request ] The request
21
- # @parameter res [Rack::Response] The response
22
- # @parameter next_middleware [Proc] The next middleware
23
- #
24
- def call(req, res, next_middleware)
25
- start_time = ::Time.now
26
- next_middleware.call
27
- end_time = ::Time.now
28
-
29
- http_method = colorize_http_method(req.request_method)
30
- status_code = colorize_status_code(res.status.to_s)
31
- duration = calculate_duration(start_time, end_time)
32
-
33
- log_message = "[#{start_time}] \"#{http_method} #{req.path_info}\" " \
34
- "#{status_code} #{format('%.2f', duration)}ms"
35
-
36
- ::Kernel.puts(log_message)
37
- end
38
-
39
- private
40
-
41
- # This method is used to colorize the request method.
42
- #
43
- # @parameter request_method [String] The request method
44
- #
45
- # @return [String] The colorized request method
46
- #
47
- # @private
48
- #
49
- def colorize_http_method(request_method)
50
- case request_method
51
- in 'GET' then 'GET'.green
52
- in 'POST' then 'POST'.magenta
53
- in 'PUT' then 'PUT'.yellow
54
- in 'DELETE' then 'DELETE'.red
55
- else request_method.blue
56
- end
57
- end
58
-
59
- # This method is used to colorize the status code.
60
- #
61
- # @param status_code [String] The status code
62
- #
63
- # @return [String] The colorized status code
64
- #
65
- # @private
66
- #
67
- def colorize_status_code(status_code)
68
- case status_code
69
- in '2' then status_code.green
70
- in '3' then status_code.blue
71
- in '4' then status_code.yellow
72
- in '5' then status_code.red
73
- else status_code
74
- end
75
- end
76
-
77
- # This method is used to calculate the duration.
78
- #
79
- # @param start_time [Time] The start time
80
- #
81
- # @param end_time [Time] The end time
82
- # @return [Float] The duration
83
- #
84
- # @api private
85
- #
86
- def calculate_duration(start_time, end_time)
87
- millis_in_second = 1000.0
88
- (end_time - start_time) * millis_in_second
89
- end
90
- end
91
- end
92
- end
93
- end
@@ -1,124 +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
- # The Route::Builder class is responsible for building the tree of routes.
9
- #
10
- # The tree of routes is built by adding routes to the tree. Each route is
11
- # represented by a node in the tree and each node has a path and an
12
- # endpoint. The path is the path of the route and the endpoint is then
13
- # action to be executed when the route is matched.
14
- #
15
- # Those nodes are stored in a cache to avoid rebuilding the tree of routes
16
- # for each request.
17
- #
18
- # The tree use `Trie` data structures to optimize the search for a route.
19
- # The trie is a tree where each node is a character of the path.
20
- # This way, the search for a route is O(n) where n is the length of the
21
- # path.
22
- #
23
- # @private
24
- #
25
- class Builder
26
- def initialize(root_node) = @root_node = root_node
27
-
28
- # This method will add a route to the tree of routes.
29
- #
30
- # @parameter method [String] the HTTP method
31
- # @parameter path [String] the path to be matched
32
- # @parameter action [Proc] the action to be executed
33
- # @parameter cache [Cache] the cache to be used
34
- #
35
- # @return [void]
36
- #
37
- def call(method, path, action, cache)
38
- path_key = cache.cache_key(method, path)
39
-
40
- return if cache.exist?(path_key)
41
-
42
- current_node = find_or_create_route_node(path)
43
- setup_endpoint(current_node, method, action)
44
-
45
- cache.add(path_key, current_node)
46
- end
47
-
48
- private
49
-
50
- # This method will create routes that are missing.
51
- # @parameter path [String] the path to be matched
52
- #
53
- # @return [Node] the node that matches the path
54
- #
55
- def find_or_create_route_node(path)
56
- current_node = @root_node
57
- split_path(path).each do |part|
58
- current_node = find_or_create_node(current_node, part)
59
- end
60
- current_node
61
- end
62
-
63
- # This method will create the nodes that are missing.
64
- #
65
- # @parameter current_node [Node] the current node
66
- # @parameter part [String] the part of the path
67
- #
68
- # @return [Node] the node that matches the part of the path
69
- #
70
- # This way, the tree of routes is built.
71
- # @example Given the part ':id' and the tree bellow:
72
- # root
73
- # └── users
74
- # └── :id
75
- # The method will return the node :id.
76
- # If the node :id does not exist, it will be created.
77
- # The tree will be:
78
- # root
79
- # └── users
80
- # └── :id
81
- #
82
- def find_or_create_node(current_node, part)
83
- if part.start_with?(':')
84
- # If it is a placeholder, then we just create or update
85
- # the placeholder node with the placeholder name.
86
- placeholder_name = part[1..].to_sym
87
- current_node.children[:placeholder] ||= Node.new(
88
- {},
89
- nil,
90
- placeholder_name
91
- )
92
- else
93
- current_node.children[part] ||= Node.new
94
- end
95
- current_node.children[part.start_with?(':') ? :placeholder : part]
96
- end
97
-
98
- # This method will setup the endpoint of the current node.
99
- #
100
- # @parameter current_node [Node] the current node
101
- # @parameter method [String] the HTTP method
102
- # @parameter action [Proc] the action to be executed
103
- #
104
- # @return [void]
105
- #
106
- def setup_endpoint(current_node, method, action)
107
- current_node.endpoint ||= {}
108
- current_node.endpoint[method] = action
109
- end
110
-
111
- # This method will split the path into parts.
112
- #
113
- # @parameter path [String] the path to be split
114
- #
115
- # @return [Array] the splitted path
116
- #
117
- # TODO: Move this to a separate file and require it here.
118
- # Maybe utils or something like that.
119
- # Use Rack::Utils.split_path_info instead.
120
- #
121
- def split_path(path) = path.split('/').reject(&:empty?)
122
- end
123
- end
124
- end
@@ -1,52 +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 used to cache the routes.
9
- #
10
- # @private
11
- #
12
- class Cache
13
- def initialize = @cache = {}
14
-
15
- # This method is used to generate a key for the cache.
16
- #
17
- # @parameter [String] method
18
- # @parameter [String] path
19
- #
20
- # @return [String]
21
- #
22
- def cache_key(method, path) = "#{method} #{path}"
23
-
24
- # This method is used to add a route to the cache.
25
- #
26
- # @parameter route_key [String] The key for the route.
27
- # @parameter node [Lenna::Route::Node] The node for the route.
28
- #
29
- # @return [Lenna::Route::Node]
30
- #
31
- def add(route_key, node) = @cache[route_key] = node
32
-
33
- # This method is used to get a route from the cache.
34
- #
35
- # @parameter route_key [String] The key for the route.
36
- #
37
- # @return [Lenna::Route::Node]
38
- #
39
- def get(route_key) = @cache[route_key]
40
-
41
- # This method is used to check if a route is in the cache.
42
- #
43
- # @api public
44
- #
45
- # @parameter route_key [String] The key for the route.
46
- #
47
- # @return [Boolean]
48
- #
49
- def exist?(route_key) = @cache.key?(route_key)
50
- end
51
- end
52
- end