lennarb 0.4.4 → 0.5.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 +6 -0
- data/lib/lennarb/application/base.rb +236 -0
- data/lib/lennarb/response.rb +2 -0
- data/lib/lennarb/version.rb +1 -1
- data/lib/lennarb.rb +7 -28
- metadata +32 -4
- data/lib/lennarb/router.rb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 888f84e566de65f5c87013ff56ac3fe485f59acba6f1cc39862178894cf101fa
|
4
|
+
data.tar.gz: 8703693e316b83f747f927c23b07d56f6dd571f813d8c00901432cf6c941d1f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc83791eb32ad87b5aa772f375b048829d794d35bdea5c5d1d083dc4a2e8b27560b313c79f51ca7a3a7084d73cc4ddeb9fb17a29a33a211bd53db57090af791a
|
7
|
+
data.tar.gz: a1fb09a73e8610d562cb4f4ea5e2ce1a07ffa68f4574363f07700b0e1004823fb5f1003d6cf6276fec2eb0002894ddf4dce7c7e4c4b55773bbb729a96ec74f4c
|
data/changelog.md
CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
## [0.4.4] - 2024-04-02
|
11
|
+
|
12
|
+
### Added
|
13
|
+
|
14
|
+
- Add `Lennarb::Router` module to manage the routes in the project. Now, the `Lennarb` class is the main class of the project.
|
15
|
+
|
10
16
|
## [0.4.3] - 2024-04-01
|
11
17
|
|
12
18
|
### Added
|
@@ -0,0 +1,236 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2023-2024, by Aristóteles Coutinho.
|
5
|
+
|
6
|
+
class Lennarb
|
7
|
+
module Application
|
8
|
+
class Base
|
9
|
+
# @attribute [r] _route
|
10
|
+
# @returns [RouteNode]
|
11
|
+
#
|
12
|
+
# @attribute [r] _middlewares
|
13
|
+
# @returns [Array]
|
14
|
+
#
|
15
|
+
# @attribute [r] _global_after_hooks
|
16
|
+
# @returns [Array]
|
17
|
+
#
|
18
|
+
# @attribute [r] _global_before_hooks
|
19
|
+
# @returns [Array]
|
20
|
+
#
|
21
|
+
# @attribute [r] _after_hooks
|
22
|
+
# @returns [RouteNode]
|
23
|
+
#
|
24
|
+
# @attribute [r] _before_hooks
|
25
|
+
# @returns [RouteNode]
|
26
|
+
#
|
27
|
+
attr_accessor :_route, :_global_after_hooks, :_global_before_hooks, :_after_hooks, :_before_hooks
|
28
|
+
|
29
|
+
# Initialize the Application
|
30
|
+
#
|
31
|
+
# @returns [Base]
|
32
|
+
#
|
33
|
+
def self.inherited(subclass)
|
34
|
+
subclass.instance_variable_set(:@_route, Lennarb.new)
|
35
|
+
subclass.instance_variable_set(:@_middlewares, [])
|
36
|
+
subclass.instance_variable_set(:@_global_after_hooks, [])
|
37
|
+
subclass.instance_variable_set(:@_global_before_hooks, [])
|
38
|
+
subclass.instance_variable_set(:@_after_hooks, Lennarb::RouteNode.new)
|
39
|
+
subclass.instance_variable_set(:@_before_hooks, Lennarb::RouteNode.new)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.get(...) = @_route.get(...)
|
43
|
+
def self.put(...) = @_route.put(...)
|
44
|
+
def self.post(...) = @_route.post(...)
|
45
|
+
def self.head(...) = @_route.head(...)
|
46
|
+
def self.match(...) = @_route.match(...)
|
47
|
+
def self.patch(...) = @_route.patch(...)
|
48
|
+
def self.delete(...) = @_route.delete(...)
|
49
|
+
def self.options(...) = @_route.options(...)
|
50
|
+
|
51
|
+
# @returns [Array] middlewares
|
52
|
+
def self.middlewares = @_middlewares
|
53
|
+
|
54
|
+
# Use a middleware
|
55
|
+
#
|
56
|
+
# @parameter [Object] middleware
|
57
|
+
# @parameter [Array] args
|
58
|
+
# @parameter [Block] block
|
59
|
+
#
|
60
|
+
# @returns [Array] middlewares
|
61
|
+
#
|
62
|
+
def self.use(middleware, *args, &block)
|
63
|
+
@_middlewares << [middleware, args, block]
|
64
|
+
end
|
65
|
+
|
66
|
+
# Add a before hook
|
67
|
+
#
|
68
|
+
# @parameter [String] path
|
69
|
+
# @parameter [Block] block
|
70
|
+
#
|
71
|
+
def self.before(path = nil, &block)
|
72
|
+
if path
|
73
|
+
parts = path.split('/').reject(&:empty?)
|
74
|
+
@_before_hooks.add_route(parts, :before, block)
|
75
|
+
else
|
76
|
+
@_global_before_hooks << block
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Add a after hook
|
81
|
+
#
|
82
|
+
# @parameter [String] path
|
83
|
+
# @parameter [Block] block
|
84
|
+
#
|
85
|
+
def self.after(path = nil, &block)
|
86
|
+
if path
|
87
|
+
parts = path.split('/').reject(&:empty?)
|
88
|
+
@_after_hooks.add_route(parts, :after, block)
|
89
|
+
else
|
90
|
+
@_global_after_hooks << block
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Call the application
|
95
|
+
#
|
96
|
+
# @parameter [Hash] env
|
97
|
+
#
|
98
|
+
# @returns [Array] response
|
99
|
+
#
|
100
|
+
def self.call(env)
|
101
|
+
response =
|
102
|
+
catch(:halt) do
|
103
|
+
execute_hooks(@_before_hooks, env, :before)
|
104
|
+
|
105
|
+
res = @_route.call(env)
|
106
|
+
|
107
|
+
execute_hooks(@_after_hooks, env, :after)
|
108
|
+
res
|
109
|
+
end
|
110
|
+
|
111
|
+
case response
|
112
|
+
in [404 | 404, _, _] if html_request?(env) && File.exist?('public/404.html')
|
113
|
+
env['PATH_INFO'] = '/404.html'
|
114
|
+
Rack::Static.new(-> { response }, urls: ['/404.html'], root: 'public').call(env)
|
115
|
+
in [404 | 404, _, _] then response
|
116
|
+
else
|
117
|
+
response.is_a?(Array) ? response : response.finish
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Run the Application
|
122
|
+
#
|
123
|
+
# @returns [Base] self
|
124
|
+
#
|
125
|
+
# When you use this method, the application will be frozen. And you can't add more routes after that.
|
126
|
+
# This method is used to run the application in a Rack server so, you can use the `rackup` command to run the application.
|
127
|
+
# Ex. rackup -p 3000
|
128
|
+
# This command will use the following middleware:
|
129
|
+
# - Rack::ShowExceptions
|
130
|
+
# - Rack::Lint
|
131
|
+
# - Rack::MethodOverride
|
132
|
+
# - Rack::Head
|
133
|
+
# - Rack::ContentLength
|
134
|
+
# - Rack::Static
|
135
|
+
#
|
136
|
+
def self.run!
|
137
|
+
stack = Rack::Builder.new
|
138
|
+
|
139
|
+
use Rack::ShowExceptions
|
140
|
+
use Rack::Lint
|
141
|
+
use Rack::MethodOverride
|
142
|
+
use Rack::Head
|
143
|
+
use Rack::ContentLength
|
144
|
+
use Rack::Static,
|
145
|
+
root: 'public',
|
146
|
+
urls: ['/404.html'],
|
147
|
+
header_rules: [
|
148
|
+
[200, %w[html], { 'content-type' => 'text/html; charset=utf-8' }]
|
149
|
+
]
|
150
|
+
|
151
|
+
middlewares.each do |(middleware, args, block)|
|
152
|
+
stack.use(middleware, *args, &block)
|
153
|
+
end
|
154
|
+
|
155
|
+
stack.run @_route
|
156
|
+
|
157
|
+
@_route.freeze!
|
158
|
+
|
159
|
+
@_route = stack.to_app.freeze
|
160
|
+
|
161
|
+
self
|
162
|
+
end
|
163
|
+
|
164
|
+
# Redirect to a path
|
165
|
+
#
|
166
|
+
# @parameter [String] path
|
167
|
+
# @parameter [Integer] status default is 302
|
168
|
+
# @parameter [Hash] env (optional)
|
169
|
+
#
|
170
|
+
def self.redirect(path, status = 302, _env = nil)
|
171
|
+
response = Rack::Response.new([], status, 'location' => path)
|
172
|
+
throw :halt, response.finish
|
173
|
+
end
|
174
|
+
|
175
|
+
# Execute the hooks
|
176
|
+
#
|
177
|
+
# @parameter [RouteNode] hook_route
|
178
|
+
# @parameter [Hash] env
|
179
|
+
# @parameter [Symbol] action
|
180
|
+
#
|
181
|
+
# @returns [void]
|
182
|
+
#
|
183
|
+
def self.execute_hooks(hook_route, env, action)
|
184
|
+
execute_global_hooks(env, action)
|
185
|
+
|
186
|
+
execute_route_hooks(hook_route, env, action)
|
187
|
+
end
|
188
|
+
|
189
|
+
# Execute the global hooks
|
190
|
+
#
|
191
|
+
# @parameter [Hash] env
|
192
|
+
# @parameter [Symbol] action
|
193
|
+
#
|
194
|
+
# @returns [void]
|
195
|
+
#
|
196
|
+
def self.execute_global_hooks(env, action)
|
197
|
+
global_hooks = action == :before ? @_global_before_hooks : @_global_after_hooks
|
198
|
+
global_hooks.each { |hook| hook.call(env) }
|
199
|
+
end
|
200
|
+
|
201
|
+
# Execute the route hooks
|
202
|
+
#
|
203
|
+
# @parameter [RouteNode] hook_route
|
204
|
+
# @parameter [Hash] env
|
205
|
+
# @parameter [Symbol] action
|
206
|
+
#
|
207
|
+
# @returns [void]
|
208
|
+
#
|
209
|
+
def self.execute_route_hooks(hook_route, env, action)
|
210
|
+
parts = parse_path(env)
|
211
|
+
return unless parts
|
212
|
+
|
213
|
+
block, = hook_route.match_route(parts, action)
|
214
|
+
block&.call(env)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Parse the path
|
218
|
+
#
|
219
|
+
# @parameter [Hash] env
|
220
|
+
#
|
221
|
+
# @returns [Array] parts
|
222
|
+
#
|
223
|
+
def self.parse_path(env) = env[Rack::PATH_INFO]&.split('/')&.reject(&:empty?)
|
224
|
+
|
225
|
+
# Check if the request is a HTML request
|
226
|
+
#
|
227
|
+
# @parameter [Hash] env
|
228
|
+
#
|
229
|
+
# @returns [Boolean]
|
230
|
+
#
|
231
|
+
def self.html_request?(env) = env['HTTP_ACCEPT']&.include?('text/html')
|
232
|
+
|
233
|
+
private_class_method :execute_hooks, :execute_global_hooks, :execute_route_hooks, :parse_path, :html_request?
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
data/lib/lennarb/response.rb
CHANGED
data/lib/lennarb/version.rb
CHANGED
data/lib/lennarb.rb
CHANGED
@@ -12,10 +12,10 @@ require 'rack'
|
|
12
12
|
|
13
13
|
# Base class for Lennarb
|
14
14
|
#
|
15
|
+
require_relative 'lennarb/application/base'
|
15
16
|
require_relative 'lennarb/request'
|
16
17
|
require_relative 'lennarb/response'
|
17
18
|
require_relative 'lennarb/route_node'
|
18
|
-
require_relative 'lennarb/router'
|
19
19
|
require_relative 'lennarb/version'
|
20
20
|
|
21
21
|
class Lennarb
|
@@ -26,7 +26,7 @@ class Lennarb
|
|
26
26
|
# @attribute [r] root
|
27
27
|
# @returns [RouteNode]
|
28
28
|
#
|
29
|
-
attr_reader :
|
29
|
+
attr_reader :_root
|
30
30
|
|
31
31
|
# Initialize the application
|
32
32
|
#
|
@@ -35,7 +35,7 @@ class Lennarb
|
|
35
35
|
# @returns [Lennarb]
|
36
36
|
#
|
37
37
|
def initialize
|
38
|
-
@
|
38
|
+
@_root = RouteNode.new
|
39
39
|
yield self if block_given?
|
40
40
|
end
|
41
41
|
|
@@ -58,7 +58,7 @@ class Lennarb
|
|
58
58
|
http_method = env[Rack::REQUEST_METHOD].to_sym
|
59
59
|
parts = SplitPath[env[Rack::PATH_INFO]]
|
60
60
|
|
61
|
-
block, params = @
|
61
|
+
block, params = @_root.match_route(parts, http_method)
|
62
62
|
return [404, { 'content-type' => 'text/plain' }, ['Not Found']] unless block
|
63
63
|
|
64
64
|
@res = Response.new
|
@@ -66,36 +66,15 @@ class Lennarb
|
|
66
66
|
|
67
67
|
catch(:halt) do
|
68
68
|
instance_exec(req, @res, &block)
|
69
|
-
finish
|
69
|
+
@res.finish
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
-
# Finish the request
|
74
|
-
#
|
75
|
-
# @returns [Array] Response
|
76
|
-
#
|
77
|
-
def finish! = halt(@res.finish)
|
78
|
-
|
79
|
-
# Immediately stops the request and returns `response`
|
80
|
-
# as per Rack's specification.
|
81
|
-
#
|
82
|
-
# halt([200, { "Content-Type" => "text/html" }, ["hello"]])
|
83
|
-
# halt([res.status, res.headers, res.body])
|
84
|
-
# halt(res.finish)
|
85
|
-
#
|
86
|
-
# @parameter [Array] response
|
87
|
-
#
|
88
|
-
# @returns [Array] response
|
89
|
-
#
|
90
|
-
def halt(response)
|
91
|
-
throw(:halt, response)
|
92
|
-
end
|
93
|
-
|
94
73
|
# Freeze the routes
|
95
74
|
#
|
96
75
|
# @returns [void]
|
97
76
|
#
|
98
|
-
def freeze! = @
|
77
|
+
def freeze! = @_root.freeze
|
99
78
|
|
100
79
|
# Add a routes
|
101
80
|
#
|
@@ -124,6 +103,6 @@ class Lennarb
|
|
124
103
|
#
|
125
104
|
def add_route(path, http_method, block)
|
126
105
|
parts = SplitPath[path]
|
127
|
-
@
|
106
|
+
@_root.add_route(parts, http_method, block)
|
128
107
|
end
|
129
108
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lennarb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aristóteles Coutinho
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-05-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -44,6 +44,34 @@ dependencies:
|
|
44
44
|
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: 3.0.8
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rack-protection
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '4.0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '4.0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: rack-session
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '2.0'
|
68
|
+
type: :runtime
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '2.0'
|
47
75
|
- !ruby/object:Gem::Dependency
|
48
76
|
name: bake
|
49
77
|
requirement: !ruby/object:Gem::Requirement
|
@@ -180,10 +208,10 @@ files:
|
|
180
208
|
- changelog.md
|
181
209
|
- exe/lenna
|
182
210
|
- lib/lennarb.rb
|
211
|
+
- lib/lennarb/application/base.rb
|
183
212
|
- lib/lennarb/request.rb
|
184
213
|
- lib/lennarb/response.rb
|
185
214
|
- lib/lennarb/route_node.rb
|
186
|
-
- lib/lennarb/router.rb
|
187
215
|
- lib/lennarb/version.rb
|
188
216
|
- license.md
|
189
217
|
- readme.md
|
@@ -211,7 +239,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
211
239
|
- !ruby/object:Gem::Version
|
212
240
|
version: '0'
|
213
241
|
requirements: []
|
214
|
-
rubygems_version: 3.5.
|
242
|
+
rubygems_version: 3.5.9
|
215
243
|
signing_key:
|
216
244
|
specification_version: 4
|
217
245
|
summary: A lightweight and experimental web framework for Ruby.
|
data/lib/lennarb/router.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2023-2024, by Aristóteles Coutinho.
|
5
|
-
|
6
|
-
class Lennarb
|
7
|
-
# This module must be included in the class that will use the router.
|
8
|
-
#
|
9
|
-
module Router
|
10
|
-
def self.included(base)
|
11
|
-
base.extend(ClassMethods)
|
12
|
-
base.instance_variable_set(:@router, Lennarb.new)
|
13
|
-
base.define_singleton_method(:router) do
|
14
|
-
base.instance_variable_get(:@router)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
module ClassMethods
|
19
|
-
# Helper method to define a route
|
20
|
-
#
|
21
|
-
# returns [Lennarb] instance of the router
|
22
|
-
#
|
23
|
-
def route = router
|
24
|
-
|
25
|
-
# Use this in congi.ru to start the application
|
26
|
-
#
|
27
|
-
def app
|
28
|
-
router.freeze!
|
29
|
-
router
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|