lennarb 0.4.4 → 0.5.0

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: 888f84e566de65f5c87013ff56ac3fe485f59acba6f1cc39862178894cf101fa
4
+ data.tar.gz: 8703693e316b83f747f927c23b07d56f6dd571f813d8c00901432cf6c941d1f2
5
5
  SHA512:
6
- metadata.gz: ab79db446aab1914aa18146f5b0f8814c68296f04fc7b300af3ac9e0079b11f002b284402335a03acba3a2d63d039c54ce20396e84b1c60b3cc9dfadbeb5f860
7
- data.tar.gz: d16922a8773b300d364c2db69b3925555dfa3d29f91a4018c2d887360dd0cdb36c5b9c3a0821a4b6044bb14586d739ae4ce98168e6bdcb9e9a6d82a494337fe0
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
@@ -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.0'
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.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-04-03 00:00:00.000000000 Z
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.3
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.
@@ -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