lennarb 0.1.7 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,97 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2023, by Aristóteles Coutinho.
5
-
6
- require 'console'
7
-
8
- # This middleware is used to reload files in development mode.
9
- #
10
- module Lenna
11
- module Middleware
12
- module Default
13
- class Reload
14
- attr_accessor :directories, :files_mtime
15
-
16
- # Initializes a new instance of Middleware::Default::Reload.
17
- #
18
- # @parameter directories [Array] An array of directories to monitor.
19
- #
20
- # @return [Middleware::Default::Reload] A new instance of Middleware::Default::Reload.
21
- def initialize(directories = [])
22
- self.files_mtime = {}
23
- self.directories = directories
24
-
25
- monitor_directories(directories)
26
- end
27
-
28
- # Calls the middleware.
29
- #
30
- # @parameter req [Rack::Request] The request.
31
- # @parameter _res [Rack::Response] The response.
32
- # @parameter next_middleware [Proc] The next middleware.
33
- #
34
- # @return [void]
35
- #
36
- def call(_req, _res, next_middleware)
37
- reload_if_needed
38
-
39
- next_middleware.call
40
- rescue ::StandardError => error
41
- ::Console.error(self, error)
42
- end
43
-
44
- private
45
-
46
- # Reloads files if needed.
47
- #
48
- # @return [void]
49
- #
50
- def reload_if_needed
51
- modified_files = check_for_modified_files
52
-
53
- reload_files(modified_files) unless modified_files.empty?
54
- end
55
-
56
- # Monitors directories for changes.
57
- #
58
- # @parameter directories [Array] An array of directories to monitor.
59
- #
60
- # @return [void]
61
- #
62
- def monitor_directories(directories)
63
- directories.each do |directory|
64
- ::Dir.glob(directory).each { |file| files_mtime[file] = ::File.mtime(file) }
65
- end
66
- end
67
-
68
- # Checks for modified files.
69
- #
70
- # @return [Array] An array of modified files.
71
- #
72
- # @example
73
- # check_for_modified_files #=> ["/path/to/file.rb"]
74
- #
75
- def check_for_modified_files
76
- @files_mtime.select do |file, last_mtime|
77
- ::File.mtime(file) > last_mtime
78
- end.keys
79
- end
80
-
81
- # Reloads files.
82
- #
83
- # @parameter files [Array] An array of files(paths) to reload.
84
- #
85
- # @return [void]
86
- #
87
- def reload_files(files)
88
- files.each do |file|
89
- ::Console.debug("Reloading #{file}")
90
- ::Kernel.load file
91
- @files_mtime[file] = ::File.mtime(file)
92
- end
93
- end
94
- end
95
- end
96
- end
97
- 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
@@ -1,77 +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 manage the namespaces.
9
- #
10
- # @private `Since v0.1.0`
11
- #
12
- class NamespaceStack
13
- # @return [Array] The stack of namespaces
14
- #
15
- # @private
16
- #
17
- attr_reader :stack
18
-
19
- # The initialize method is used to initialize the stack of namespaces.
20
- #
21
- # @private
22
- #
23
- # @attibute stack [Array] The stack of namespaces
24
- #
25
- # @return [void]
26
- #
27
- def initialize = @stack = ['']
28
-
29
- # This method is used to push a prefix to the stack.
30
- #
31
- # @parameter prefix [String] The prefix to be pushed
32
- #
33
- # @return [void]
34
- #
35
- # ex.
36
- #
37
- # stack = NamespaceStack.new
38
- # stack.push('/users')
39
- # stack.current_prefix # => '/users'
40
- #
41
- # @see #resolve_prefix
42
- #
43
- def push(prefix)
44
- @stack.push(resolve_prefix(prefix))
45
- end
46
-
47
- # This method is used to remove the last prefix from the stack.
48
- #
49
- # @return [String] The popped prefix
50
- #
51
- def pop
52
- @stack.pop unless @stack.size == 1
53
- end
54
-
55
- # @return [String] The current prefix
56
- #
57
- def current_prefix = @stack.last
58
-
59
- # The to_s method is used to return the current prefix.
60
- #
61
- # @return [String] The current prefix
62
- #
63
- def to_s = current_prefix
64
-
65
- private
66
-
67
- # The resolve_prefix method is used to resolve the prefix.
68
- #
69
- # @parameter prefix [String] The prefix to be resolved
70
- # @return [String] The resolved prefix
71
- #
72
- def resolve_prefix(prefix)
73
- current_prefix + prefix
74
- end
75
- end
76
- end
77
- end