lennarb 0.5.1 → 0.6.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ff30294350acea0efb5cf2c074d256472f99300d46fee6fe0ea9cadfb375e151
4
- data.tar.gz: d96ea3d9ade277730f83fa0b011d3396d73be3ba8692229c5ad98cfd72f48ea4
3
+ metadata.gz: d9cfd70e5b40a64d96f5cd52b748c32a36b796c43e6d490b4df64bc5fae3c781
4
+ data.tar.gz: ffc4abf90906f7378879a0d342e74922c01c6ae9d6d59855128d4fd3ec11d40c
5
5
  SHA512:
6
- metadata.gz: a5aad4351361de62499992df5f53ac8ec5f8b42f3cd1b207d108ac137404ab6e4e103f394193ae58c847195cc97fe53d475d275e542840360b4d61dfe74474c4
7
- data.tar.gz: 55661804a4cb178b3365534c5df7a3c7653ecbe07e598b04ae4edfda64f0ada02edcd03ff08cc6f3004a519f5b1df80da4eccc1f4b2a6162d14166e6786f471e
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
@@ -32,107 +32,97 @@ class Lennarb
32
32
  #
33
33
  # @returns [Base]
34
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
35
 
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
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)
44
+ end
67
45
 
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
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]
79
68
  end
80
- end
81
69
 
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
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
81
+ end
93
82
  end
94
- end
95
83
 
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)
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
131
96
  end
132
97
 
133
- stack.run ->(env) do
134
- catch(:halt) do
135
- begin
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
123
+
124
+ stack.run ->(env) do
125
+ catch(:halt) do
136
126
  execute_hooks(@_before_hooks, env, :before)
137
127
  res = @_route.call(env)
138
128
  execute_hooks(@_after_hooks, env, :after)
@@ -143,110 +133,113 @@ class Lennarb
143
133
  puts e.message.red
144
134
  puts e.backtrace
145
135
  raise e
146
- end.then do |response|
147
- case response
148
- in [400..499, _, _] then render_not_found
149
- else response
150
- end
151
136
  end
152
137
  end
153
- end
154
138
 
155
- @_route.freeze!
139
+ @_route.freeze!
156
140
 
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
141
+ stack.to_app
142
+ end
183
143
 
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 }, []]
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
190
157
 
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)
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
201
167
 
202
- execute_route_hooks(hook_route, env, action)
203
- 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
204
198
 
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
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
216
210
 
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
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
228
226
 
229
- block, = hook_route.match_route(parts, action)
230
- 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')
231
242
  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
243
  end
251
244
  end
252
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.1'
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.1
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-05 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
@@ -181,6 +181,7 @@ files:
181
181
  - exe/lenna
182
182
  - lib/lennarb.rb
183
183
  - lib/lennarb/application/base.rb
184
+ - lib/lennarb/plugin.rb
184
185
  - lib/lennarb/request.rb
185
186
  - lib/lennarb/response.rb
186
187
  - lib/lennarb/route_node.rb