lennarb 1.2.0 → 1.3.0
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 +20 -0
- data/lib/lennarb/plugin.rb +45 -31
- data/lib/lennarb/plugins/hooks.rb +117 -0
- data/lib/lennarb/plugins/mount.rb +66 -0
- data/lib/lennarb/request.rb +76 -37
- data/lib/lennarb/response.rb +133 -133
- data/lib/lennarb/route_node.rb +49 -67
- data/lib/lennarb/version.rb +2 -2
- data/lib/lennarb.rb +138 -124
- data/license.md +1 -2
- metadata +5 -4
- data/lib/lennarb/application/base.rb +0 -283
data/lib/lennarb/response.rb
CHANGED
@@ -4,137 +4,137 @@
|
|
4
4
|
# Copyright, 2023-2024, by Aristóteles Coutinho.
|
5
5
|
|
6
6
|
class Lennarb
|
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
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
7
|
+
class Response
|
8
|
+
# @!attribute [rw] status
|
9
|
+
# @returns [Integer]
|
10
|
+
#
|
11
|
+
attr_accessor :status
|
12
|
+
|
13
|
+
# @!attribute [r] body
|
14
|
+
# @returns [Array]
|
15
|
+
#
|
16
|
+
attr_reader :body
|
17
|
+
|
18
|
+
# @!attribute [r] headers
|
19
|
+
# @returns [Hash]
|
20
|
+
#
|
21
|
+
attr_reader :headers
|
22
|
+
|
23
|
+
# @!attribute [r] length
|
24
|
+
# @returns [Integer]
|
25
|
+
#
|
26
|
+
attr_reader :length
|
27
|
+
|
28
|
+
# Constants
|
29
|
+
#
|
30
|
+
LOCATION = 'location'
|
31
|
+
private_constant :LOCATION
|
32
|
+
|
33
|
+
CONTENT_TYPE = 'content-type'
|
34
|
+
private_constant :CONTENT_TYPE
|
35
|
+
|
36
|
+
CONTENT_LENGTH = 'content-length'
|
37
|
+
private_constant :CONTENT_LENGTH
|
38
|
+
|
39
|
+
ContentType = { HTML: 'text/html', TEXT: 'text/plain', JSON: 'application/json' }.freeze
|
40
|
+
private_constant :ContentType
|
41
|
+
|
42
|
+
# Initialize the response object
|
43
|
+
#
|
44
|
+
# @returns [Response]
|
45
|
+
#
|
46
|
+
def initialize
|
47
|
+
@status = 404
|
48
|
+
@headers = {}
|
49
|
+
@body = []
|
50
|
+
@length = 0
|
51
|
+
end
|
52
|
+
|
53
|
+
# Set the response header
|
54
|
+
#
|
55
|
+
# @parameter [String] key
|
56
|
+
#
|
57
|
+
# @returns [String] value
|
58
|
+
#
|
59
|
+
def [](key)
|
60
|
+
@headers[key]
|
61
|
+
end
|
62
|
+
|
63
|
+
# Get the response header
|
64
|
+
#
|
65
|
+
# @parameter [String] key
|
66
|
+
# @parameter [String] value
|
67
|
+
#
|
68
|
+
# @returns [String] value
|
69
|
+
#
|
70
|
+
def []=(key, value)
|
71
|
+
@headers[key] = value
|
72
|
+
end
|
73
|
+
|
74
|
+
# Write to the response body
|
75
|
+
#
|
76
|
+
# @parameter [String] str
|
77
|
+
#
|
78
|
+
# @returns [String] str
|
79
|
+
#
|
80
|
+
def write(str)
|
81
|
+
str = str.to_s
|
82
|
+
@length += str.bytesize
|
83
|
+
@headers[CONTENT_LENGTH] = @length.to_s
|
84
|
+
@body << str
|
85
|
+
end
|
86
|
+
|
87
|
+
# Set the response type to text
|
88
|
+
#
|
89
|
+
# @parameter [String] str
|
90
|
+
#
|
91
|
+
# @returns [String] str
|
92
|
+
#
|
93
|
+
def text(str)
|
94
|
+
@headers[CONTENT_TYPE] = ContentType[:TEXT]
|
95
|
+
write(str)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Set the response type to html
|
99
|
+
#
|
100
|
+
# @parameter [String] str
|
101
|
+
#
|
102
|
+
# @returns [String] str
|
103
|
+
#
|
104
|
+
def html(str)
|
105
|
+
@headers[CONTENT_TYPE] = ContentType[:HTML]
|
106
|
+
write(str)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Set the response type to json
|
110
|
+
#
|
111
|
+
# @parameter [String] str
|
112
|
+
#
|
113
|
+
# @returns [String] str
|
114
|
+
#
|
115
|
+
def json(str)
|
116
|
+
@headers[CONTENT_TYPE] = ContentType[:JSON]
|
117
|
+
write(str)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Redirect the response
|
121
|
+
#
|
122
|
+
# @parameter [String] path
|
123
|
+
# @parameter [Integer] status, default: 302
|
124
|
+
#
|
125
|
+
def redirect(path, status = 302)
|
126
|
+
@headers[LOCATION] = path
|
127
|
+
@status = status
|
128
|
+
|
129
|
+
throw :halt, finish
|
130
|
+
end
|
131
|
+
|
132
|
+
# Finish the response
|
133
|
+
#
|
134
|
+
# @returns [Array] response
|
135
|
+
#
|
136
|
+
def finish
|
137
|
+
[@status, @headers, @body]
|
138
|
+
end
|
139
|
+
end
|
140
140
|
end
|
data/lib/lennarb/route_node.rb
CHANGED
@@ -4,81 +4,63 @@
|
|
4
4
|
# Copyright, 2023-2024, by Aristóteles Coutinho.
|
5
5
|
|
6
6
|
class Lennarb
|
7
|
-
|
8
|
-
|
7
|
+
class RouteNode
|
8
|
+
attr_accessor :static_children, :dynamic_children, :blocks, :param_key
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@param_key = nil
|
17
|
-
@static_children = {}
|
18
|
-
@dynamic_children = {}
|
19
|
-
end
|
10
|
+
def initialize
|
11
|
+
@blocks = {}
|
12
|
+
@param_key = nil
|
13
|
+
@static_children = {}
|
14
|
+
@dynamic_children = {}
|
15
|
+
end
|
20
16
|
|
21
|
-
|
22
|
-
|
23
|
-
# @parameter parts [Array<String>] The parts of the route
|
24
|
-
# @parameter http_method [Symbol] The HTTP method of the route
|
25
|
-
# @parameter block [Proc] The block to be executed when the route is matched
|
26
|
-
#
|
27
|
-
# @return [void]
|
28
|
-
#
|
29
|
-
def add_route(parts, http_method, block)
|
30
|
-
current_node = self
|
17
|
+
def add_route(parts, http_method, block)
|
18
|
+
current_node = self
|
31
19
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
20
|
+
parts.each do |part|
|
21
|
+
if part.start_with?(':')
|
22
|
+
param_sym = part[1..].to_sym
|
23
|
+
current_node.dynamic_children[param_sym] ||= RouteNode.new
|
24
|
+
dynamic_node = current_node.dynamic_children[param_sym]
|
25
|
+
dynamic_node.param_key = param_sym
|
26
|
+
current_node = dynamic_node
|
27
|
+
else
|
28
|
+
current_node.static_children[part] ||= RouteNode.new
|
29
|
+
current_node = current_node.static_children[part]
|
30
|
+
end
|
31
|
+
end
|
44
32
|
|
45
|
-
|
46
|
-
|
33
|
+
current_node.blocks[http_method] = block
|
34
|
+
end
|
47
35
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
36
|
+
def match_route(parts, http_method, params: {})
|
37
|
+
if parts.empty?
|
38
|
+
return [blocks[http_method], params] if blocks[http_method]
|
39
|
+
else
|
40
|
+
part = parts.first
|
41
|
+
rest = parts[1..]
|
54
42
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
43
|
+
if static_children.key?(part)
|
44
|
+
result_block, result_params = static_children[part].match_route(rest, http_method, params:)
|
45
|
+
return [result_block, result_params] if result_block
|
46
|
+
end
|
59
47
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
48
|
+
dynamic_children.each_value do |dyn_node|
|
49
|
+
new_params = params.dup
|
50
|
+
new_params[dyn_node.param_key] = part
|
51
|
+
result_block, result_params = dyn_node.match_route(rest, http_method, params: new_params)
|
64
52
|
|
65
|
-
|
66
|
-
|
67
|
-
|
53
|
+
return [result_block, result_params] if result_block
|
54
|
+
end
|
55
|
+
end
|
68
56
|
|
69
|
-
|
70
|
-
|
57
|
+
[nil, nil]
|
58
|
+
end
|
71
59
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
def merge!(other)
|
79
|
-
self.static_children.merge!(other.static_children)
|
80
|
-
self.dynamic_children.merge!(other.dynamic_children)
|
81
|
-
self.blocks.merge!(other.blocks)
|
82
|
-
end
|
83
|
-
end
|
60
|
+
def merge!(other)
|
61
|
+
static_children.merge!(other.static_children)
|
62
|
+
dynamic_children.merge!(other.dynamic_children)
|
63
|
+
blocks.merge!(other.blocks)
|
64
|
+
end
|
65
|
+
end
|
84
66
|
end
|