lennarb 0.4.4 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 00b277c3ca70e4b15e74cb88d343915623d6ae869d4c98b15f46cf6cd286821d
4
- data.tar.gz: c539aa17c39cd2d861e2bfc16b8ae1b76c1d4e37304f95e918ded2235e8c7a82
3
+ metadata.gz: ff30294350acea0efb5cf2c074d256472f99300d46fee6fe0ea9cadfb375e151
4
+ data.tar.gz: d96ea3d9ade277730f83fa0b011d3396d73be3ba8692229c5ad98cfd72f48ea4
5
5
  SHA512:
6
- metadata.gz: ab79db446aab1914aa18146f5b0f8814c68296f04fc7b300af3ac9e0079b11f002b284402335a03acba3a2d63d039c54ce20396e84b1c60b3cc9dfadbeb5f860
7
- data.tar.gz: d16922a8773b300d364c2db69b3925555dfa3d29f91a4018c2d887360dd0cdb36c5b9c3a0821a4b6044bb14586d739ae4ce98168e6bdcb9e9a6d82a494337fe0
6
+ metadata.gz: a5aad4351361de62499992df5f53ac8ec5f8b42f3cd1b207d108ac137404ab6e4e103f394193ae58c847195cc97fe53d475d275e542840360b4d61dfe74474c4
7
+ data.tar.gz: 55661804a4cb178b3365534c5df7a3c7653ecbe07e598b04ae4edfda64f0ada02edcd03ff08cc6f3004a519f5b1df80da4eccc1f4b2a6162d14166e6786f471e
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,252 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2023-2024, by Aristóteles Coutinho.
5
+
6
+ require 'colorize'
7
+
8
+ class Lennarb
9
+ module Application
10
+ class Base
11
+ # @attribute [r] _route
12
+ # @returns [RouteNode]
13
+ #
14
+ # @attribute [r] _middlewares
15
+ # @returns [Array]
16
+ #
17
+ # @attribute [r] _global_after_hooks
18
+ # @returns [Array]
19
+ #
20
+ # @attribute [r] _global_before_hooks
21
+ # @returns [Array]
22
+ #
23
+ # @attribute [r] _after_hooks
24
+ # @returns [RouteNode]
25
+ #
26
+ # @attribute [r] _before_hooks
27
+ # @returns [RouteNode]
28
+ #
29
+ attr_accessor :_route, :_global_after_hooks, :_global_before_hooks, :_after_hooks, :_before_hooks
30
+
31
+ # Initialize the Application
32
+ #
33
+ # @returns [Base]
34
+ #
35
+ def self.inherited(subclass)
36
+ subclass.instance_variable_set(:@_route, Lennarb.new)
37
+ subclass.instance_variable_set(:@_middlewares, [])
38
+ subclass.instance_variable_set(:@_global_after_hooks, [])
39
+ subclass.instance_variable_set(:@_global_before_hooks, [])
40
+ subclass.instance_variable_set(:@_after_hooks, Lennarb::RouteNode.new)
41
+ subclass.instance_variable_set(:@_before_hooks, Lennarb::RouteNode.new)
42
+ end
43
+
44
+ def self.get(...) = @_route.get(...)
45
+ def self.put(...) = @_route.put(...)
46
+ def self.post(...) = @_route.post(...)
47
+ def self.head(...) = @_route.head(...)
48
+ def self.match(...) = @_route.match(...)
49
+ def self.patch(...) = @_route.patch(...)
50
+ def self.delete(...) = @_route.delete(...)
51
+ def self.options(...) = @_route.options(...)
52
+
53
+ # @returns [Array] middlewares
54
+ def self.middlewares = @_middlewares
55
+
56
+ # Use a middleware
57
+ #
58
+ # @parameter [Object] middleware
59
+ # @parameter [Array] args
60
+ # @parameter [Block] block
61
+ #
62
+ # @returns [Array] middlewares
63
+ #
64
+ def self.use(middleware, *args, &block)
65
+ @_middlewares << [middleware, args, block]
66
+ end
67
+
68
+ # Add a before hook
69
+ #
70
+ # @parameter [String] path
71
+ # @parameter [Block] block
72
+ #
73
+ def self.before(path = nil, &block)
74
+ if path
75
+ parts = path.split('/').reject(&:empty?)
76
+ @_before_hooks.add_route(parts, :before, block)
77
+ else
78
+ @_global_before_hooks << block
79
+ end
80
+ end
81
+
82
+ # Add a after hook
83
+ #
84
+ # @parameter [String] path
85
+ # @parameter [Block] block
86
+ #
87
+ def self.after(path = nil, &block)
88
+ if path
89
+ parts = path.split('/').reject(&:empty?)
90
+ @_after_hooks.add_route(parts, :after, block)
91
+ else
92
+ @_global_after_hooks << block
93
+ end
94
+ end
95
+
96
+ # Run the Application
97
+ #
98
+ # @returns [Base] self
99
+ #
100
+ # When you use this method, the application will be frozen. And you can't add more routes after that.
101
+ # This method is used to run the application in a Rack server so, you can use the `rackup` command
102
+ # to run the application.
103
+ # Ex. rackup -p 3000
104
+ # This command will use the following middleware:
105
+ # - Rack::ShowExceptions
106
+ # - Rack::Lint
107
+ # - Rack::MethodOverride
108
+ # - Rack::Head
109
+ # - Rack::ContentLength
110
+ # - Rack::Static
111
+ #
112
+ def self.run!
113
+ stack = Rack::Builder.new
114
+
115
+ use Rack::ShowExceptions
116
+ use Rack::Lint
117
+ use Rack::MethodOverride
118
+ use Rack::Head
119
+ use Rack::ContentLength
120
+ use Rack::Static,
121
+ root: 'public',
122
+ urls: ['/404.html', '/500.html'],
123
+ header_rules: [
124
+ [200, %w[html], { 'content-type' => 'text/html; charset=utf-8' }],
125
+ [400, %w[html], { 'content-type' => 'text/html; charset=utf-8' }],
126
+ [500, %w[html], { 'content-type' => 'text/html; charset=utf-8' }]
127
+ ]
128
+
129
+ middlewares.each do |(middleware, args, block)|
130
+ stack.use(middleware, *args, &block)
131
+ end
132
+
133
+ stack.run ->(env) do
134
+ catch(:halt) do
135
+ begin
136
+ execute_hooks(@_before_hooks, env, :before)
137
+ res = @_route.call(env)
138
+ execute_hooks(@_after_hooks, env, :after)
139
+ res
140
+ rescue StandardError => e
141
+ render_error if production?
142
+
143
+ puts e.message.red
144
+ puts e.backtrace
145
+ raise e
146
+ end.then do |response|
147
+ case response
148
+ in [400..499, _, _] then render_not_found
149
+ else response
150
+ end
151
+ end
152
+ end
153
+ end
154
+
155
+ @_route.freeze!
156
+
157
+ stack.to_app
158
+ end
159
+
160
+ def self.test? = ENV['RACK_ENV'] == 'test' || ENV['LENNARB_ENV'] == 'test'
161
+ def self.production? = ENV['RACK_ENV'] == 'production' || ENV['LENNARB_ENV'] == 'production'
162
+ def self.development? = ENV['RACK_ENV'] == 'development' || ENV['LENNARB_ENV'] == 'development'
163
+
164
+ # Render a not found
165
+ #
166
+ # @returns [void]
167
+ #
168
+ def self.render_not_found(content = nil)
169
+ default = File.exist?('public/404.html')
170
+ body = content || default || 'Not Found'
171
+ throw :halt, [404, { 'content-type' => 'text/html' }, [body]]
172
+ end
173
+
174
+ # Render an error
175
+ #
176
+ # @returns [void]
177
+ #
178
+ def self.render_error(content = nil)
179
+ default = File.exist?('public/500.html')
180
+ body = content || default || 'Internal Server Error'
181
+ throw :halt, [500, { 'content-type' => 'text/html' }, [body]]
182
+ end
183
+
184
+ # Redirect to a path
185
+ #
186
+ # @parameter [String] path
187
+ # @parameter [Integer] status default is 302
188
+ #
189
+ def self.redirect(path, status = 302) = throw :halt, [status, { 'location' => path }, []]
190
+
191
+ # Execute the hooks
192
+ #
193
+ # @parameter [RouteNode] hook_route
194
+ # @parameter [Hash] env
195
+ # @parameter [Symbol] action
196
+ #
197
+ # @returns [void]
198
+ #
199
+ def self.execute_hooks(hook_route, env, action)
200
+ execute_global_hooks(env, action)
201
+
202
+ execute_route_hooks(hook_route, env, action)
203
+ end
204
+
205
+ # Execute the global hooks
206
+ #
207
+ # @parameter [Hash] env
208
+ # @parameter [Symbol] action
209
+ #
210
+ # @returns [void]
211
+ #
212
+ def self.execute_global_hooks(env, action)
213
+ global_hooks = action == :before ? @_global_before_hooks : @_global_after_hooks
214
+ global_hooks.each { |hook| hook.call(env) }
215
+ end
216
+
217
+ # Execute the route hooks
218
+ #
219
+ # @parameter [RouteNode] hook_route
220
+ # @parameter [Hash] env
221
+ # @parameter [Symbol] action
222
+ #
223
+ # @returns [void]
224
+ #
225
+ def self.execute_route_hooks(hook_route, env, action)
226
+ parts = parse_path(env)
227
+ return unless parts
228
+
229
+ block, = hook_route.match_route(parts, action)
230
+ block&.call(env)
231
+ end
232
+
233
+ # Parse the path
234
+ #
235
+ # @parameter [Hash] env
236
+ #
237
+ # @returns [Array] parts
238
+ #
239
+ def self.parse_path(env) = env[Rack::PATH_INFO]&.split('/')&.reject(&:empty?)
240
+
241
+ # Check if the request is a HTML request
242
+ #
243
+ # @parameter [Hash] env
244
+ #
245
+ # @returns [Boolean]
246
+ #
247
+ def self.html_request?(env) = env['HTTP_ACCEPT']&.include?('text/html')
248
+
249
+ private_class_method :execute_hooks, :execute_global_hooks, :execute_route_hooks, :parse_path, :html_request?
250
+ end
251
+ end
252
+ end
@@ -125,6 +125,8 @@ class Lennarb
125
125
  def redirect(path, status = 302)
126
126
  @headers[LOCATION] = path
127
127
  @status = status
128
+
129
+ throw :halt, finish
128
130
  end
129
131
 
130
132
  # Finish the response
@@ -4,7 +4,7 @@
4
4
  # Copyright, 2023-2024, by Aristóteles Coutinho.
5
5
 
6
6
  class Lennarb
7
- VERSION = '0.4.4'
7
+ VERSION = '0.5.1'
8
8
 
9
9
  public_constant :VERSION
10
10
  end
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 :root
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
- @root = RouteNode.new
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 = @root.match_route(parts, http_method)
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! = @root.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
- @root.add_route(parts, http_method, block)
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.4
4
+ version: 0.5.1
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-04-03 00:00:00.000000000 Z
11
+ date: 2024-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -180,10 +180,10 @@ files:
180
180
  - changelog.md
181
181
  - exe/lenna
182
182
  - lib/lennarb.rb
183
+ - lib/lennarb/application/base.rb
183
184
  - lib/lennarb/request.rb
184
185
  - lib/lennarb/response.rb
185
186
  - lib/lennarb/route_node.rb
186
- - lib/lennarb/router.rb
187
187
  - lib/lennarb/version.rb
188
188
  - license.md
189
189
  - readme.md
@@ -211,7 +211,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
211
211
  - !ruby/object:Gem::Version
212
212
  version: '0'
213
213
  requirements: []
214
- rubygems_version: 3.5.3
214
+ rubygems_version: 3.5.9
215
215
  signing_key:
216
216
  specification_version: 4
217
217
  summary: A lightweight and experimental web framework for Ruby.
@@ -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