lennarb 1.4.0 → 1.5.0
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 +4 -4
- data/.github/workflows/coverage.yaml +11 -33
- data/.github/workflows/documentation.yaml +32 -13
- data/.github/workflows/test.yaml +2 -1
- data/.gitignore +9 -0
- data/.yardopts +8 -0
- data/CODE_OF_CONDUCT.md +118 -0
- data/CONTRIBUTING.md +155 -0
- data/README.pt-BR.md +147 -0
- data/Rakefile +35 -2
- data/changelog.md +76 -0
- data/gems.rb +3 -22
- data/guides/getting-started/readme.md +48 -30
- data/guides/mounting-applications/readme.md +38 -0
- data/guides/response/readme.md +6 -6
- data/lennarb.gemspec +5 -2
- data/lib/lennarb/app.rb +253 -0
- data/lib/lennarb/base.rb +314 -0
- data/lib/lennarb/config.rb +52 -0
- data/lib/lennarb/constants.rb +12 -0
- data/lib/lennarb/environment.rb +80 -0
- data/lib/lennarb/errors.rb +17 -0
- data/lib/lennarb/helpers.rb +40 -0
- data/lib/lennarb/hooks.rb +71 -0
- data/lib/lennarb/logger.rb +100 -0
- data/lib/lennarb/middleware/request_logger.rb +117 -0
- data/lib/lennarb/middleware_stack.rb +56 -0
- data/lib/lennarb/parameter_filter.rb +76 -0
- data/lib/lennarb/request.rb +211 -33
- data/lib/lennarb/request_handler.rb +61 -0
- data/lib/lennarb/response.rb +34 -28
- data/lib/lennarb/route_node.rb +58 -4
- data/lib/lennarb/routes.rb +67 -0
- data/lib/lennarb/version.rb +2 -4
- data/lib/lennarb.rb +35 -67
- data/logo/lennarb.svg +11 -0
- data/readme.md +183 -34
- metadata +67 -7
- data/lib/lennarb/constansts.rb +0 -1
- data/logo/lennarb.png +0 -0
data/changelog.md
CHANGED
@@ -7,6 +7,81 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
### Added
|
11
|
+
|
12
|
+
- Add `Lennarb::Application` class to be the base class of the "standard" implementation of the Lennarb framework.
|
13
|
+
- Add middleware support to Lennarb::App class.
|
14
|
+
- Add `middleware` support to the `Lennarb::Application` with default middlewares.
|
15
|
+
- Add files to centralize the errors of the project.
|
16
|
+
- Add CODE_OF_CONDUCT.md in English and Portuguese
|
17
|
+
- Add CONTRIBUTING.md in English and Portuguese
|
18
|
+
- Add `Lennarb::Logger` class for structured logging with support for tags and colorization
|
19
|
+
- Add logger dependency to enhance logging capabilities in the framework
|
20
|
+
- Add `Lennarb::ParameterFilter` for sensitive parameter filtering in logs and exceptions
|
21
|
+
- Add `Lennarb::RequestLogger` middleware for detailed HTTP request logging
|
22
|
+
- Add `Lennarb::Hooks` module for implementing hooks in the framework
|
23
|
+
- Add improved request handling and configuration options
|
24
|
+
- Add `RoutesFrozenError` for improved route modification handling
|
25
|
+
- Add support for defining helpers with both modules and blocks in `Lennarb::App`.
|
26
|
+
- Add `Lennarb::Helpers.define` method to handle modules and blocks for helper definitions.
|
27
|
+
- Add tests for `Lennarb::Helpers` to validate module inclusion and block evaluation.
|
28
|
+
- Add `Lennarb::Hooks` tests to validate hook initialization, addition, and execution.
|
29
|
+
|
30
|
+
### Changed
|
31
|
+
|
32
|
+
- Lennarb Logo
|
33
|
+
- Migrate from utopiaproject to yard for documentation
|
34
|
+
- Fix logo SVG display in yard documentation
|
35
|
+
- Restructure App class to enhance routing, middleware, and initialization processes
|
36
|
+
- Simplify Routes class by removing unnecessary comments and enhancing route definitions
|
37
|
+
- Introduce Helpers module for managing application-specific helper methods
|
38
|
+
- Update logger tag to use symbol for consistency
|
39
|
+
- Enhance logging functionality with improved request logging details
|
40
|
+
- Simplify config method by removing block parameter and improving readability
|
41
|
+
- Update `Lennarb::App.helpers` to accept a module or block for defining helpers.
|
42
|
+
|
43
|
+
### Fixed
|
44
|
+
|
45
|
+
- Fix typo in require_relative statement for constants file
|
46
|
+
- Fix conditional debug dependency based on Ruby engine
|
47
|
+
|
48
|
+
## [1.4.1] - 2025-02-23
|
49
|
+
|
50
|
+
### Added
|
51
|
+
|
52
|
+
- Add support to mount routes. Now, you can centralize the routes in a single file and mount them in the main application. Ex.
|
53
|
+
|
54
|
+
```rb
|
55
|
+
class PostsController
|
56
|
+
extend Lennarb::Routes::Mixin
|
57
|
+
|
58
|
+
get '/posts' do |req, res|
|
59
|
+
res.html('Posts')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
SampleApp = Lennarb.new do |router|
|
64
|
+
mount PostsController
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
The `mount` method will add the routes from the `PostsController` class to the main application. You can use the `mount` method with multiple classes, ex. `mount PostsController, CommentsController`.
|
69
|
+
|
70
|
+
- Add `Lennarb::Environment` module to manage the environment variables in the project. Now, the `Lennarb` class is the main class of the project.
|
71
|
+
- Add `Lennarb::Config` module to manage the configuration in the project. Now, the `Lennarb` class is the main class of the project.
|
72
|
+
- Add `Lennarb::App` class.
|
73
|
+
- Lint the code with `standard` gem on the CI/CD pipeline.
|
74
|
+
|
75
|
+
### Changed
|
76
|
+
|
77
|
+
- Convert the `Lennarb` class to a module. Now, the `App` class is the main class of the project.
|
78
|
+
- Move the request process to `Lennarb::RequestHandler` class.
|
79
|
+
- Improve the method `merge!` from `Lennarb::RouterNode` to prevent the duplication of the routes.
|
80
|
+
|
81
|
+
### Fixed
|
82
|
+
|
83
|
+
- Software design issues.
|
84
|
+
|
10
85
|
## [1.4.0] - 2025-02-09
|
11
86
|
|
12
87
|
### Changed
|
@@ -177,6 +252,7 @@ end
|
|
177
252
|
- Add `console` gem to print the logs in the console.
|
178
253
|
|
179
254
|
- Add CLI module to:
|
255
|
+
|
180
256
|
- Create a new project with `lennarb new` command.
|
181
257
|
- Run the server with `lennarb server` command.
|
182
258
|
|
data/gems.rb
CHANGED
@@ -4,26 +4,7 @@ source "https://rubygems.org"
|
|
4
4
|
gemspec
|
5
5
|
|
6
6
|
group :maintenance, optional: true do
|
7
|
-
#
|
8
|
-
#
|
9
|
-
gem "
|
10
|
-
# [https://rubygems.org/gems/covered]
|
11
|
-
# Covered is a Ruby library that helps you to test your project.
|
12
|
-
gem "covered"
|
13
|
-
# [https://rubygems.org/gems/bake-gem]
|
14
|
-
# Bake Gem is a Bake extension that helps you to create a new Ruby
|
15
|
-
# gem.
|
16
|
-
gem "bake-gem"
|
17
|
-
# [https://rubygems.org/gems/bake-modernize]
|
18
|
-
# Bake Modernize is a Bake extension that helps you to modernize your
|
19
|
-
# Ruby code.
|
20
|
-
gem "bake-modernize"
|
21
|
-
# [https://rubygems.org/gems/bake-github-pages]
|
22
|
-
# Bake Github Pages is a Bake extension that helps you to publish your
|
23
|
-
# project documentation to Github Pages.
|
24
|
-
gem "bake-github-pages"
|
25
|
-
# [https://rubygems.org/gems/utpia-project]
|
26
|
-
# Utopia Project is a Bake extension that helps you to create a new
|
27
|
-
# project.
|
28
|
-
gem "utopia-project"
|
7
|
+
# Yard is a documentation generation tool for the Ruby programming language.
|
8
|
+
# [https://rubygems.org/gems/yard]
|
9
|
+
gem "yard"
|
29
10
|
end
|
@@ -27,15 +27,17 @@ Create a new file named `config.ru`:
|
|
27
27
|
```ruby
|
28
28
|
require 'lennarb'
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
MyApp = Lennarb::App.new do
|
31
|
+
routes do
|
32
|
+
get '/' do |req, res|
|
33
|
+
res.status = 200
|
34
|
+
res.html('<h1>Welcome to Lennarb!</h1>')
|
35
|
+
end
|
34
36
|
end
|
35
37
|
end
|
36
38
|
|
37
|
-
|
38
|
-
run
|
39
|
+
MyApp.initialize!
|
40
|
+
run MyApp
|
39
41
|
```
|
40
42
|
|
41
43
|
Start the server:
|
@@ -69,7 +71,7 @@ app.get '/html' do |req, res|
|
|
69
71
|
end
|
70
72
|
|
71
73
|
app.get '/json' do |req, res|
|
72
|
-
res.json(
|
74
|
+
res.json({ message: "JSON response" })
|
73
75
|
end
|
74
76
|
```
|
75
77
|
|
@@ -84,16 +86,16 @@ end
|
|
84
86
|
Routes are defined using HTTP method helpers:
|
85
87
|
|
86
88
|
```ruby
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
89
|
+
Lennarb::App.new do
|
90
|
+
routes do
|
91
|
+
get '/' do |req, res|
|
92
|
+
res.html('Home page')
|
93
|
+
end
|
94
|
+
|
95
|
+
get '/users/:id' do |req, res|
|
96
|
+
user_id = req.params[:id]
|
97
|
+
res.json({ id: user_id })
|
98
|
+
end
|
97
99
|
end
|
98
100
|
end
|
99
101
|
```
|
@@ -103,7 +105,7 @@ end
|
|
103
105
|
Parameters from dynamic route segments are available in `req.params`:
|
104
106
|
|
105
107
|
```ruby
|
106
|
-
|
108
|
+
get '/hello/:name' do |req, res|
|
107
109
|
name = req.params[:name]
|
108
110
|
res.text("Hello, #{name}!")
|
109
111
|
end
|
@@ -117,20 +119,26 @@ Lennarb is thread-safe by design:
|
|
117
119
|
- The router tree is frozen after initialization
|
118
120
|
- Response objects are created per-request
|
119
121
|
|
120
|
-
## Application
|
122
|
+
## Application Life-cycle
|
121
123
|
|
122
124
|
### Initialization
|
123
125
|
|
124
126
|
```ruby
|
125
|
-
|
126
|
-
# Define routes
|
127
|
+
MyApp = Lennarb::App.new do
|
128
|
+
# Define routes
|
129
|
+
routes do
|
130
|
+
end
|
131
|
+
|
132
|
+
# Define Configurations
|
133
|
+
config do
|
134
|
+
end
|
127
135
|
end
|
128
136
|
|
129
137
|
# Initialize and freeze the application
|
130
|
-
|
138
|
+
MyApp.initialize!
|
131
139
|
```
|
132
140
|
|
133
|
-
The `
|
141
|
+
The `initialize!` method:
|
134
142
|
|
135
143
|
- Loads environment-specific dependencies
|
136
144
|
- Freezes the route tree
|
@@ -140,6 +148,12 @@ The `initializer!` method:
|
|
140
148
|
|
141
149
|
Lennarb uses the `LENNA_ENV` environment variable (defaults to "development"):
|
142
150
|
|
151
|
+
It can be set using the following environment variables:
|
152
|
+
|
153
|
+
- `LENNA_ENV`
|
154
|
+
- `APP_ENV`
|
155
|
+
- `RACK_ENV`
|
156
|
+
|
143
157
|
```bash
|
144
158
|
LENNA_ENV=production rackup
|
145
159
|
```
|
@@ -149,7 +163,7 @@ LENNA_ENV=production rackup
|
|
149
163
|
Lennarb provides basic error handling:
|
150
164
|
|
151
165
|
```ruby
|
152
|
-
|
166
|
+
get '/api' do |req, res|
|
153
167
|
# Errors are caught and return 500 with error message
|
154
168
|
raise "Something went wrong"
|
155
169
|
end
|
@@ -162,20 +176,20 @@ Default error responses:
|
|
162
176
|
|
163
177
|
## Best Practices
|
164
178
|
|
165
|
-
1. **Always call
|
179
|
+
1. **Always call initialize!**
|
166
180
|
|
167
181
|
```ruby
|
168
|
-
app = Lennarb.new
|
169
|
-
app.
|
182
|
+
app = Lennarb::App.new
|
183
|
+
app.initialize!
|
170
184
|
run app
|
171
185
|
```
|
172
186
|
|
173
187
|
2. **Set response status**
|
174
188
|
|
175
189
|
```ruby
|
176
|
-
|
190
|
+
get '/api' do |req, res|
|
177
191
|
res.status = 200
|
178
|
-
res.json(
|
192
|
+
res.json({ status: "ok" })
|
179
193
|
end
|
180
194
|
```
|
181
195
|
|
@@ -186,7 +200,7 @@ Default error responses:
|
|
186
200
|
res.html('<h1>Web Page</h1>')
|
187
201
|
|
188
202
|
# JSON for APIs
|
189
|
-
res.json(
|
203
|
+
res.json({ data: "value" })
|
190
204
|
|
191
205
|
# Text for simple responses
|
192
206
|
res.text('Hello')
|
@@ -199,3 +213,7 @@ For help and bug reports, please visit:
|
|
199
213
|
- GitHub Issues: [lennarb/issues](https://github.com/aristotelesbr/lennarb/issues)
|
200
214
|
|
201
215
|
Now you can run your app!
|
216
|
+
|
217
|
+
```
|
218
|
+
|
219
|
+
```
|
@@ -0,0 +1,38 @@
|
|
1
|
+
## Mounting Applications
|
2
|
+
|
3
|
+
You can mount other applications at specific paths using the `mount` method. The component to be mounted must be a subclass of `Lennarb::App`.
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
class Blog < Lennarb::App
|
7
|
+
get "/" do |req, res|
|
8
|
+
res.html("<h1>Welcome to my blog</h1>")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Admin < Lennarb::App
|
13
|
+
get "/" do |req, res|
|
14
|
+
res.html("<h1>Admin Dashboard</h1>")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Application < Lennarb::Base
|
19
|
+
mount(Blog, at: "/blog")
|
20
|
+
mount(Admin, at: "/admin")
|
21
|
+
end
|
22
|
+
```
|
23
|
+
|
24
|
+
## Middleware Configuration
|
25
|
+
|
26
|
+
Middleware can be configured at both the base application level and within individual mounted applications. This allows for flexible and modular application design.
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
class Blog < Lennarb::App
|
30
|
+
middleware do
|
31
|
+
use MyCustomMiddleware
|
32
|
+
end
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
## Conclusion
|
37
|
+
|
38
|
+
By using the `Base` class and the `mount` method, Lennarb provides a powerful way to structure your applications into modular components, each with its own routes and middleware.
|
data/guides/response/readme.md
CHANGED
@@ -10,19 +10,19 @@ You can use the `res` object to send a response to the client.
|
|
10
10
|
```ruby
|
11
11
|
# app.rb
|
12
12
|
|
13
|
-
|
13
|
+
get '/' do |req, res|
|
14
14
|
res.html 'Hello World'
|
15
15
|
end
|
16
16
|
```
|
17
17
|
|
18
18
|
## Content Types
|
19
19
|
|
20
|
-
|
20
|
+
Lennarb supports the following content types:
|
21
21
|
|
22
22
|
```ruby
|
23
23
|
# app.rb
|
24
24
|
|
25
|
-
|
25
|
+
get '/' do |req, res|
|
26
26
|
res.html 'Hello World'
|
27
27
|
res.json '{"message": "Hello World"}'
|
28
28
|
res.text 'Hello World'
|
@@ -43,7 +43,7 @@ You can use the `res.write` method to write to the response body:
|
|
43
43
|
```ruby
|
44
44
|
# app.rb
|
45
45
|
|
46
|
-
|
46
|
+
get '/' do |req, res|
|
47
47
|
res.write 'Hello World'
|
48
48
|
end
|
49
49
|
```
|
@@ -53,7 +53,7 @@ JSON example:
|
|
53
53
|
```ruby
|
54
54
|
# app.rb
|
55
55
|
|
56
|
-
|
56
|
+
post '/posts' do |req, res|
|
57
57
|
req.params # => { name: 'Lenna' }
|
58
58
|
name = req.params[:name]
|
59
59
|
|
@@ -76,7 +76,7 @@ You can redirect the client using the `res.redirect` method:
|
|
76
76
|
```ruby
|
77
77
|
# app.ruby
|
78
78
|
|
79
|
-
|
79
|
+
get '/' do |req, res|
|
80
80
|
# Stuff code here...
|
81
81
|
res.redirect '/hello'
|
82
82
|
end
|
data/lennarb.gemspec
CHANGED
@@ -31,14 +31,17 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.add_dependency "bigdecimal"
|
32
32
|
spec.add_dependency "colorize", "~> 1.1"
|
33
33
|
spec.add_dependency "rack", "~> 3.1"
|
34
|
+
spec.add_dependency "superconfig", "~> 3.0"
|
35
|
+
spec.add_dependency "logger", "~> 1.7"
|
34
36
|
spec.add_development_dependency "bundler"
|
35
|
-
spec.add_development_dependency "covered"
|
36
37
|
spec.add_development_dependency "simplecov"
|
38
|
+
spec.add_development_dependency "simplecov-json"
|
37
39
|
spec.add_development_dependency "minitest"
|
40
|
+
spec.add_development_dependency "minitest-utils"
|
38
41
|
spec.add_development_dependency "rack-test"
|
39
42
|
spec.add_development_dependency "rake"
|
40
43
|
spec.add_development_dependency "standard"
|
41
44
|
spec.add_development_dependency "standard-custom"
|
42
45
|
spec.add_development_dependency "standard-performance"
|
43
|
-
spec.add_development_dependency "
|
46
|
+
spec.add_development_dependency "debug" if RUBY_ENGINE == "ruby"
|
44
47
|
end
|
data/lib/lennarb/app.rb
ADDED
@@ -0,0 +1,253 @@
|
|
1
|
+
module Lennarb
|
2
|
+
# Main application class with hooks and helpers support
|
3
|
+
class App
|
4
|
+
class << self
|
5
|
+
attr_writer :app
|
6
|
+
# Rack environment variable name
|
7
|
+
#
|
8
|
+
# @return [String] The environment variable name
|
9
|
+
def app
|
10
|
+
@app ||= self
|
11
|
+
end
|
12
|
+
|
13
|
+
# Define helper methods for the application
|
14
|
+
#
|
15
|
+
# @yield Block containing helper definitions
|
16
|
+
# @return [Module] The helpers module
|
17
|
+
def helpers(mod_or_block = nil, &block)
|
18
|
+
Helpers.define(self, mod_or_block, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Define a before hook
|
22
|
+
#
|
23
|
+
# @yield [req, res] Block to execute before route
|
24
|
+
# @return [Array] The before hooks array
|
25
|
+
def before(&block)
|
26
|
+
Hooks.add(self, :before, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Define an after hook
|
30
|
+
#
|
31
|
+
# @yield [req, res] Block to execute after route
|
32
|
+
# @return [Array] The after hooks array
|
33
|
+
def after(&block)
|
34
|
+
Hooks.add(self, :after, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Get routes for this app class
|
38
|
+
#
|
39
|
+
# @return [Routes] The routes instance
|
40
|
+
def routes
|
41
|
+
@routes ||= Routes.new
|
42
|
+
end
|
43
|
+
|
44
|
+
# Set up subclass
|
45
|
+
#
|
46
|
+
# @param [Class] subclass The new subclass
|
47
|
+
# @return [void]
|
48
|
+
def inherited(subclass)
|
49
|
+
super
|
50
|
+
# Each subclass gets its own routes
|
51
|
+
subclass.instance_variable_set(:@routes, Routes.new)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Define route methods for all HTTP methods
|
55
|
+
HTTP_METHODS.each do |http_method|
|
56
|
+
define_method(http_method.downcase) do |path, &block|
|
57
|
+
routes.send(http_method.downcase, path, &block)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Define root route (GET /)
|
62
|
+
#
|
63
|
+
# @yield [req, res, params] Route block
|
64
|
+
# @return [void]
|
65
|
+
def root(&block)
|
66
|
+
get("/", &block)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Define configuration
|
70
|
+
#
|
71
|
+
# @param [Array<Symbol>] envs Environments
|
72
|
+
# @yield Configuration block
|
73
|
+
# @return [Config] The config instance
|
74
|
+
def config(*envs, &)
|
75
|
+
@config ||= Config.new(self)
|
76
|
+
|
77
|
+
if block_given?
|
78
|
+
write = envs.empty? || envs.map(&:to_sym).include?(env.name)
|
79
|
+
@config.instance_eval(&) if write
|
80
|
+
end
|
81
|
+
|
82
|
+
@config
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Instance methods
|
87
|
+
|
88
|
+
# Initialize a new app
|
89
|
+
#
|
90
|
+
# @yield [self] Configuration block
|
91
|
+
def initialize(&block)
|
92
|
+
@initialized = false
|
93
|
+
@root = Pathname.pwd
|
94
|
+
@env = Environment.new(compute_env)
|
95
|
+
|
96
|
+
instance_eval(&block) if block_given?
|
97
|
+
end
|
98
|
+
|
99
|
+
# The current environment
|
100
|
+
attr_reader :env
|
101
|
+
|
102
|
+
# The root directory
|
103
|
+
attr_accessor :root
|
104
|
+
|
105
|
+
# Set environment
|
106
|
+
#
|
107
|
+
# @param [String, Symbol] value Environment name
|
108
|
+
# @raise [AlreadyInitializedError] If already initialized
|
109
|
+
# @return [Environment] The new environment
|
110
|
+
def env=(value)
|
111
|
+
raise AlreadyInitializedError if initialized?
|
112
|
+
@env = Environment.new(value)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Get the Rack app with middleware
|
116
|
+
#
|
117
|
+
# @return [#call] The Rack app
|
118
|
+
def app
|
119
|
+
@app ||= begin
|
120
|
+
handler = build_request_handler
|
121
|
+
stack = middleware.to_a
|
122
|
+
|
123
|
+
Rack::Builder.app do
|
124
|
+
stack.each { |middleware, args, block| use(middleware, *args, &block) }
|
125
|
+
run handler
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Rack interface method
|
131
|
+
#
|
132
|
+
# @param [Hash] env Rack environment
|
133
|
+
# @return [Array] Rack response
|
134
|
+
def call(env)
|
135
|
+
env[RACK_LENNA_APP] = self
|
136
|
+
app.call(env)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Define middleware
|
140
|
+
#
|
141
|
+
# @yield Block to configure middleware
|
142
|
+
# @return [MiddlewareStack] The middleware stack
|
143
|
+
def middleware(&block)
|
144
|
+
@middleware_stack ||= default_middleware_stack
|
145
|
+
@middleware_stack.instance_eval(&block) if block_given?
|
146
|
+
@middleware_stack
|
147
|
+
end
|
148
|
+
|
149
|
+
# Define helpers (instance method)
|
150
|
+
#
|
151
|
+
# @yield Block with helper definitions
|
152
|
+
# @return [Module] The helpers module
|
153
|
+
def helpers(&block)
|
154
|
+
self.class.helpers(&block)
|
155
|
+
end
|
156
|
+
|
157
|
+
# Define before hook (instance method)
|
158
|
+
#
|
159
|
+
# @yield [req, res] Before hook block
|
160
|
+
# @return [Array] The before hooks array
|
161
|
+
def before(&block)
|
162
|
+
self.class.before(&block)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Define after hook (instance method)
|
166
|
+
#
|
167
|
+
# @yield [req, res] After hook block
|
168
|
+
# @return [Array] The after hooks array
|
169
|
+
def after(&block)
|
170
|
+
self.class.after(&block)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Get/define routes
|
174
|
+
#
|
175
|
+
# @yield Block to define routes
|
176
|
+
# @return [Routes] The routes instance
|
177
|
+
def routes(&block)
|
178
|
+
if block_given?
|
179
|
+
self.class.instance_exec(&block)
|
180
|
+
end
|
181
|
+
|
182
|
+
self.class.routes
|
183
|
+
end
|
184
|
+
|
185
|
+
# Get/define configuration
|
186
|
+
#
|
187
|
+
# @param [Array<Symbol>] envs Environments
|
188
|
+
# @yield Configuration block
|
189
|
+
# @return [Config] The config instance
|
190
|
+
def config(*envs, &)
|
191
|
+
@config ||= self.class.config
|
192
|
+
|
193
|
+
if block_given?
|
194
|
+
write = envs.empty? || envs.map(&:to_sym).include?(env.name)
|
195
|
+
@config.instance_eval(&) if write
|
196
|
+
end
|
197
|
+
|
198
|
+
@config
|
199
|
+
end
|
200
|
+
|
201
|
+
# Initialize the app
|
202
|
+
#
|
203
|
+
# @return [self] The initialized app
|
204
|
+
# @raise [AlreadyInitializedError] If already initialized
|
205
|
+
def initialize!
|
206
|
+
raise AlreadyInitializedError if @initialized
|
207
|
+
|
208
|
+
@initialized = true
|
209
|
+
routes.freeze
|
210
|
+
self
|
211
|
+
end
|
212
|
+
|
213
|
+
# Check if initialized
|
214
|
+
#
|
215
|
+
# @return [Boolean] true if initialized
|
216
|
+
def initialized?
|
217
|
+
@initialized
|
218
|
+
end
|
219
|
+
|
220
|
+
# Error for already initialized app
|
221
|
+
AlreadyInitializedError = Class.new(StandardError)
|
222
|
+
|
223
|
+
protected
|
224
|
+
|
225
|
+
# Build the request handler
|
226
|
+
#
|
227
|
+
# @return [RequestHandler] The request handler
|
228
|
+
def build_request_handler
|
229
|
+
RequestHandler.new(self)
|
230
|
+
end
|
231
|
+
|
232
|
+
# Create default middleware stack
|
233
|
+
#
|
234
|
+
# @return [MiddlewareStack] The middleware stack
|
235
|
+
private def default_middleware_stack
|
236
|
+
stack = MiddlewareStack.new
|
237
|
+
stack.use(Lennarb::Middleware::RequestLogger)
|
238
|
+
stack.use(Rack::Runtime)
|
239
|
+
stack.use(Rack::Head)
|
240
|
+
stack.use(Rack::ETag)
|
241
|
+
stack.use(Rack::ShowExceptions) if env.development?
|
242
|
+
stack
|
243
|
+
end
|
244
|
+
|
245
|
+
# Compute environment from ENV variables
|
246
|
+
#
|
247
|
+
# @return [String] Environment name
|
248
|
+
private def compute_env
|
249
|
+
env = ENV_NAMES.map { |name| ENV[name] }.compact.first.to_s
|
250
|
+
env.empty? ? "development" : env
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|