lennarb 0.5.0 → 0.6.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: 888f84e566de65f5c87013ff56ac3fe485f59acba6f1cc39862178894cf101fa
4
- data.tar.gz: 8703693e316b83f747f927c23b07d56f6dd571f813d8c00901432cf6c941d1f2
3
+ metadata.gz: d9cfd70e5b40a64d96f5cd52b748c32a36b796c43e6d490b4df64bc5fae3c781
4
+ data.tar.gz: ffc4abf90906f7378879a0d342e74922c01c6ae9d6d59855128d4fd3ec11d40c
5
5
  SHA512:
6
- metadata.gz: bc83791eb32ad87b5aa772f375b048829d794d35bdea5c5d1d083dc4a2e8b27560b313c79f51ca7a3a7084d73cc4ddeb9fb17a29a33a211bd53db57090af791a
7
- data.tar.gz: a1fb09a73e8610d562cb4f4ea5e2ce1a07ffa68f4574363f07700b0e1004823fb5f1003d6cf6276fec2eb0002894ddf4dce7c7e4c4b55773bbb729a96ec74f4c
6
+ metadata.gz: 6d1e7a05a3e931a3163b4103d9900c36163e72e738e0435bc4396fb5f3aeb3c6d044edbdfd8a7fde7512265df91d86dc01a719b7b58ffdf7dc10698a69c1016c
7
+ data.tar.gz: 75c64ac16b3fd3599e78c6a9dd04c951137d11b64b11685af4f9d7d0626331aa554564d8786914d129b93e128ebaa54015f0098b75626e5c74bd347e7ece2861
data/changelog.md CHANGED
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.1] - 2024-05-17
11
+
12
+ ### Added
13
+
14
+ - Add `Lennarb::Plugin` module to manage the plugins in the project. Now, the `Lennarb` class is the main class of the project.
15
+ - Add `Lennarb::Plugin::Base` class to be the base class of the plugins in the project.
16
+ - Add simple guide to use `Lenn` plugins. See [guides/plugins/readme.md](guides/plugins/readme.md) for more details.
17
+
10
18
  ## [0.4.4] - 2024-04-02
11
19
 
12
20
  ### Added
@@ -3,6 +3,8 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2023-2024, by Aristóteles Coutinho.
5
5
 
6
+ require 'colorize'
7
+
6
8
  class Lennarb
7
9
  module Application
8
10
  class Base
@@ -30,207 +32,214 @@ class Lennarb
30
32
  #
31
33
  # @returns [Base]
32
34
  #
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
35
 
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
36
+ class << self
37
+ def inherited(subclass)
38
+ subclass.instance_variable_set(:@_route, Lennarb.new)
39
+ subclass.instance_variable_set(:@_middlewares, [])
40
+ subclass.instance_variable_set(:@_global_after_hooks, [])
41
+ subclass.instance_variable_set(:@_global_before_hooks, [])
42
+ subclass.instance_variable_set(:@_after_hooks, Lennarb::RouteNode.new)
43
+ subclass.instance_variable_set(:@_before_hooks, Lennarb::RouteNode.new)
77
44
  end
78
- end
79
45
 
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
46
+ def get(path, &) = @_route.get(path, &)
47
+ def put(...) = @_route.put(...)
48
+ def post(...) = @_route.post(...)
49
+ def head(...) = @_route.head(...)
50
+ def match(...) = @_route.match(...)
51
+ def patch(...) = @_route.patch(...)
52
+ def delete(...) = @_route.delete(...)
53
+ def options(...) = @_route.options(...)
54
+
55
+ # @returns [Array] middlewares
56
+ def middlewares = @_middlewares
57
+
58
+ # Use a middleware
59
+ #
60
+ # @parameter [Object] middleware
61
+ # @parameter [Array] args
62
+ # @parameter [Block] block
63
+ #
64
+ # @returns [Array] middlewares
65
+ #
66
+ def use(middleware, *args, &block)
67
+ @_middlewares << [middleware, args, block]
91
68
  end
92
- end
93
69
 
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
70
+ # Add a before hook
71
+ #
72
+ # @parameter [String] path
73
+ # @parameter [Block] block
74
+ #
75
+ def before(path = nil, &block)
76
+ if path
77
+ parts = path.split('/').reject(&:empty?)
78
+ @_before_hooks.add_route(parts, :before, block)
79
+ else
80
+ @_global_before_hooks << block
109
81
  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
82
  end
119
- end
120
83
 
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)
84
+ # Add a after hook
85
+ #
86
+ # @parameter [String] path
87
+ # @parameter [Block] block
88
+ #
89
+ def after(path = nil, &block)
90
+ if path
91
+ parts = path.split('/').reject(&:empty?)
92
+ @_after_hooks.add_route(parts, :after, block)
93
+ else
94
+ @_global_after_hooks << block
95
+ end
153
96
  end
154
97
 
155
- stack.run @_route
98
+ # Run the Application
99
+ #
100
+ # @returns [Base] self
101
+ #
102
+ # When you use this method, the application will be frozen. And you can't add more routes after that.
103
+ # This method is used to run the application in a Rack server so, you can use the `rackup` command
104
+ # to run the application.
105
+ # Ex. rackup -p 3000
106
+ # This command will use the following middleware:
107
+ # - Rack::ShowExceptions
108
+ # - Rack::MethodOverride
109
+ # - Rack::Head
110
+ # - Rack::ContentLength
111
+ #
112
+ def run!
113
+ stack = Rack::Builder.new
114
+
115
+ use Rack::ShowExceptions if test? || development?
116
+ use Rack::MethodOverride
117
+ use Rack::Head
118
+ use Rack::ContentLength
119
+
120
+ middlewares.each do |(middleware, args, block)|
121
+ stack.use(middleware, *args, &block)
122
+ end
156
123
 
157
- @_route.freeze!
124
+ stack.run ->(env) do
125
+ catch(:halt) do
126
+ execute_hooks(@_before_hooks, env, :before)
127
+ res = @_route.call(env)
128
+ execute_hooks(@_after_hooks, env, :after)
129
+ res
130
+ rescue StandardError => e
131
+ render_error if production?
132
+
133
+ puts e.message.red
134
+ puts e.backtrace
135
+ raise e
136
+ end
137
+ end
158
138
 
159
- @_route = stack.to_app.freeze
139
+ @_route.freeze!
160
140
 
161
- self
162
- end
141
+ stack.to_app
142
+ end
163
143
 
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
144
+ def test? = ENV['RACK_ENV'] == 'test' || ENV['LENNARB_ENV'] == 'test'
145
+ def production? = ENV['RACK_ENV'] == 'production' || ENV['LENNARB_ENV'] == 'production'
146
+ def development? = ENV['RACK_ENV'] == 'development' || ENV['LENNARB_ENV'] == 'development'
147
+
148
+ # Render a not found
149
+ #
150
+ # @returns [void]
151
+ #
152
+ def render_not_found(content = nil)
153
+ default = File.exist?('public/404.html')
154
+ body = content || default || 'Not Found'
155
+ throw :halt, [404, { 'content-type' => 'text/html' }, [body]]
156
+ end
174
157
 
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)
158
+ # Render an error
159
+ #
160
+ # @returns [void]
161
+ #
162
+ def render_error(content = nil)
163
+ default = File.exist?('public/500.html')
164
+ body = content || default || 'Internal Server Error'
165
+ throw :halt, [500, { 'content-type' => 'text/html' }, [body]]
166
+ end
185
167
 
186
- execute_route_hooks(hook_route, env, action)
187
- end
168
+ # Redirect to a path
169
+ #
170
+ # @parameter [String] path
171
+ # @parameter [Integer] status default is 302
172
+ #
173
+ def redirect(path, status = 302) = throw :halt, [status, { 'location' => path }, []]
174
+
175
+ # To use a plugin
176
+ #
177
+ # @parameter [String | Symbol] plugin_name
178
+ #
179
+ # @returns [void]
180
+ #
181
+ def plugin(plugin_name) = @_route.plugin(plugin_name)
182
+
183
+ private
184
+
185
+ # Execute the hooks
186
+ #
187
+ # @parameter [RouteNode] hook_route
188
+ # @parameter [Hash] env
189
+ # @parameter [Symbol] action
190
+ #
191
+ # @returns [void]
192
+ #
193
+ def execute_hooks(hook_route, env, action)
194
+ execute_global_hooks(env, action)
195
+
196
+ execute_route_hooks(hook_route, env, action)
197
+ end
188
198
 
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
199
+ # Execute the global hooks
200
+ #
201
+ # @parameter [Hash] env
202
+ # @parameter [Symbol] action
203
+ #
204
+ # @returns [void]
205
+ #
206
+ def execute_global_hooks(env, action)
207
+ global_hooks = action == :before ? @_global_before_hooks : @_global_after_hooks
208
+ global_hooks.each { |hook| hook.call(env) }
209
+ end
200
210
 
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
211
+ # Execute the route hooks
212
+ #
213
+ # @parameter [RouteNode] hook_route
214
+ # @parameter [Hash] env
215
+ # @parameter [Symbol] action
216
+ #
217
+ # @returns [void]
218
+ #
219
+ def execute_route_hooks(hook_route, env, action)
220
+ parts = parse_path(env)
221
+ return unless parts
222
+
223
+ block, = hook_route.match_route(parts, action)
224
+ block&.call(env)
225
+ end
212
226
 
213
- block, = hook_route.match_route(parts, action)
214
- block&.call(env)
227
+ # Parse the path
228
+ #
229
+ # @parameter [Hash] env
230
+ #
231
+ # @returns [Array] parts
232
+ #
233
+ def parse_path(env) = env[Rack::PATH_INFO]&.split('/')&.reject(&:empty?)
234
+
235
+ # Check if the request is a HTML request
236
+ #
237
+ # @parameter [Hash] env
238
+ #
239
+ # @returns [Boolean]
240
+ #
241
+ def html_request?(env) = env['HTTP_ACCEPT']&.include?('text/html')
215
242
  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
243
  end
235
244
  end
236
245
  end
@@ -0,0 +1,20 @@
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 Plugin
8
+ @plugins = {}
9
+
10
+ def self.register(name, mod)
11
+ @plugins[name] = mod
12
+ end
13
+
14
+ def self.load(name)
15
+ @plugins[name] || raise("Plugin #{name} did not register itself correctly")
16
+ end
17
+
18
+ def self.plugins = @plugins
19
+ end
20
+ end
@@ -4,7 +4,7 @@
4
4
  # Copyright, 2023-2024, by Aristóteles Coutinho.
5
5
 
6
6
  class Lennarb
7
- VERSION = '0.5.0'
7
+ VERSION = '0.6.1'
8
8
 
9
9
  public_constant :VERSION
10
10
  end
data/lib/lennarb.rb CHANGED
@@ -3,8 +3,6 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2023-2024, by Aristóteles Coutinho.
5
5
 
6
- ENV['RACK_ENV'] ||= 'development'
7
-
8
6
  # Core extensions
9
7
  #
10
8
  require 'pathname'
@@ -13,6 +11,7 @@ require 'rack'
13
11
  # Base class for Lennarb
14
12
  #
15
13
  require_relative 'lennarb/application/base'
14
+ require_relative 'lennarb/plugin'
16
15
  require_relative 'lennarb/request'
17
16
  require_relative 'lennarb/response'
18
17
  require_relative 'lennarb/route_node'
@@ -91,6 +90,17 @@ class Lennarb
91
90
  def delete(path, &block) = add_route(path, :DELETE, block)
92
91
  def options(path, &block) = add_route(path, :OPTIONS, block)
93
92
 
93
+ # Register a plugin
94
+ #
95
+ # @parameter [String | Symbol] plugin_name
96
+ #
97
+ # @returns [void]
98
+ #
99
+ def plugin(plugin_name)
100
+ plugin_module = Lennarb::Plugin.load(plugin_name)
101
+ extend plugin_module
102
+ end
103
+
94
104
  private
95
105
 
96
106
  # Add a route
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.5.0
4
+ version: 0.6.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-05-03 00:00:00.000000000 Z
11
+ date: 2024-05-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -44,34 +44,6 @@ 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'
75
47
  - !ruby/object:Gem::Dependency
76
48
  name: bake
77
49
  requirement: !ruby/object:Gem::Requirement
@@ -209,6 +181,7 @@ files:
209
181
  - exe/lenna
210
182
  - lib/lennarb.rb
211
183
  - lib/lennarb/application/base.rb
184
+ - lib/lennarb/plugin.rb
212
185
  - lib/lennarb/request.rb
213
186
  - lib/lennarb/response.rb
214
187
  - lib/lennarb/route_node.rb