rage-rb 0.5.2 → 0.7.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/CHANGELOG.md +19 -0
- data/CODE_OF_CONDUCT.md +1 -1
- data/README.md +12 -11
- data/lib/rage/all.rb +7 -1
- data/lib/rage/application.rb +21 -3
- data/lib/rage/cli.rb +32 -8
- data/lib/rage/code_loader.rb +48 -0
- data/lib/rage/configuration.rb +122 -5
- data/lib/rage/controller/api.rb +178 -32
- data/lib/rage/errors.rb +3 -0
- data/lib/rage/fiber.rb +8 -0
- data/lib/rage/fiber_scheduler.rb +2 -2
- data/lib/rage/logger/json_formatter.rb +44 -0
- data/lib/rage/logger/logger.rb +4 -4
- data/lib/rage/logger/text_formatter.rb +8 -10
- data/lib/rage/middleware/cors.rb +139 -0
- data/lib/rage/{fiber_wrapper.rb → middleware/fiber_wrapper.rb} +4 -2
- data/lib/rage/middleware/reloader.rb +16 -0
- data/lib/rage/rails.rb +62 -0
- data/lib/rage/request.rb +51 -0
- data/lib/rage/response.rb +21 -0
- data/lib/rage/router/backend.rb +9 -1
- data/lib/rage/router/dsl.rb +3 -1
- data/lib/rage/setup.rb +4 -16
- data/lib/rage/templates/config-environments-development.rb +2 -0
- data/lib/rage/templates/config-initializers-.keep +0 -0
- data/lib/rage/version.rb +1 -1
- data/lib/rage-rb.rb +23 -1
- data/rage.gemspec +1 -0
- metadata +24 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1c3041038ae63ae245e261a7a2096195ab56291d236414e086646ae96a4a6d16
|
4
|
+
data.tar.gz: a64817ded16716fe714310c7318d8e1d318454615da43d37055b333e76778fa2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 350f5150012852a3ca2532c031ce8ef28338da6d985ef66dff59095dea6b3dbbc4498826996876722bc5b7991683fed82c2fd3854313f7ed59f9fff20aaf4315
|
7
|
+
data.tar.gz: 84cd798056a43c8eb0e94809f72f6b0ac602076aa904831a3a049a8ed13352cad9321329233063343b73e65c0fbf1521564b0cee6f3696c89877a0af15191c94
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.7.0] - 2024-01-09
|
4
|
+
|
5
|
+
- Add conditional GET using `stale?` by [@tonekk](https://github.com/tonekk) (#55).
|
6
|
+
- Add Rails integration (#57).
|
7
|
+
- Add JSON log formatter (#59).
|
8
|
+
|
9
|
+
## [0.6.0] - 2023-12-22
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- Implement after actions (#53).
|
14
|
+
- Zeitwerk autoloading and reloading by [@alex-rogachev](https://github.com/alex-rogachev) (#54).
|
15
|
+
- Support the `environment`, `binding`, `timeout`, and `max_clients` options when using `rage s` (#52).
|
16
|
+
- Add CORS middleware (#49).
|
17
|
+
|
18
|
+
### Fixed
|
19
|
+
|
20
|
+
- Prevent `block` and `sleep` channels from conflicting (#51).
|
21
|
+
|
3
22
|
## [0.5.2] - 2023-12-11
|
4
23
|
|
5
24
|
### Added
|
data/CODE_OF_CONDUCT.md
CHANGED
@@ -39,7 +39,7 @@ This Code of Conduct applies within all community spaces, and also applies when
|
|
39
39
|
|
40
40
|
## Enforcement
|
41
41
|
|
42
|
-
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at
|
42
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at developers@rage-rb.dev. All complaints will be reviewed and investigated promptly and fairly.
|
43
43
|
|
44
44
|
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
|
45
45
|
|
data/README.md
CHANGED
@@ -52,6 +52,7 @@ Check out in-depth API docs for more information:
|
|
52
52
|
- [Routing API](https://rage-rb.pages.dev/Rage/Router/DSL/Handler)
|
53
53
|
- [Fiber API](https://rage-rb.pages.dev/Fiber)
|
54
54
|
- [Logger API](https://rage-rb.pages.dev/Rage/Logger)
|
55
|
+
- [Configuration API](https://rage-rb.pages.dev/Rage/Configuration)
|
55
56
|
|
56
57
|
Also, see the [changelog](https://github.com/rage-rb/rage/blob/master/CHANGELOG.md) and [upcoming-releases](https://github.com/rage-rb/rage#upcoming-releases) for currently supported and planned features.
|
57
58
|
|
@@ -142,17 +143,17 @@ end
|
|
142
143
|
|
143
144
|
## Upcoming releases
|
144
145
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
146
|
+
Status | Changes
|
147
|
+
-- | ------------
|
148
|
+
:white_check_mark: | ~~Gem configuration by env.<br>Add `skip_before_action`.<br>Add `rescue_from`.<br>Router updates:<br> • make the `root` helper work correctly with `scope`;<br> • support the `defaults` option;~~
|
149
|
+
:white_check_mark: | ~~CLI updates:<br> • `routes` task;<br> • `console` task;<br>Support the `:if` and `:unless` options in `before_action`.<br>Allow to set response headers.~~
|
150
|
+
:white_check_mark: | ~~Expose the `params` object.<br>Support header authentication with `authenticate_with_http_token`.<br>Router updates:<br> • add the `resources` route helper;<br> • add the `namespace` route helper;~~
|
151
|
+
:white_check_mark: | ~~Add request logging.~~
|
152
|
+
:white_check_mark: | ~~Automatic code reloading in development with Zeitwerk.~~
|
153
|
+
⏳ | Expose the `send_data` and `send_file` methods.
|
154
|
+
⏳ | Support conditional get with `etag` and `last_modified`.
|
155
|
+
⏳ | Expose the `cookies` and `session` objects.
|
156
|
+
⏳ | Implement Iodine-based equivalent of Action Cable.
|
156
157
|
|
157
158
|
## Development
|
158
159
|
|
data/lib/rage/all.rb
CHANGED
@@ -6,10 +6,11 @@ require_relative "fiber"
|
|
6
6
|
require_relative "fiber_scheduler"
|
7
7
|
require_relative "configuration"
|
8
8
|
require_relative "request"
|
9
|
+
require_relative "response"
|
9
10
|
require_relative "uploaded_file"
|
10
11
|
require_relative "errors"
|
11
12
|
require_relative "params_parser"
|
12
|
-
require_relative "
|
13
|
+
require_relative "code_loader"
|
13
14
|
|
14
15
|
require_relative "router/strategies/host"
|
15
16
|
require_relative "router/backend"
|
@@ -21,8 +22,13 @@ require_relative "router/node"
|
|
21
22
|
require_relative "controller/api"
|
22
23
|
|
23
24
|
require_relative "logger/text_formatter"
|
25
|
+
require_relative "logger/json_formatter"
|
24
26
|
require_relative "logger/logger"
|
25
27
|
|
28
|
+
require_relative "middleware/fiber_wrapper"
|
29
|
+
require_relative "middleware/cors"
|
30
|
+
require_relative "middleware/reloader"
|
31
|
+
|
26
32
|
if defined?(Sidekiq)
|
27
33
|
require_relative "sidekiq_session"
|
28
34
|
end
|
data/lib/rage/application.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
class Rage::Application
|
4
4
|
def initialize(router)
|
5
5
|
@router = router
|
6
|
+
@exception_app = build_exception_app
|
6
7
|
end
|
7
8
|
|
8
9
|
def call(env)
|
@@ -17,10 +18,11 @@ class Rage::Application
|
|
17
18
|
[404, {}, ["Not Found"]]
|
18
19
|
end
|
19
20
|
|
21
|
+
rescue Rage::Errors::BadRequest => e
|
22
|
+
response = @exception_app.call(400, e)
|
23
|
+
|
20
24
|
rescue Exception => e
|
21
|
-
|
22
|
-
Rage.logger.error(exception_str)
|
23
|
-
response = [500, {}, [exception_str]]
|
25
|
+
response = @exception_app.call(500, e)
|
24
26
|
|
25
27
|
ensure
|
26
28
|
finalize_logger(env, response, params)
|
@@ -50,4 +52,20 @@ class Rage::Application
|
|
50
52
|
Rage.logger.info("")
|
51
53
|
logger[:final] = nil
|
52
54
|
end
|
55
|
+
|
56
|
+
def build_exception_app
|
57
|
+
if Rage.env.development?
|
58
|
+
->(status, e) do
|
59
|
+
exception_str = "#{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}"
|
60
|
+
Rage.logger.error(exception_str)
|
61
|
+
[status, {}, [exception_str]]
|
62
|
+
end
|
63
|
+
else
|
64
|
+
->(status, e) do
|
65
|
+
exception_str = "#{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}"
|
66
|
+
Rage.logger.error(exception_str)
|
67
|
+
[status, {}, []]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
53
71
|
end
|
data/lib/rage/cli.rb
CHANGED
@@ -16,18 +16,24 @@ module Rage
|
|
16
16
|
|
17
17
|
desc "s", "Start the app server."
|
18
18
|
option :port, aliases: "-p", desc: "Runs Rage on the specified port - defaults to 3000."
|
19
|
+
option :environment, aliases: "-e", desc: "Specifies the environment to run this server under (test/development/production)."
|
20
|
+
option :binding, aliases: "-b", desc: "Binds Rails to the specified IP - defaults to 'localhost' in development and '0.0.0.0' in other environments."
|
21
|
+
option :config, aliases: "-c", desc: "Uses a custom rack configuration."
|
22
|
+
option :help, aliases: "-h", desc: "Show this message."
|
19
23
|
def server
|
20
|
-
|
24
|
+
return help("server") if options.help?
|
25
|
+
|
26
|
+
set_env(options)
|
27
|
+
|
28
|
+
app = ::Rack::Builder.parse_file(options[:config] || "config.ru")
|
21
29
|
app = app[0] if app.is_a?(Array)
|
22
30
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
ERR
|
28
|
-
end
|
31
|
+
port = options[:port] || Rage.config.server.port
|
32
|
+
address = options[:binding] || (Rage.env.production? ? "0.0.0.0" : "localhost")
|
33
|
+
timeout = Rage.config.server.timeout
|
34
|
+
max_clients = Rage.config.server.max_clients
|
29
35
|
|
30
|
-
::Iodine.listen service: :http, handler: app, port:
|
36
|
+
::Iodine.listen service: :http, handler: app, port: port, address: address, timeout: timeout, max_clients: max_clients
|
31
37
|
::Iodine.threads = Rage.config.server.threads_count
|
32
38
|
::Iodine.workers = Rage.config.server.workers_count
|
33
39
|
|
@@ -36,12 +42,15 @@ module Rage
|
|
36
42
|
|
37
43
|
desc 'routes', 'List all routes.'
|
38
44
|
option :grep, aliases: "-g", desc: "Filter routes by pattern"
|
45
|
+
option :help, aliases: "-h", desc: "Show this message."
|
39
46
|
def routes
|
47
|
+
return help("routes") if options.help?
|
40
48
|
# the result would be something like this:
|
41
49
|
# Verb Path Controller#Action
|
42
50
|
# GET / application#index
|
43
51
|
|
44
52
|
# load config/application.rb
|
53
|
+
set_env(options)
|
45
54
|
environment
|
46
55
|
|
47
56
|
routes = Rage.__router.routes
|
@@ -90,7 +99,12 @@ module Rage
|
|
90
99
|
end
|
91
100
|
|
92
101
|
desc "c", "Start the app console."
|
102
|
+
option :help, aliases: "-h", desc: "Show this message."
|
93
103
|
def console
|
104
|
+
return help("console") if options.help?
|
105
|
+
|
106
|
+
set_env(options)
|
107
|
+
|
94
108
|
require "irb"
|
95
109
|
environment
|
96
110
|
ARGV.clear
|
@@ -101,6 +115,16 @@ module Rage
|
|
101
115
|
|
102
116
|
def environment
|
103
117
|
require File.expand_path("config/application.rb", Dir.pwd)
|
118
|
+
|
119
|
+
# in Rails mode we delegate code loading to Rails, and thus need
|
120
|
+
# to manually load application code for CLI utilities to work
|
121
|
+
if Rage.config.internal.rails_mode
|
122
|
+
require "rage/setup"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def set_env(options)
|
127
|
+
ENV["RAGE_ENV"] = options[:environment] || ENV["RAGE_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
104
128
|
end
|
105
129
|
end
|
106
130
|
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "zeitwerk"
|
4
|
+
|
5
|
+
class Rage::CodeLoader
|
6
|
+
def initialize
|
7
|
+
@reloading = false
|
8
|
+
end
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@loader = Zeitwerk::Loader.new
|
12
|
+
|
13
|
+
autoload_path = "#{Rage.root}/app"
|
14
|
+
enable_reloading = Rage.env.development?
|
15
|
+
enable_eager_loading = !Rage.env.development? && !Rage.env.test?
|
16
|
+
|
17
|
+
@loader.push_dir(autoload_path)
|
18
|
+
# The first level of directories in app directory won't be treated as modules
|
19
|
+
# e.g. app/controllers/pages_controller.rb will be linked to PagesController class
|
20
|
+
# instead of Controllers::PagesController
|
21
|
+
@loader.collapse("#{Rage.root}/app/*")
|
22
|
+
@loader.enable_reloading if enable_reloading
|
23
|
+
@loader.setup
|
24
|
+
@loader.eager_load if enable_eager_loading
|
25
|
+
end
|
26
|
+
|
27
|
+
# in standalone mode - reload the code and the routes
|
28
|
+
def reload
|
29
|
+
return unless @loader
|
30
|
+
|
31
|
+
@reloading = true
|
32
|
+
@loader.reload
|
33
|
+
Rage.__router.reset_routes
|
34
|
+
load("#{Rage.root}/config/routes.rb")
|
35
|
+
end
|
36
|
+
|
37
|
+
# in Rails mode - reset the routes; everything else will be done by Rails
|
38
|
+
def rails_mode_reload
|
39
|
+
return if @loader
|
40
|
+
|
41
|
+
@reloading = true
|
42
|
+
Rage.__router.reset_routes
|
43
|
+
end
|
44
|
+
|
45
|
+
def reloading?
|
46
|
+
@reloading
|
47
|
+
end
|
48
|
+
end
|
data/lib/rage/configuration.rb
CHANGED
@@ -1,9 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# # General Configuration
|
5
|
+
#
|
6
|
+
# • _config.logger_
|
7
|
+
#
|
8
|
+
# > The logger that will be used for `Rage.logger` and any related `Rage` logging. Custom loggers should implement Ruby's {https://ruby-doc.org/3.2.2/stdlibs/logger/Logger.html#class-Logger-label-Entries Logger} interface.
|
9
|
+
#
|
10
|
+
# • _config.log_formatter_
|
11
|
+
#
|
12
|
+
# > The formatter of the Rage logger. Built in options include `Rage::TextFormatter` and `Rage::JSONFormatter`. Defaults to an instance of `Rage::TextFormatter`.
|
13
|
+
#
|
14
|
+
# • _config.log_level_
|
15
|
+
#
|
16
|
+
# > Defines the verbosity of the Rage logger. This option defaults to `:debug` for all environments except production, where it defaults to `:info`. The available log levels are: `:debug`, `:info`, `:warn`, `:error`, `:fatal`, and `:unknown`.
|
17
|
+
#
|
18
|
+
# # Middleware Configuration
|
19
|
+
#
|
20
|
+
# • _config.middleware.use_
|
21
|
+
#
|
22
|
+
# > Adds a middleware to the top of the middleware stack. **This is the preferred way of adding a middleware.**
|
23
|
+
# > ```
|
24
|
+
# config.middleware.use Rack::Cors do
|
25
|
+
# allow do
|
26
|
+
# origins "*"
|
27
|
+
# resource "*", headers: :any
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
# > ```
|
31
|
+
#
|
32
|
+
# • _config.middleware.insert_before_
|
33
|
+
#
|
34
|
+
# > Adds middleware at a specified position before another middleware. The position can be either an index or another middleware.
|
35
|
+
#
|
36
|
+
# > **_❗️Heads up:_** By default, Rage always uses the `Rage::FiberWrapper` middleware, which wraps every request in a separate fiber. Make sure to always have this middleware in the top of the stack. Placing other middlewares in front may lead to undefined behavior.
|
37
|
+
#
|
38
|
+
# > ```
|
39
|
+
# config.middleware.insert_before Rack::Head, Magical::Unicorns
|
40
|
+
# config.middleware.insert_before 0, Magical::Unicorns
|
41
|
+
# > ```
|
42
|
+
#
|
43
|
+
# • _config.middleware.insert_after_
|
44
|
+
#
|
45
|
+
# > Adds middleware at a specified position after another middleware. The position can be either an index or another middleware.
|
46
|
+
#
|
47
|
+
# > ```
|
48
|
+
# config.middleware.insert_after Rack::Head, Magical::Unicorns
|
49
|
+
# > ```
|
50
|
+
#
|
51
|
+
# # Server Configuration
|
52
|
+
#
|
53
|
+
# _• config.server.max_clients_
|
54
|
+
#
|
55
|
+
# > Limits the number of simultaneous connections the server can accept. Defaults to the maximum number of open files.
|
56
|
+
#
|
57
|
+
# > **_❗️Heads up:_** Decreasing this number is almost never a good idea. Depending on your application specifics, you are encouraged to use other methods to limit the number of concurrent connections:
|
58
|
+
#
|
59
|
+
# > 1. If your application is exposed to the public, you may want to use a cloud rate limiter, like {https://developers.cloudflare.com/waf Cloudflare WAF} or {https://docs.fastly.com/en/ngwaf Fastly WAF}.
|
60
|
+
# > 2. Otherwise, consider using tools like {https://github.com/rack/rack-attack Rack::Attack} or {https://github.com/mperham/connection_pool connection_pool}.
|
61
|
+
#
|
62
|
+
# > ```
|
63
|
+
# # Limit the amount of connections your application can accept
|
64
|
+
# config.middleware.use Rack::Attack
|
65
|
+
# Rack::Attack.throttle("req/ip", limit: 300, period: 5.minutes) do |req|
|
66
|
+
# req.ip
|
67
|
+
# end
|
68
|
+
# #
|
69
|
+
# # Limit the amount of connections to a specific resource
|
70
|
+
# HTTP = ConnectionPool.new(size: 5, timeout: 5) { Net::HTTP }
|
71
|
+
# HTTP.with do |conn|
|
72
|
+
# conn.get("/my-resource")
|
73
|
+
# end
|
74
|
+
# > ```
|
75
|
+
#
|
76
|
+
# • _config.server.port_
|
77
|
+
#
|
78
|
+
# > Specifies what port the server will listen on.
|
79
|
+
#
|
80
|
+
# • _config.server.workers_count_
|
81
|
+
#
|
82
|
+
# > Specifies the number of server processes to run.
|
83
|
+
#
|
84
|
+
# • _config.server.timeout_
|
85
|
+
#
|
86
|
+
# > Specifies connection timeout.
|
87
|
+
#
|
1
88
|
class Rage::Configuration
|
2
|
-
attr_accessor :logger
|
89
|
+
attr_accessor :logger
|
90
|
+
attr_reader :log_formatter, :log_level
|
3
91
|
|
4
92
|
# used in DSL
|
5
93
|
def config = self
|
6
94
|
|
95
|
+
def log_formatter=(formatter)
|
96
|
+
raise "Custom log formatter should respond to `#call`" unless formatter.respond_to?(:call)
|
97
|
+
@log_formatter = formatter
|
98
|
+
end
|
99
|
+
|
100
|
+
def log_level=(level)
|
101
|
+
@log_level = level.is_a?(Symbol) ? Logger.const_get(level.to_s.upcase) : level
|
102
|
+
end
|
103
|
+
|
7
104
|
def server
|
8
105
|
@server ||= Server.new
|
9
106
|
end
|
@@ -12,8 +109,12 @@ class Rage::Configuration
|
|
12
109
|
@middleware ||= Middleware.new
|
13
110
|
end
|
14
111
|
|
112
|
+
def internal
|
113
|
+
@internal ||= Internal.new
|
114
|
+
end
|
115
|
+
|
15
116
|
class Server
|
16
|
-
attr_accessor :port, :workers_count
|
117
|
+
attr_accessor :port, :workers_count, :timeout, :max_clients
|
17
118
|
attr_reader :threads_count
|
18
119
|
|
19
120
|
def initialize
|
@@ -36,6 +137,9 @@ class Rage::Configuration
|
|
36
137
|
|
37
138
|
def insert_before(existing_middleware, new_middleware, *args, &block)
|
38
139
|
index = find_middleware_index(existing_middleware)
|
140
|
+
if index == 0 && @middlewares[0][0] == Rage::FiberWrapper
|
141
|
+
puts("Warning: inserting #{new_middleware} before Rage::FiberWrapper may lead to undefined behavior.")
|
142
|
+
end
|
39
143
|
@middlewares = (@middlewares[0...index] + [[new_middleware, args, block]] + @middlewares[index..]).uniq(&:first)
|
40
144
|
end
|
41
145
|
|
@@ -60,9 +164,22 @@ class Rage::Configuration
|
|
60
164
|
end
|
61
165
|
end
|
62
166
|
|
167
|
+
# @private
|
168
|
+
class Internal
|
169
|
+
attr_accessor :rails_mode, :rails_console
|
170
|
+
|
171
|
+
def inspect
|
172
|
+
"#<#{self.class.name}>"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# @private
|
63
177
|
def __finalize
|
64
|
-
@logger
|
65
|
-
|
66
|
-
|
178
|
+
if @logger
|
179
|
+
@logger.formatter = @log_formatter if @log_formatter
|
180
|
+
@logger.level = @log_level if @log_level
|
181
|
+
else
|
182
|
+
@logger = Rage::Logger.new(nil)
|
183
|
+
end
|
67
184
|
end
|
68
185
|
end
|