rage-rb 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -0
- data/CODE_OF_CONDUCT.md +1 -1
- data/README.md +4 -1
- data/lib/rage/all.rb +5 -0
- data/lib/rage/application.rb +14 -27
- data/lib/rage/cli.rb +25 -1
- data/lib/rage/code_loader.rb +36 -0
- data/lib/rage/configuration.rb +150 -5
- data/lib/rage/controller/api.rb +81 -23
- data/lib/rage/env.rb +34 -0
- data/lib/rage/errors.rb +3 -0
- data/lib/rage/fiber.rb +18 -0
- data/lib/rage/fiber_scheduler.rb +2 -2
- data/lib/rage/logger/logger.rb +2 -2
- data/lib/rage/middleware/cors.rb +137 -0
- data/lib/rage/middleware/fiber_wrapper.rb +30 -0
- data/lib/rage/middleware/reloader.rb +16 -0
- data/lib/rage/router/backend.rb +9 -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/templates/config.ru +1 -0
- data/lib/rage/version.rb +1 -1
- data/lib/rage-rb.rb +13 -1
- data/rage.gemspec +2 -1
- metadata +24 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0eed317e69386a2c2cc765df7be687be754934801390e96013902e78670171eb
|
4
|
+
data.tar.gz: 44c899c5843d112ca222df4b863852df9e3728a63954ee3b5ab4ffd2cffd4e2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e158fd8202e9c6e4f94a7b243383c34f2179306a10724978083b6599197197221915857540ae2beb5ea98a5f602c6507bec89d192a57706b3aae2203c75a8a3
|
7
|
+
data.tar.gz: e9723bb6a0ef7b1a67db08799e3429ae4c2e9f88894afb9e614645673df7cb4e019107f0ca1339eb521fa7958fd259ca032385f98fdbddacd59415d960f0c347
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,28 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.6.0] - 2023-12-22
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- Implement after actions (#53).
|
8
|
+
- Zeitwerk autoloading and reloading by [@alex-rogachev](https://github.com/alex-rogachev) (#54).
|
9
|
+
- Support the `environment`, `binding`, `timeout`, and `max_clients` options when using `rage s` (#52).
|
10
|
+
- Add CORS middleware (#49).
|
11
|
+
|
12
|
+
### Fixed
|
13
|
+
|
14
|
+
- Prevent `block` and `sleep` channels from conflicting (#51).
|
15
|
+
|
16
|
+
## [0.5.2] - 2023-12-11
|
17
|
+
|
18
|
+
### Added
|
19
|
+
|
20
|
+
- Add env class (#43).
|
21
|
+
|
22
|
+
### Changed
|
23
|
+
|
24
|
+
- Schedule request Fibers in a separate middleware (#48).
|
25
|
+
|
3
26
|
## [0.5.1] - 2023-12-01
|
4
27
|
|
5
28
|
### Fixed
|
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
@@ -4,6 +4,8 @@
|
|
4
4
|
|
5
5
|
[![Gem Version](https://badge.fury.io/rb/rage-rb.svg)](https://badge.fury.io/rb/rage-rb)
|
6
6
|
![Tests](https://github.com/rage-rb/rage/actions/workflows/main.yml/badge.svg)
|
7
|
+
![Ruby Requirement](https://img.shields.io/badge/Ruby-3.1%2B-%23f40000)
|
8
|
+
|
7
9
|
|
8
10
|
Inspired by [Deno](https://deno.com) and built on top of [Iodine](https://github.com/rage-rb/iodine), this is a Ruby web framework that is based on the following design principles:
|
9
11
|
|
@@ -11,7 +13,7 @@ Inspired by [Deno](https://deno.com) and built on top of [Iodine](https://github
|
|
11
13
|
|
12
14
|
* **High performance** - some think performance is not a major metric for a framework, but it's not true. Poor performance is a risk, and in today's world, companies refuse to use risky technologies.
|
13
15
|
|
14
|
-
* **API-only** -
|
16
|
+
* **API-only** - separation of concerns is one of the most fundamental principles in software development. Backend and frontend are very different layers with different goals and paths to those goals. Separating BE code from FE code results in a much more sustainable architecture compared with classic Rails monoliths.
|
15
17
|
|
16
18
|
* **Acceptance of modern Ruby** - the framework includes a fiber scheduler, which means your code never blocks while waiting on IO.
|
17
19
|
|
@@ -50,6 +52,7 @@ Check out in-depth API docs for more information:
|
|
50
52
|
- [Routing API](https://rage-rb.pages.dev/Rage/Router/DSL/Handler)
|
51
53
|
- [Fiber API](https://rage-rb.pages.dev/Fiber)
|
52
54
|
- [Logger API](https://rage-rb.pages.dev/Rage/Logger)
|
55
|
+
- [Configuration API](https://rage-rb.pages.dev/Rage/Configuration)
|
53
56
|
|
54
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.
|
55
58
|
|
data/lib/rage/all.rb
CHANGED
@@ -9,6 +9,7 @@ require_relative "request"
|
|
9
9
|
require_relative "uploaded_file"
|
10
10
|
require_relative "errors"
|
11
11
|
require_relative "params_parser"
|
12
|
+
require_relative "code_loader"
|
12
13
|
|
13
14
|
require_relative "router/strategies/host"
|
14
15
|
require_relative "router/backend"
|
@@ -22,6 +23,10 @@ require_relative "controller/api"
|
|
22
23
|
require_relative "logger/text_formatter"
|
23
24
|
require_relative "logger/logger"
|
24
25
|
|
26
|
+
require_relative "middleware/fiber_wrapper"
|
27
|
+
require_relative "middleware/cors"
|
28
|
+
require_relative "middleware/reloader"
|
29
|
+
|
25
30
|
if defined?(Sidekiq)
|
26
31
|
require_relative "sidekiq_session"
|
27
32
|
end
|
data/lib/rage/application.rb
CHANGED
@@ -2,41 +2,28 @@
|
|
2
2
|
|
3
3
|
class Rage::Application
|
4
4
|
def initialize(router)
|
5
|
-
Iodine.on_state(:on_start) do
|
6
|
-
Fiber.set_scheduler(Rage::FiberScheduler.new)
|
7
|
-
end
|
8
5
|
@router = router
|
9
6
|
end
|
10
7
|
|
11
8
|
def call(env)
|
12
|
-
|
13
|
-
init_logger
|
14
|
-
|
15
|
-
handler = @router.lookup(env)
|
9
|
+
init_logger
|
16
10
|
|
17
|
-
|
18
|
-
params = Rage::ParamsParser.prepare(env, handler[:params])
|
19
|
-
handler[:handler].call(env, params)
|
20
|
-
else
|
21
|
-
[404, {}, ["Not Found"]]
|
22
|
-
end
|
11
|
+
handler = @router.lookup(env)
|
23
12
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
response = [500, {}, [exception_str]]
|
28
|
-
|
29
|
-
ensure
|
30
|
-
finalize_logger(env, response, params)
|
31
|
-
Iodine.publish(env["IODINE_REQUEST_ID"], "") # notify Iodine the request can now be served
|
32
|
-
end
|
33
|
-
|
34
|
-
# the fiber encountered blocking IO and yielded; instruct Iodine to pause the request;
|
35
|
-
if fiber.alive?
|
36
|
-
[:__http_defer__, fiber]
|
13
|
+
response = if handler
|
14
|
+
params = Rage::ParamsParser.prepare(env, handler[:params])
|
15
|
+
handler[:handler].call(env, params)
|
37
16
|
else
|
38
|
-
|
17
|
+
[404, {}, ["Not Found"]]
|
39
18
|
end
|
19
|
+
|
20
|
+
rescue Exception => e
|
21
|
+
exception_str = "#{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}"
|
22
|
+
Rage.logger.error(exception_str)
|
23
|
+
response = [500, {}, [exception_str]]
|
24
|
+
|
25
|
+
ensure
|
26
|
+
finalize_logger(env, response, params)
|
40
27
|
end
|
41
28
|
|
42
29
|
private
|
data/lib/rage/cli.rb
CHANGED
@@ -16,11 +16,23 @@ 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 :help, aliases: "-h", desc: "Show this message."
|
19
22
|
def server
|
23
|
+
return help("server") if options.help?
|
24
|
+
|
25
|
+
set_env(options)
|
26
|
+
|
20
27
|
app = ::Rack::Builder.parse_file("config.ru")
|
21
28
|
app = app[0] if app.is_a?(Array)
|
22
29
|
|
23
|
-
|
30
|
+
port = options[:port] || Rage.config.server.port
|
31
|
+
address = options[:binding] || (Rage.env.production? ? "0.0.0.0" : "localhost")
|
32
|
+
timeout = Rage.config.server.timeout
|
33
|
+
max_clients = Rage.config.server.max_clients
|
34
|
+
|
35
|
+
::Iodine.listen service: :http, handler: app, port: port, address: address, timeout: timeout, max_clients: max_clients
|
24
36
|
::Iodine.threads = Rage.config.server.threads_count
|
25
37
|
::Iodine.workers = Rage.config.server.workers_count
|
26
38
|
|
@@ -29,12 +41,15 @@ module Rage
|
|
29
41
|
|
30
42
|
desc 'routes', 'List all routes.'
|
31
43
|
option :grep, aliases: "-g", desc: "Filter routes by pattern"
|
44
|
+
option :help, aliases: "-h", desc: "Show this message."
|
32
45
|
def routes
|
46
|
+
return help("routes") if options.help?
|
33
47
|
# the result would be something like this:
|
34
48
|
# Verb Path Controller#Action
|
35
49
|
# GET / application#index
|
36
50
|
|
37
51
|
# load config/application.rb
|
52
|
+
set_env(options)
|
38
53
|
environment
|
39
54
|
|
40
55
|
routes = Rage.__router.routes
|
@@ -83,7 +98,12 @@ module Rage
|
|
83
98
|
end
|
84
99
|
|
85
100
|
desc "c", "Start the app console."
|
101
|
+
option :help, aliases: "-h", desc: "Show this message."
|
86
102
|
def console
|
103
|
+
return help("console") if options.help?
|
104
|
+
|
105
|
+
set_env(options)
|
106
|
+
|
87
107
|
require "irb"
|
88
108
|
environment
|
89
109
|
ARGV.clear
|
@@ -95,6 +115,10 @@ module Rage
|
|
95
115
|
def environment
|
96
116
|
require File.expand_path("config/application.rb", Dir.pwd)
|
97
117
|
end
|
118
|
+
|
119
|
+
def set_env(options)
|
120
|
+
ENV["RAGE_ENV"] = options[:environment] || ENV["RAGE_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
121
|
+
end
|
98
122
|
end
|
99
123
|
|
100
124
|
class NewAppGenerator < Thor::Group
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "zeitwerk"
|
4
|
+
|
5
|
+
class Rage::CodeLoader
|
6
|
+
def initialize
|
7
|
+
@loader = Zeitwerk::Loader.new
|
8
|
+
@reloading = false
|
9
|
+
end
|
10
|
+
|
11
|
+
def setup
|
12
|
+
autoload_path = "#{Rage.root}/app"
|
13
|
+
enable_reloading = Rage.env.development?
|
14
|
+
enable_eager_loading = !Rage.env.development? && !Rage.env.test?
|
15
|
+
|
16
|
+
@loader.push_dir(autoload_path)
|
17
|
+
# The first level of directories in app directory won't be treated as modules
|
18
|
+
# e.g. app/controllers/pages_controller.rb will be linked to PagesController class
|
19
|
+
# instead of Controllers::PagesController
|
20
|
+
@loader.collapse("#{Rage.root}/app/*")
|
21
|
+
@loader.enable_reloading if enable_reloading
|
22
|
+
@loader.setup
|
23
|
+
@loader.eager_load if enable_eager_loading
|
24
|
+
end
|
25
|
+
|
26
|
+
def reload
|
27
|
+
@reloading = true
|
28
|
+
@loader.reload
|
29
|
+
Rage.__router.reset_routes
|
30
|
+
load("#{Rage.root}/config/routes.rb")
|
31
|
+
end
|
32
|
+
|
33
|
+
def reloading?
|
34
|
+
@reloading
|
35
|
+
end
|
36
|
+
end
|
data/lib/rage/configuration.rb
CHANGED
@@ -1,15 +1,116 @@
|
|
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
|
10
107
|
|
108
|
+
def middleware
|
109
|
+
@middleware ||= Middleware.new
|
110
|
+
end
|
111
|
+
|
11
112
|
class Server
|
12
|
-
attr_accessor :port, :workers_count
|
113
|
+
attr_accessor :port, :workers_count, :timeout, :max_clients
|
13
114
|
attr_reader :threads_count
|
14
115
|
|
15
116
|
def initialize
|
@@ -19,9 +120,53 @@ class Rage::Configuration
|
|
19
120
|
end
|
20
121
|
end
|
21
122
|
|
123
|
+
class Middleware
|
124
|
+
attr_reader :middlewares
|
125
|
+
|
126
|
+
def initialize
|
127
|
+
@middlewares = [[Rage::FiberWrapper]]
|
128
|
+
end
|
129
|
+
|
130
|
+
def use(new_middleware, *args, &block)
|
131
|
+
insert_after(@middlewares.length - 1, new_middleware, *args, &block)
|
132
|
+
end
|
133
|
+
|
134
|
+
def insert_before(existing_middleware, new_middleware, *args, &block)
|
135
|
+
index = find_middleware_index(existing_middleware)
|
136
|
+
if index == 0 && @middlewares[0][0] == Rage::FiberWrapper
|
137
|
+
puts("Warning: inserting #{new_middleware} before Rage::FiberWrapper may lead to undefined behavior.")
|
138
|
+
end
|
139
|
+
@middlewares = (@middlewares[0...index] + [[new_middleware, args, block]] + @middlewares[index..]).uniq(&:first)
|
140
|
+
end
|
141
|
+
|
142
|
+
def insert_after(existing_middleware, new_middleware, *args, &block)
|
143
|
+
index = find_middleware_index(existing_middleware)
|
144
|
+
@middlewares = (@middlewares[0..index] + [[new_middleware, args, block]] + @middlewares[index + 1..]).uniq(&:first)
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def find_middleware_index(middleware)
|
150
|
+
if middleware.is_a?(Integer)
|
151
|
+
if middleware < 0 || middleware >= @middlewares.length
|
152
|
+
raise ArgumentError, "Middleware index should be in the (0...#{@middlewares.length}) range"
|
153
|
+
end
|
154
|
+
middleware
|
155
|
+
else
|
156
|
+
@middlewares.index { |m, _, _| m == middleware }.tap do |i|
|
157
|
+
raise ArgumentError, "Couldn't find #{middleware} in the middleware stack" unless i
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# @private
|
22
164
|
def __finalize
|
23
|
-
@logger
|
24
|
-
|
25
|
-
|
165
|
+
if @logger
|
166
|
+
@logger.formatter = @log_formatter if @log_formatter
|
167
|
+
@logger.level = @log_level if @log_level
|
168
|
+
else
|
169
|
+
@logger = Rage::Logger.new(nil)
|
170
|
+
end
|
26
171
|
end
|
27
172
|
end
|
data/lib/rage/controller/api.rb
CHANGED
@@ -8,7 +8,7 @@ class RageController::API
|
|
8
8
|
# sends a correct response down to the server;
|
9
9
|
# returns the name of the newly defined method;
|
10
10
|
def __register_action(action)
|
11
|
-
raise "The action '#{action}' could not be found for #{self}" unless method_defined?(action)
|
11
|
+
raise Rage::Errors::RouterError, "The action '#{action}' could not be found for #{self}" unless method_defined?(action)
|
12
12
|
|
13
13
|
before_actions_chunk = if @__before_actions
|
14
14
|
filtered_before_actions = @__before_actions.select do |h|
|
@@ -25,7 +25,7 @@ class RageController::API
|
|
25
25
|
"unless #{h[:unless]}"
|
26
26
|
end
|
27
27
|
|
28
|
-
|
28
|
+
<<~RUBY
|
29
29
|
#{h[:name]} #{condition}
|
30
30
|
return [@__status, @__headers, @__body] if @__rendered
|
31
31
|
RUBY
|
@@ -36,9 +36,34 @@ class RageController::API
|
|
36
36
|
""
|
37
37
|
end
|
38
38
|
|
39
|
+
after_actions_chunk = if @__after_actions
|
40
|
+
filtered_after_actions = @__after_actions.select do |h|
|
41
|
+
(!h[:only] || h[:only].include?(action)) &&
|
42
|
+
(!h[:except] || !h[:except].include?(action))
|
43
|
+
end
|
44
|
+
|
45
|
+
lines = filtered_after_actions.map! do |h|
|
46
|
+
condition = if h[:if] && h[:unless]
|
47
|
+
"if #{h[:if]} && !#{h[:unless]}"
|
48
|
+
elsif h[:if]
|
49
|
+
"if #{h[:if]}"
|
50
|
+
elsif h[:unless]
|
51
|
+
"unless #{h[:unless]}"
|
52
|
+
end
|
53
|
+
|
54
|
+
<<~RUBY
|
55
|
+
#{h[:name]} #{condition}
|
56
|
+
RUBY
|
57
|
+
end
|
58
|
+
|
59
|
+
lines.join("\n")
|
60
|
+
else
|
61
|
+
""
|
62
|
+
end
|
63
|
+
|
39
64
|
rescue_handlers_chunk = if @__rescue_handlers
|
40
65
|
lines = @__rescue_handlers.map do |klasses, handler|
|
41
|
-
|
66
|
+
<<~RUBY
|
42
67
|
rescue #{klasses.join(", ")} => __e
|
43
68
|
#{handler}(__e)
|
44
69
|
[@__status, @__headers, @__body]
|
@@ -50,11 +75,18 @@ class RageController::API
|
|
50
75
|
""
|
51
76
|
end
|
52
77
|
|
53
|
-
class_eval
|
78
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
54
79
|
def __run_#{action}
|
55
80
|
#{before_actions_chunk}
|
56
81
|
#{action}
|
57
82
|
|
83
|
+
#{if !after_actions_chunk.empty?
|
84
|
+
<<~RUBY
|
85
|
+
@__rendered = true
|
86
|
+
#{after_actions_chunk}
|
87
|
+
RUBY
|
88
|
+
end}
|
89
|
+
|
58
90
|
[@__status, @__headers, @__body]
|
59
91
|
|
60
92
|
#{rescue_handlers_chunk}
|
@@ -63,13 +95,14 @@ class RageController::API
|
|
63
95
|
end
|
64
96
|
|
65
97
|
# @private
|
66
|
-
attr_writer :__before_actions, :__rescue_handlers
|
98
|
+
attr_writer :__before_actions, :__after_actions, :__rescue_handlers
|
67
99
|
|
68
100
|
# @private
|
69
101
|
# pass the variable down to the child; the child will continue to use it until changes need to be made;
|
70
102
|
# only then the object will be copied; the frozen state communicates that the object is shared with the parent;
|
71
103
|
def inherited(klass)
|
72
104
|
klass.__before_actions = @__before_actions.freeze
|
105
|
+
klass.__after_actions = @__after_actions.freeze
|
73
106
|
klass.__rescue_handlers = @__rescue_handlers.freeze
|
74
107
|
end
|
75
108
|
|
@@ -148,29 +181,12 @@ class RageController::API
|
|
148
181
|
# end
|
149
182
|
# @note The block form doesn't receive an argument and is executed on the controller level as if it was a regular method.
|
150
183
|
def before_action(action_name = nil, **opts, &block)
|
151
|
-
|
152
|
-
action_name = define_tmp_method(block)
|
153
|
-
elsif action_name.nil?
|
154
|
-
raise "No handler provided. Pass the `action_name` parameter or provide a block."
|
155
|
-
end
|
156
|
-
|
157
|
-
_only, _except, _if, _unless = opts.values_at(:only, :except, :if, :unless)
|
184
|
+
action = prepare_action_params(action_name, **opts, &block)
|
158
185
|
|
159
186
|
if @__before_actions && @__before_actions.frozen?
|
160
187
|
@__before_actions = @__before_actions.dup
|
161
188
|
end
|
162
189
|
|
163
|
-
action = {
|
164
|
-
name: action_name,
|
165
|
-
only: _only && Array(_only),
|
166
|
-
except: _except && Array(_except),
|
167
|
-
if: _if,
|
168
|
-
unless: _unless
|
169
|
-
}
|
170
|
-
|
171
|
-
action[:if] = define_tmp_method(action[:if]) if action[:if].is_a?(Proc)
|
172
|
-
action[:unless] = define_tmp_method(action[:unless]) if action[:unless].is_a?(Proc)
|
173
|
-
|
174
190
|
if @__before_actions.nil?
|
175
191
|
@__before_actions = [action]
|
176
192
|
elsif i = @__before_actions.find_index { |a| a[:name] == action_name }
|
@@ -180,6 +196,22 @@ class RageController::API
|
|
180
196
|
end
|
181
197
|
end
|
182
198
|
|
199
|
+
def after_action(action_name = nil, **opts, &block)
|
200
|
+
action = prepare_action_params(action_name, **opts, &block)
|
201
|
+
|
202
|
+
if @__after_actions && @__after_actions.frozen?
|
203
|
+
@__after_actions = @__after_actions.dup
|
204
|
+
end
|
205
|
+
|
206
|
+
if @__after_actions.nil?
|
207
|
+
@__after_actions = [action]
|
208
|
+
elsif i = @__after_actions.find_index { |a| a[:name] == action_name }
|
209
|
+
@__after_actions[i] = action
|
210
|
+
else
|
211
|
+
@__after_actions << action
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
183
215
|
# Prevent a `before_action` hook from running.
|
184
216
|
#
|
185
217
|
# @param action_name [String] the name of the callback to skip
|
@@ -208,6 +240,32 @@ class RageController::API
|
|
208
240
|
|
209
241
|
@__before_actions[i] = action
|
210
242
|
end
|
243
|
+
|
244
|
+
private
|
245
|
+
|
246
|
+
# used by `before_action` and `after_action`
|
247
|
+
def prepare_action_params(action_name = nil, **opts, &block)
|
248
|
+
if block_given?
|
249
|
+
action_name = define_tmp_method(block)
|
250
|
+
elsif action_name.nil?
|
251
|
+
raise "No handler provided. Pass the `action_name` parameter or provide a block."
|
252
|
+
end
|
253
|
+
|
254
|
+
_only, _except, _if, _unless = opts.values_at(:only, :except, :if, :unless)
|
255
|
+
|
256
|
+
action = {
|
257
|
+
name: action_name,
|
258
|
+
only: _only && Array(_only),
|
259
|
+
except: _except && Array(_except),
|
260
|
+
if: _if,
|
261
|
+
unless: _unless
|
262
|
+
}
|
263
|
+
|
264
|
+
action[:if] = define_tmp_method(action[:if]) if action[:if].is_a?(Proc)
|
265
|
+
action[:unless] = define_tmp_method(action[:unless]) if action[:unless].is_a?(Proc)
|
266
|
+
|
267
|
+
action
|
268
|
+
end
|
211
269
|
end # class << self
|
212
270
|
|
213
271
|
# @private
|
data/lib/rage/env.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Rage::Env
|
4
|
+
STANDARD_ENVS = %w(development test staging production)
|
5
|
+
|
6
|
+
def initialize(env)
|
7
|
+
@env = env
|
8
|
+
|
9
|
+
STANDARD_ENVS.each do |standard_env|
|
10
|
+
self.class.define_method("#{standard_env}?") { false } if standard_env != @env
|
11
|
+
end
|
12
|
+
self.class.define_method("#{@env}?") { true }
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(method_name, *, &)
|
16
|
+
method_name.end_with?("?") ? false : super
|
17
|
+
end
|
18
|
+
|
19
|
+
def respond_to_missing?(method_name, include_private = false)
|
20
|
+
method_name.end_with?("?")
|
21
|
+
end
|
22
|
+
|
23
|
+
def ==(other)
|
24
|
+
@env == other
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_sym
|
28
|
+
@env.to_sym
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
@env
|
33
|
+
end
|
34
|
+
end
|
data/lib/rage/errors.rb
CHANGED
data/lib/rage/fiber.rb
CHANGED
@@ -23,6 +23,24 @@ class Fiber
|
|
23
23
|
@__err
|
24
24
|
end
|
25
25
|
|
26
|
+
# @private
|
27
|
+
def __get_id
|
28
|
+
@__rage_id ||= object_id.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
# @private
|
32
|
+
def __yielded?
|
33
|
+
!@__rage_id.nil?
|
34
|
+
end
|
35
|
+
|
36
|
+
# @private
|
37
|
+
def __block_channel(force = false)
|
38
|
+
@__block_channel_i ||= 0
|
39
|
+
@__block_channel_i += 1 if force
|
40
|
+
|
41
|
+
"block:#{object_id}:#{@__block_channel_i}"
|
42
|
+
end
|
43
|
+
|
26
44
|
# @private
|
27
45
|
# pause a fiber and resume in the next iteration of the event loop
|
28
46
|
def self.pause
|
data/lib/rage/fiber_scheduler.rb
CHANGED
@@ -81,7 +81,7 @@ class Rage::FiberScheduler
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def block(_blocker, timeout = nil)
|
84
|
-
f, fulfilled, channel = Fiber.current, false,
|
84
|
+
f, fulfilled, channel = Fiber.current, false, Fiber.current.__block_channel(true)
|
85
85
|
|
86
86
|
resume_fiber_block = proc do
|
87
87
|
unless fulfilled
|
@@ -100,7 +100,7 @@ class Rage::FiberScheduler
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def unblock(_blocker, fiber)
|
103
|
-
::Iodine.publish(
|
103
|
+
::Iodine.publish(fiber.__block_channel, "")
|
104
104
|
end
|
105
105
|
|
106
106
|
def fiber(&block)
|
data/lib/rage/logger/logger.rb
CHANGED
@@ -56,8 +56,8 @@ class Rage::Logger
|
|
56
56
|
# @param shift_period_suffix [String] the log file suffix format for daily, weekly or monthly rotation
|
57
57
|
# @param binmode sets whether the logger writes in binary mode
|
58
58
|
def initialize(log, level: Logger::DEBUG, formatter: Rage::TextFormatter.new, shift_age: 0, shift_size: 104857600, shift_period_suffix: "%Y%m%d", binmode: false)
|
59
|
-
if log && log != File::NULL
|
60
|
-
|
59
|
+
@logdev = if log && log != File::NULL
|
60
|
+
Logger::LogDevice.new(log, shift_age:, shift_size:, shift_period_suffix:, binmode:)
|
61
61
|
end
|
62
62
|
|
63
63
|
@formatter = formatter
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Rage::Cors
|
4
|
+
def initialize(app, *, &)
|
5
|
+
@app = app
|
6
|
+
instance_eval(&)
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
if env["REQUEST_METHOD"] == "OPTIONS"
|
11
|
+
return (response = @cors_response)
|
12
|
+
end
|
13
|
+
|
14
|
+
response = @app.call(env)
|
15
|
+
response[1]["Access-Control-Allow-Credentials"] = @allow_credentials if @allow_credentials
|
16
|
+
response[1]["Access-Control-Expose-Headers"] = @expose_headers if @expose_headers
|
17
|
+
|
18
|
+
response
|
19
|
+
ensure
|
20
|
+
if origin = @cors_check.call(env)
|
21
|
+
headers = response[1]
|
22
|
+
headers["Access-Control-Allow-Origin"] = origin
|
23
|
+
if @origins != "*"
|
24
|
+
vary = headers["Vary"]
|
25
|
+
if vary.nil?
|
26
|
+
headers["Vary"] = "Origin"
|
27
|
+
elsif vary != "Origin"
|
28
|
+
headers["Vary"] += ", Origin"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Set CORS rules for the application.
|
35
|
+
#
|
36
|
+
# @param origins [String, Regexp, "*"] origins allowed to access the application
|
37
|
+
# @param methods [Array<Symbol>, "*"] allowed methods when accessing the application
|
38
|
+
# @param allow_headers [Array<String>, "*"] indicate which HTTP headers can be used when making the actual request
|
39
|
+
# @param expose_headers [Array<String>, "*"] adds the specified headers to the allowlist that JavaScript in browsers is allowed to access
|
40
|
+
# @param max_age [Integer] indicate how long the results of a preflight request can be cached
|
41
|
+
# @param allow_credentials [Boolean] indicate whether or not the response to the request can be exposed when the `credentials` flag is `true`
|
42
|
+
# @example
|
43
|
+
# config.middleware.use Rage::Cors do
|
44
|
+
# allow "localhost:5173", "myhost.com"
|
45
|
+
# end
|
46
|
+
# @example
|
47
|
+
# config.middleware.use Rage::Cors do
|
48
|
+
# allow "*",
|
49
|
+
# methods: [:get, :post, :put],
|
50
|
+
# allow_headers: ["x-domain-token"],
|
51
|
+
# expose: ["Some-Custom-Response-Header"],
|
52
|
+
# max_age: 600
|
53
|
+
# end
|
54
|
+
# @note The middleware only supports the basic case of allowing one or several origins for the whole application. Use {https://github.com/cyu/rack-cors Rack::Cors} if you are looking to specify more advanced rules.
|
55
|
+
def allow(*origins, methods: "*", allow_headers: "*", expose_headers: nil, max_age: nil, allow_credentials: false)
|
56
|
+
@allow_headers = Array(allow_headers).join(", ") if allow_headers
|
57
|
+
@expose_headers = Array(expose_headers).join(", ") if expose_headers
|
58
|
+
@max_age = max_age.to_s if max_age
|
59
|
+
@allow_credentials = "true" if allow_credentials
|
60
|
+
|
61
|
+
@default_methods = %w(GET POST PUT PATCH DELETE HEAD OPTIONS)
|
62
|
+
@methods = if methods != "*"
|
63
|
+
methods.map! { |method| method.to_s.upcase }.tap { |m|
|
64
|
+
if (invalid_methods = m - @default_methods).any?
|
65
|
+
raise "Unsupported method passed to Rage::Cors: #{invalid_methods[0]}"
|
66
|
+
end
|
67
|
+
}.join(", ")
|
68
|
+
elsif @allow_credentials
|
69
|
+
@default_methods.join(", ")
|
70
|
+
else
|
71
|
+
"*"
|
72
|
+
end
|
73
|
+
|
74
|
+
if @allow_credentials
|
75
|
+
raise "Rage::Cors requires you to explicitly list allowed headers when using `allow_credentials: true`" if @allow_headers == "*"
|
76
|
+
raise "Rage::Cors requires you to explicitly list exposed headers when using `allow_credentials: true`" if @expose_headers == "*"
|
77
|
+
end
|
78
|
+
|
79
|
+
@origins = []
|
80
|
+
origins.each do |origin|
|
81
|
+
if origin == "*"
|
82
|
+
@origins = "*"
|
83
|
+
break
|
84
|
+
elsif origin.is_a?(Regexp) || origin =~ /^\S+:\/\//
|
85
|
+
@origins << origin
|
86
|
+
else
|
87
|
+
@origins << "https://#{origin}" << "http://#{origin}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
@cors_check = create_cors_proc
|
92
|
+
@cors_response = [204, create_headers, []]
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def create_headers
|
98
|
+
headers = {
|
99
|
+
"Access-Control-Allow-Origin" => "",
|
100
|
+
"Access-Control-Allow-Methods" => @methods,
|
101
|
+
}
|
102
|
+
|
103
|
+
if @allow_headers
|
104
|
+
headers["Access-Control-Allow-Headers"] = @allow_headers
|
105
|
+
end
|
106
|
+
if @expose_headers
|
107
|
+
headers["Access-Control-Expose-Headers"] = @expose_headers
|
108
|
+
end
|
109
|
+
if @max_age
|
110
|
+
headers["Access-Control-Max-Age"] = @max_age
|
111
|
+
end
|
112
|
+
if @allow_credentials
|
113
|
+
headers["Access-Control-Allow-Credentials"] = @allow_credentials
|
114
|
+
end
|
115
|
+
|
116
|
+
headers
|
117
|
+
end
|
118
|
+
|
119
|
+
def create_cors_proc
|
120
|
+
if @origins == "*"
|
121
|
+
->(env) { env["HTTP_ORIGIN"] }
|
122
|
+
else
|
123
|
+
origins_eval = @origins.map { |origin|
|
124
|
+
origin.is_a?(Regexp) ?
|
125
|
+
"origin =~ /#{origin.source}/.freeze" :
|
126
|
+
"origin == '#{origin}'.freeze"
|
127
|
+
}.join(" || ")
|
128
|
+
|
129
|
+
eval <<-RUBY
|
130
|
+
->(env) do
|
131
|
+
origin = env["HTTP_ORIGIN".freeze]
|
132
|
+
origin if #{origins_eval}
|
133
|
+
end
|
134
|
+
RUBY
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# The middleware wraps every request in a separate Fiber. It should always be on the top of the middleware stack,
|
5
|
+
# as it implements a custom defer protocol, which may break middlewares located above.
|
6
|
+
#
|
7
|
+
class Rage::FiberWrapper
|
8
|
+
def initialize(app)
|
9
|
+
Iodine.on_state(:on_start) do
|
10
|
+
Fiber.set_scheduler(Rage::FiberScheduler.new)
|
11
|
+
end
|
12
|
+
@app = app
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
fiber = Fiber.schedule do
|
17
|
+
@app.call(env)
|
18
|
+
ensure
|
19
|
+
# notify Iodine the request can now be resumed
|
20
|
+
Iodine.publish(Fiber.current.__get_id, "", Iodine::PubSub::PROCESS) if Fiber.current.__yielded?
|
21
|
+
end
|
22
|
+
|
23
|
+
# the fiber encountered blocking IO and yielded; instruct Iodine to pause the request
|
24
|
+
if fiber.alive?
|
25
|
+
[:__http_defer__, fiber]
|
26
|
+
else
|
27
|
+
fiber.__get_result
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Rage::Reloader
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
Rage.code_loader.reload
|
10
|
+
@app.call(env)
|
11
|
+
rescue Exception => e
|
12
|
+
exception_str = "#{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}"
|
13
|
+
puts(exception_str)
|
14
|
+
[500, {}, [exception_str]]
|
15
|
+
end
|
16
|
+
end
|
data/lib/rage/router/backend.rb
CHANGED
@@ -14,6 +14,11 @@ class Rage::Router::Backend
|
|
14
14
|
@constrainer = Rage::Router::Constrainer.new({})
|
15
15
|
end
|
16
16
|
|
17
|
+
def reset_routes
|
18
|
+
@routes = []
|
19
|
+
@trees = {}
|
20
|
+
end
|
21
|
+
|
17
22
|
def mount(path, handler, methods)
|
18
23
|
raise "Mount handler should respond to `call`" unless handler.respond_to?(:call)
|
19
24
|
|
@@ -79,6 +84,9 @@ class Rage::Router::Backend
|
|
79
84
|
end
|
80
85
|
|
81
86
|
__on(method, path, handler, constraints, defaults, meta)
|
87
|
+
|
88
|
+
rescue Rage::Errors::RouterError => e
|
89
|
+
raise e unless Rage.code_loader.reloading?
|
82
90
|
end
|
83
91
|
|
84
92
|
def lookup(env)
|
@@ -280,7 +288,7 @@ class Rage::Router::Backend
|
|
280
288
|
if Object.const_defined?(klass)
|
281
289
|
Object.const_get(klass)
|
282
290
|
else
|
283
|
-
raise "Routing error: could not find the #{klass} class"
|
291
|
+
raise Rage::Errors::RouterError, "Routing error: could not find the #{klass} class"
|
284
292
|
end
|
285
293
|
end
|
286
294
|
end
|
data/lib/rage/setup.rb
CHANGED
@@ -2,22 +2,10 @@ Iodine.patch_rack
|
|
2
2
|
|
3
3
|
require_relative "#{Rage.root}/config/environments/#{Rage.env}"
|
4
4
|
|
5
|
+
# Run application initializers
|
6
|
+
Dir["#{Rage.root}/config/initializers/**/*.rb"].each { |initializer| load(initializer) }
|
5
7
|
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
loop do
|
10
|
-
path = app.shift
|
11
|
-
break if path.nil?
|
12
|
-
|
13
|
-
require_relative path
|
14
|
-
|
15
|
-
# push the file to the end of the list in case it depends on another file that has not yet been required;
|
16
|
-
# re-raise if only errored out files are left
|
17
|
-
rescue NameError
|
18
|
-
raise if (app - bad).empty?
|
19
|
-
app << path
|
20
|
-
bad << path
|
21
|
-
end
|
8
|
+
# Load application classes
|
9
|
+
Rage.code_loader.setup
|
22
10
|
|
23
11
|
require_relative "#{Rage.root}/config/routes"
|
File without changes
|
data/lib/rage/version.rb
CHANGED
data/lib/rage-rb.rb
CHANGED
@@ -28,7 +28,7 @@ module Rage
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def self.env
|
31
|
-
@__env ||= ENV["RAGE_ENV"]
|
31
|
+
@__env ||= Rage::Env.new(ENV["RAGE_ENV"])
|
32
32
|
end
|
33
33
|
|
34
34
|
def self.groups
|
@@ -43,6 +43,16 @@ module Rage
|
|
43
43
|
@logger ||= config.logger
|
44
44
|
end
|
45
45
|
|
46
|
+
def self.load_middlewares(rack_builder)
|
47
|
+
config.middleware.middlewares.each do |middleware, args, block|
|
48
|
+
rack_builder.use(middleware, *args, &block)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.code_loader
|
53
|
+
@code_loader ||= Rage::CodeLoader.new
|
54
|
+
end
|
55
|
+
|
46
56
|
module Router
|
47
57
|
module Strategies
|
48
58
|
end
|
@@ -51,3 +61,5 @@ end
|
|
51
61
|
|
52
62
|
module RageController
|
53
63
|
end
|
64
|
+
|
65
|
+
require_relative "rage/env"
|
data/rage.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rage-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roman Samoilov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-12-
|
11
|
+
date: 2023-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -44,14 +44,28 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '3.0'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: zeitwerk
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.6'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.6'
|
55
69
|
description:
|
56
70
|
email:
|
57
71
|
- rsamoi@icloud.com
|
@@ -74,13 +88,18 @@ files:
|
|
74
88
|
- lib/rage/all.rb
|
75
89
|
- lib/rage/application.rb
|
76
90
|
- lib/rage/cli.rb
|
91
|
+
- lib/rage/code_loader.rb
|
77
92
|
- lib/rage/configuration.rb
|
78
93
|
- lib/rage/controller/api.rb
|
94
|
+
- lib/rage/env.rb
|
79
95
|
- lib/rage/errors.rb
|
80
96
|
- lib/rage/fiber.rb
|
81
97
|
- lib/rage/fiber_scheduler.rb
|
82
98
|
- lib/rage/logger/logger.rb
|
83
99
|
- lib/rage/logger/text_formatter.rb
|
100
|
+
- lib/rage/middleware/cors.rb
|
101
|
+
- lib/rage/middleware/fiber_wrapper.rb
|
102
|
+
- lib/rage/middleware/reloader.rb
|
84
103
|
- lib/rage/params_parser.rb
|
85
104
|
- lib/rage/request.rb
|
86
105
|
- lib/rage/router/README.md
|
@@ -99,6 +118,7 @@ files:
|
|
99
118
|
- lib/rage/templates/config-environments-development.rb
|
100
119
|
- lib/rage/templates/config-environments-production.rb
|
101
120
|
- lib/rage/templates/config-environments-test.rb
|
121
|
+
- lib/rage/templates/config-initializers-.keep
|
102
122
|
- lib/rage/templates/config-routes.rb
|
103
123
|
- lib/rage/templates/config.ru
|
104
124
|
- lib/rage/templates/lib-.keep
|