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.
- checksums.yaml +4 -4
- data/changelog.md +210 -0
- data/exe/lenna +17 -0
- data/lib/lenna/application.rb +53 -0
- data/lib/lenna/cli/app.rb +39 -0
- data/lib/lenna/cli/commands/create_project.rb +125 -0
- data/lib/lenna/cli/commands/interface.rb +20 -0
- data/lib/lenna/cli/commands/start_server.rb +143 -0
- data/lib/lenna/cli/templates/application.erb +11 -0
- data/lib/lenna/cli/templates/config.ru.erb +5 -0
- data/lib/lenna/cli/templates/gemfile.erb +14 -0
- data/lib/lenna/middleware/app.rb +107 -92
- data/lib/lenna/middleware/default/error_handler.rb +184 -179
- data/lib/lenna/middleware/default/logging.rb +79 -81
- data/lib/lenna/middleware/default/reload.rb +97 -0
- data/lib/lenna/router/builder.rb +111 -86
- data/lib/lenna/router/cache.rb +44 -30
- data/lib/lenna/router/namespace_stack.rb +66 -62
- data/lib/lenna/router/request.rb +125 -101
- data/lib/lenna/router/response.rb +505 -375
- data/lib/lenna/router/route_matcher.rb +56 -57
- data/lib/lenna/router.rb +187 -154
- data/lib/lennarb/array_extensions.rb +25 -11
- data/lib/lennarb/version.rb +5 -2
- data/lib/lennarb.rb +39 -1
- data/license.md +21 -0
- data/readme.md +31 -0
- metadata +99 -41
- data/CHANGELOG.md +0 -62
- data/LICENCE +0 -24
- data/README.md +0 -229
- data/lib/lenna/base.rb +0 -52
data/lib/lenna/middleware/app.rb
CHANGED
@@ -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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
25
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
68
|
-
end
|
81
|
+
@middleware_chains_cache[signature] ||=
|
82
|
+
build_middleware_chain(action, route_middlewares)
|
83
|
+
end
|
69
84
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
144
|
+
#{error_message_and_backtrace(error)}
|
139
145
|
</div>
|
140
146
|
</body>
|
141
147
|
</html>
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
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
|