lennarb 0.1.4 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,97 +1,103 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Released under the MIT License.
4
+ # Copyright, 2023, by Aristóteles Coutinho.
5
+
3
6
  require 'cgi'
4
7
 
5
8
  module Lenna
6
- module Middleware
7
- module Default
8
- # This middleware will handle errors.
9
- module ErrorHandler
10
- extend self
11
-
12
- # This method will be called by the server.
13
- #
14
- # @param req [Rack::Request] The request object
15
- # @param res [Rack::Response] The response object
16
- # @param next_middleware [Proc] The next middleware in the stack
17
- # @return [void]
18
- #
19
- # @api private
20
- #
21
- def call(req, res, next_middleware)
22
- next_middleware.call
23
- rescue StandardError => e
24
- env = req.env
25
- log_error(env, e)
26
-
27
- render_error_page(e, env, req, res)
28
- end
29
-
30
- private
31
-
32
- # This method will render the error page.
33
- #
34
- # @param error [StandardError] The error object
35
- # @param env [Hash] The environment variables
36
- # @param res [Rack::Response] The response object
37
- # @return [void]
38
- #
39
- # @api private
40
- def render_error_page(error, env, req, res)
41
- case req.headers['Content-Type']
42
- in 'application/json' then render_json(error, env, res)
43
- else render_html(error, env, res)
44
- end
45
- end
46
-
47
- # This method will render the JSON error page.
48
- #
49
- # @param error [StandardError] The error object
50
- # @param env [Hash] The environment variables
51
- # @param res [Rack::Response] The response object
52
- # @return [void]
53
- #
54
- # @api private
55
- def render_json(error, env, res)
56
- case env['RACK_ENV']
57
- in 'development' | 'test' then res.json(
58
- data: {
59
- error: error.message,
60
- status: 500
61
- }
62
- )
63
- else res.json(error: 'Internal Server Error', status: 500)
64
- end
65
- end
66
-
67
- # This method will render the HTML error page.
68
- #
69
- # @param error [StandardError] The error object
70
- # @param env [Hash] The environment variables
71
- # @param res [Rack::Response] The response object
72
- # @return [void]
73
- #
74
- # @api private
75
- def render_html(error, env, res)
76
- res.html(error_page(error, env), status: 500)
77
- end
78
-
79
- # This method will log the error.
80
- #
81
- # @param env [Hash] The environment variables
82
- # @param error [StandardError] The error object
83
- # @return [void]
84
- #
85
- # @api private
86
- def log_error(env, error)
87
- env['rack.errors'].puts error.message
88
- env['rack.errors'].puts error.backtrace.join("\n")
89
- env['rack.errors'].flush
90
- end
91
-
92
- # This method will render the error page.
93
- def error_page(error, env)
94
- style = <<-STYLE
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
95
101
  <style>
96
102
  body { font-family: 'Helvetica Neue', sans-serif; background-color: #F7F7F7; color: #333; margin: 0; padding: 0; }
97
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; }
@@ -105,18 +111,18 @@ module Lenna
105
111
  svg { fill: #e74c3c; width: 50px; height: auto; }
106
112
  .container { display: flex; justify-content: space-between; align-items: center; align-content: center;}
107
113
  </style>
108
- STYLE
109
-
110
- # SVG logo
111
- svg_logo = <<~SVG
112
- <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="100" height="100" viewBox="0 0 100 100">
113
- <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>
114
- </svg>
115
- #{' '}
116
- SVG
117
-
118
- # HTML page
119
- <<-HTML
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
120
126
  <!DOCTYPE html>
121
127
  <html lang="en">
122
128
  <head>
@@ -135,28 +141,28 @@ module Lenna
135
141
  <div class="item-right">#{svg_logo}</div>
136
142
  </div>
137
143
  #{' '}
138
- #{error_message_and_backtrace(error, env)}
144
+ #{error_message_and_backtrace(error)}
139
145
  </div>
140
146
  </body>
141
147
  </html>
142
- HTML
143
- end
144
-
145
- # This method will render the error message and backtrace.
146
- #
147
- # @param error [StandardError] The error object
148
- # @param env [Hash] The environment variables
149
- # @return [String] The HTML string
150
- #
151
- # @api private
152
- def error_message_and_backtrace(error, env)
153
- if env['RACK_ENV'] == 'development'
154
- truncated_message =
155
- error.message[0..500] + (error.message.length > 500 ? '...' : '')
156
-
157
- file, line = error.backtrace.first.split(':')
158
- line_number = Integer(line)
159
- <<-DETAILS
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
160
166
  <div class="error-details">
161
167
  <h2>Error Details:</h2>
162
168
  <p>
@@ -176,63 +182,62 @@ module Lenna
176
182
  </p>
177
183
  </details>
178
184
  </div>
179
- DETAILS
180
- else
181
- "<p>We're sorry, but something went wrong. We've been notified " \
182
- 'about this issue and will take a look at it shortly.</p>'
183
- end
184
- end
185
-
186
- # This method will extract the source code.
187
- #
188
- # @param file [String] The file path
189
- # @param line_number [Integer] The line number
190
- # @return [String] The HTML string
191
- #
192
- # @api private
193
- #
194
- # @example:
195
- # extract_source('/path/to/file.rb', 10)
196
- # # => "<strong style='color: red;'> 7: </strong> =>
197
- # def foo\n<strong style='color: red;'> 8: </strong>
198
- # puts 'bar'\n<strong style='color: red;'> 9: </strong>
199
- # end\n<strong style='color: red;'> 10: </strong> foo\n<strong
200
- # style='color: red;'> 11: </strong> "
201
- def extract_source(file, line_number)
202
- lines = ::File.readlines(file)
203
- start_line = [line_number - 3, 0].max
204
- end_line = [line_number + 3, lines.size].min
205
-
206
- line_ranger = lines[start_line...end_line]
207
-
208
- format_lines(line_ranger, line_number).join
209
- end
210
-
211
- # This method will format the lines.
212
- #
213
- # @api private
214
- #
215
- # @example:
216
- # format_lines(line_ranger, line_number)
217
- # # => ["<strong style='color: red;'> 7: </strong> =>\n",
218
- # "<strong style='color: red;'> 8: </strong> puts 'bar'\n",
219
- # "<strong style='color: red;'> 9: </strong> end\n",
220
- # "<strong style='color: red;'> 10: </strong> foo\n",
221
- # "<strong style='color: red;'> 11: </strong> "]
222
- def format_lines(lines, highlight_line)
223
- lines.map.with_index(highlight_line - 3 + 1) do |line, line_num|
224
- line_number_text = "#{line_num.to_s.rjust(6)}: "
225
- formatted_line = ::CGI.escapeHTML(line)
226
-
227
- if line_num == highlight_line
228
- "<strong style='color: red;'>#{line_number_text}</strong> " \
229
- "#{formatted_line}"
230
- else
231
- "#{line_number_text}#{formatted_line}"
232
- end
233
- end
234
- end
235
- end
236
- end
237
- end
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
238
243
  end
@@ -1,95 +1,93 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Released under the MIT License.
4
+ # Copyright, 2023, by Aristóteles Coutinho.
5
+
3
6
  require 'colorize'
4
7
 
5
8
  module Lenna
6
- module Middleware
7
- module Default
8
- # The Logging module is responsible for logging the requests.
9
- #
10
- # @api private
11
- #
12
- # @example:
13
- # Logging.call(req, res, next_middleware)
14
- # # => [2021-01-01 00:00:00 +0000] "GET /" 200 0.00ms
15
- module Logging
16
- extend self
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
17
 
18
- # This method is used to log the request.
19
- #
20
- # @param req [Rack::Request ] The request
21
- # @param res [Rack::Response] The response
22
- # @param next_middleware [Proc] The next middleware
23
- def call(req, res, next_middleware)
24
- start_time = ::Time.now
25
- next_middleware.call
26
- end_time = ::Time.now
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
27
28
 
28
- http_method = colorize_http_method(req.request_method)
29
- status_code = colorize_status_code(res.status.to_s)
30
- duration = calculate_duration(start_time, end_time)
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)
31
32
 
32
- log_message = "[#{start_time}] \"#{http_method} #{req.path_info}\" " \
33
- "#{status_code} #{format('%.2f', duration)}ms"
33
+ log_message = "[#{start_time}] \"#{http_method} #{req.path_info}\" " \
34
+ "#{status_code} #{format('%.2f', duration)}ms"
34
35
 
35
- ::Kernel.puts(log_message)
36
- end
36
+ ::Kernel.puts(log_message)
37
+ end
37
38
 
38
- private
39
+ private
39
40
 
40
- # This method is used to colorize the request method.
41
- #
42
- # @param request_method [String] The request method
43
- # @return [String] The colorized request method
44
- #
45
- # @api private
46
- #
47
- # @example:
48
- # colorize_http_method('GET') # => 'GET'.green
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
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
58
 
59
- # This method is used to colorize the status code.
60
- #
61
- # @param status_code [String] The status code
62
- # @return [String] The colorized status code
63
- #
64
- # @api private
65
- #
66
- # @example:
67
- # colorize_status_code('200') # => '200'.green
68
- def colorize_status_code(status_code)
69
- case status_code[0]
70
- in '2' then status_code.green
71
- in '3' then status_code.blue
72
- in '4' then status_code.yellow
73
- in '5' then status_code.red
74
- else status_code
75
- end
76
- end
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
77
76
 
78
- # This method is used to calculate the duration.
79
- #
80
- # @param start_time [Time] The start time
81
- # @param end_time [Time] The end time
82
- # @return [Float] The duration
83
- #
84
- # @api private
85
- #
86
- # @example:
87
- # calculate_duration(Time.now, Time.now + 1) # => 1000
88
- def calculate_duration(start_time, end_time)
89
- millis_in_second = 1000.0
90
- (end_time - start_time) * millis_in_second
91
- end
92
- end
93
- end
94
- end
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
95
93
  end