lennarb 0.5.0 → 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: 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