lennarb 0.1.5 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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