lennarb 0.1.5 → 0.1.7

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.
@@ -1,103 +1,118 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Released under the MIT License.
4
+ # Copyright, 2023, by Aristóteles Coutinho.
5
+
6
+ require 'singleton'
7
+
3
8
  module Lenna
4
- module Middleware
5
- # The MiddlewareManager class is responsible for managing the middlewares.
6
- #
7
- # @attr mutex [Mutex] the mutex used to synchronize the
8
- # access to the global middlewares and
9
- # the middleware chains cache.
10
- # @attr global_middlewares [Array] the global middlewares
11
- # @attr middleware_chains_cache [Hash] the middleware chains cache
12
- #
13
- # @note Middleware chains are cached by action.
14
- # The middlewares that are added to a specific route are added to the
15
- # global middlewares.
16
- #
17
- # @api private
18
- #
19
- # @since 0.1.0
20
- class App
21
- # @return [Mutex] the mutex used to synchronize the access to the global
22
- attr_reader :global_middlewares
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
23
30
 
24
- # @return [Hash] the middleware chains cache
25
- attr_reader :middleware_chains_cache
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
26
40
 
27
- # This method will initialize the global middlewares and the
28
- # middleware chains cache.
29
- #
30
- # @return [void]
31
- #
32
- # @since 0.1.0
33
- def initialize
34
- @mutex = ::Mutex.new
35
- @global_middlewares = []
36
- @middleware_chains_cache = {}
37
- end
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
38
50
 
39
- # This method is used to add a middleware to the global middlewares.
40
- # @param middlewares [Array] the middlewares to be used
41
- # @return [void]
42
- #
43
- # @since 0.1.0
44
- def use(middlewares)
45
- @mutex.synchronize do
46
- @global_middlewares += Array(middlewares)
47
- @middleware_chains_cache = {}
48
- end
49
- end
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
50
59
 
51
- # This method is used to fetch or build the middleware chain for the given
52
- # action and route middlewares.
53
- #
54
- # @param action [Proc] the action to be executed
55
- # @param route_middlewares [Array] the middlewares to be used
56
- # @return [Proc] the middleware chain
57
- #
58
- # @see #build_middleware_chain
59
- #
60
- # @since 0.1.0
61
- def fetch_or_build_middleware_chain(action, route_middlewares)
62
- middleware_signature = action.object_id.to_s
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
63
80
 
64
- @mutex.synchronize do
65
- @middleware_chains_cache[middleware_signature] ||=
66
- build_middleware_chain(action, route_middlewares)
67
- end
68
- end
81
+ @middleware_chains_cache[signature] ||=
82
+ build_middleware_chain(action, route_middlewares)
83
+ end
69
84
 
70
- # This method is used to build the middleware chain for the given action
71
- # and middlewares.
72
- #
73
- # @param action [Proc] the action to be executed
74
- # @param middlewares [Array] the middlewares to be used
75
- # @return [Proc] the middleware chain
76
- #
77
- # @since 0.1.0
78
- #
79
- # @example Given the action:
80
- # `->(req, res) { res << 'Hello' }` and the
81
- # middlewares [mw1, mw2], the middleware
82
- # chain will be:
83
- # mw1 -> mw2 -> action
84
- # The action will be the last middleware in the
85
- # chain.
86
- def build_middleware_chain(action, middlewares)
87
- all_middlewares = @global_middlewares + Array(middlewares)
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))
88
103
 
89
- all_middlewares.reverse.reduce(action) do |next_middleware, middleware|
90
- ->(req, res) {
91
- middleware.call(
92
- req,
93
- res,
94
- -> {
95
- next_middleware.call(req, res)
96
- }
97
- )
98
- }
99
- end
100
- end
101
- end
102
- end
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
103
118
  end
@@ -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