rage-rb 1.17.1 → 1.19.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/ARCHITECTURE.md +47 -0
- data/CHANGELOG.md +23 -0
- data/lib/rage/all.rb +1 -0
- data/lib/rage/application.rb +3 -25
- data/lib/rage/cable/cable.rb +2 -3
- data/lib/rage/cli.rb +101 -18
- data/lib/rage/code_loader.rb +8 -0
- data/lib/rage/configuration.rb +525 -195
- data/lib/rage/controller/api.rb +15 -3
- data/lib/rage/deferred/context.rb +48 -0
- data/lib/rage/deferred/deferred.rb +5 -1
- data/lib/rage/deferred/queue.rb +8 -8
- data/lib/rage/deferred/task.rb +29 -23
- data/lib/rage/env.rb +15 -0
- data/lib/rage/events/events.rb +140 -0
- data/lib/rage/events/subscriber.rb +174 -0
- data/lib/rage/fiber.rb +11 -2
- data/lib/rage/fiber_scheduler.rb +2 -2
- data/lib/rage/hooks.rb +1 -0
- data/lib/rage/internal.rb +53 -0
- data/lib/rage/log_processor.rb +117 -0
- data/lib/rage/logger/json_formatter.rb +37 -18
- data/lib/rage/logger/logger.rb +136 -30
- data/lib/rage/logger/text_formatter.rb +21 -2
- data/lib/rage/middleware/fiber_wrapper.rb +8 -0
- data/lib/rage/middleware/reloader.rb +6 -11
- data/lib/rage/request.rb +18 -1
- data/lib/rage/response.rb +1 -1
- data/lib/rage/router/util.rb +8 -0
- data/lib/rage/setup.rb +2 -0
- data/lib/rage/templates/config-environments-production.rb +1 -0
- data/lib/rage/version.rb +1 -1
- data/lib/rage-rb.rb +51 -0
- data/rage.gemspec +1 -0
- metadata +22 -4
- data/OVERVIEW.md +0 -83
- data/lib/rage/deferred/metadata.rb +0 -43
data/lib/rage-rb.rb
CHANGED
|
@@ -6,67 +6,115 @@ require "iodine"
|
|
|
6
6
|
require "pathname"
|
|
7
7
|
|
|
8
8
|
module Rage
|
|
9
|
+
# Builds the Rage application with the configured middlewares.
|
|
9
10
|
def self.application
|
|
10
11
|
with_middlewares(Application.new(__router), config.middleware.middlewares)
|
|
11
12
|
end
|
|
12
13
|
|
|
14
|
+
# Builds the Rage application which delegates Rails requests to `Rails.application`.
|
|
13
15
|
def self.multi_application
|
|
14
16
|
Rage::Router::Util::Cascade.new(application, Rails.application)
|
|
15
17
|
end
|
|
16
18
|
|
|
19
|
+
# Shorthand to access {Rage::Cable Rage::Cable}.
|
|
20
|
+
# @return [Rage::Cable]
|
|
17
21
|
def self.cable
|
|
18
22
|
Rage::Cable
|
|
19
23
|
end
|
|
20
24
|
|
|
25
|
+
# Shorthand to access {Rage::OpenAPI Rage::OpenAPI}.
|
|
26
|
+
# @return [Rage::OpenAPI]
|
|
21
27
|
def self.openapi
|
|
22
28
|
Rage::OpenAPI
|
|
23
29
|
end
|
|
24
30
|
|
|
31
|
+
# Shorthand to access {Rage::Deferred Rage::Deferred}.
|
|
32
|
+
# @return [Rage::Deferred]
|
|
25
33
|
def self.deferred
|
|
26
34
|
Rage::Deferred
|
|
27
35
|
end
|
|
28
36
|
|
|
37
|
+
# Shorthand to access {Rage::Events Rage::Events}.
|
|
38
|
+
# @return [Rage::Events]
|
|
39
|
+
def self.events
|
|
40
|
+
Rage::Events
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Configure routes for the Rage application.
|
|
44
|
+
# @return [Rage::Router::DSL::Handler]
|
|
45
|
+
# @example
|
|
46
|
+
# Rage.routes.draw do
|
|
47
|
+
# root to: "users#index"
|
|
48
|
+
# end
|
|
29
49
|
def self.routes
|
|
30
50
|
Rage::Router::DSL.new(__router)
|
|
31
51
|
end
|
|
32
52
|
|
|
53
|
+
# @private
|
|
33
54
|
def self.__router
|
|
34
55
|
@__router ||= Rage::Router::Backend.new
|
|
35
56
|
end
|
|
36
57
|
|
|
58
|
+
# @private
|
|
59
|
+
def self.__log_processor
|
|
60
|
+
@__log_processor ||= Rage::LogProcessor.new
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Access the Rage configuration.
|
|
64
|
+
# @return [Rage::Configuration] the Rage configuration instance.
|
|
37
65
|
def self.config
|
|
38
66
|
@config ||= Rage::Configuration.new
|
|
39
67
|
end
|
|
40
68
|
|
|
69
|
+
# Configure Rage using a block.
|
|
70
|
+
# @example
|
|
71
|
+
# Rage.configure do |config|
|
|
72
|
+
# config.log_level = :debug
|
|
73
|
+
# end
|
|
41
74
|
def self.configure(&)
|
|
42
75
|
config.instance_eval(&)
|
|
43
76
|
config.__finalize
|
|
44
77
|
end
|
|
45
78
|
|
|
79
|
+
# Access the current Rage environment.
|
|
80
|
+
# @return [Rage::Env] the Rage environment instance
|
|
81
|
+
# @example
|
|
82
|
+
# if Rage.env.development?
|
|
83
|
+
# puts "Running in development mode"
|
|
84
|
+
# end
|
|
46
85
|
def self.env
|
|
47
86
|
@__env ||= Rage::Env.new(ENV["RAGE_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development")
|
|
48
87
|
end
|
|
49
88
|
|
|
89
|
+
# Access the current Gem groups based on the Rage environment.
|
|
50
90
|
def self.groups
|
|
51
91
|
[:default, Rage.env.to_sym]
|
|
52
92
|
end
|
|
53
93
|
|
|
94
|
+
# Access the root path of the Rage application.
|
|
95
|
+
# @return [Pathname] the root path
|
|
54
96
|
def self.root
|
|
55
97
|
@root ||= Pathname.new(".").expand_path
|
|
56
98
|
end
|
|
57
99
|
|
|
100
|
+
# Access the Rage logger.
|
|
101
|
+
# @return [Rage::Logger] the Rage logger instance
|
|
58
102
|
def self.logger
|
|
59
103
|
@logger ||= config.logger
|
|
60
104
|
end
|
|
61
105
|
|
|
106
|
+
# Load middlewares into the Rage application.
|
|
107
|
+
# @deprecated This method is deprecated and has been merged into `Rage.application`.
|
|
62
108
|
def self.load_middlewares(_)
|
|
63
109
|
puts "`Rage.load_middlewares` is deprecated and has been merged into `Rage.application`. Please remove this call."
|
|
64
110
|
end
|
|
65
111
|
|
|
112
|
+
# @private
|
|
66
113
|
def self.code_loader
|
|
67
114
|
@code_loader ||= Rage::CodeLoader.new
|
|
68
115
|
end
|
|
69
116
|
|
|
117
|
+
# @private
|
|
70
118
|
def self.patch_active_record_connection_pool
|
|
71
119
|
patch = proc do
|
|
72
120
|
is_connected = ActiveRecord::Base.connection_pool rescue false
|
|
@@ -90,6 +138,7 @@ module Rage
|
|
|
90
138
|
end
|
|
91
139
|
end
|
|
92
140
|
|
|
141
|
+
# Load Rake tasks for the Rage application.
|
|
93
142
|
def self.load_tasks
|
|
94
143
|
Rage::Tasks.init
|
|
95
144
|
end
|
|
@@ -135,9 +184,11 @@ module Rage
|
|
|
135
184
|
autoload :Cable, "rage/cable/cable"
|
|
136
185
|
autoload :OpenAPI, "rage/openapi/openapi"
|
|
137
186
|
autoload :Deferred, "rage/deferred/deferred"
|
|
187
|
+
autoload :Events, "rage/events/events"
|
|
138
188
|
end
|
|
139
189
|
|
|
140
190
|
module RageController
|
|
141
191
|
end
|
|
142
192
|
|
|
143
193
|
require_relative "rage/env"
|
|
194
|
+
require_relative "rage/internal"
|
data/rage.gemspec
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rage-rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.19.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Roman Samoilov
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2025-
|
|
10
|
+
date: 2025-12-03 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: thor
|
|
@@ -93,6 +93,20 @@ dependencies:
|
|
|
93
93
|
- - ">="
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
95
|
version: '12.0'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: logger
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '0'
|
|
103
|
+
type: :runtime
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0'
|
|
96
110
|
email:
|
|
97
111
|
- rsamoi@icloud.com
|
|
98
112
|
executables:
|
|
@@ -102,11 +116,11 @@ extra_rdoc_files: []
|
|
|
102
116
|
files:
|
|
103
117
|
- ".rspec"
|
|
104
118
|
- ".yardopts"
|
|
119
|
+
- ARCHITECTURE.md
|
|
105
120
|
- CHANGELOG.md
|
|
106
121
|
- CODE_OF_CONDUCT.md
|
|
107
122
|
- Gemfile
|
|
108
123
|
- LICENSE.txt
|
|
109
|
-
- OVERVIEW.md
|
|
110
124
|
- README.md
|
|
111
125
|
- Rakefile
|
|
112
126
|
- exe/rage
|
|
@@ -130,18 +144,22 @@ files:
|
|
|
130
144
|
- lib/rage/cookies.rb
|
|
131
145
|
- lib/rage/deferred/backends/disk.rb
|
|
132
146
|
- lib/rage/deferred/backends/nil.rb
|
|
147
|
+
- lib/rage/deferred/context.rb
|
|
133
148
|
- lib/rage/deferred/deferred.rb
|
|
134
|
-
- lib/rage/deferred/metadata.rb
|
|
135
149
|
- lib/rage/deferred/proxy.rb
|
|
136
150
|
- lib/rage/deferred/queue.rb
|
|
137
151
|
- lib/rage/deferred/task.rb
|
|
138
152
|
- lib/rage/env.rb
|
|
139
153
|
- lib/rage/errors.rb
|
|
154
|
+
- lib/rage/events/events.rb
|
|
155
|
+
- lib/rage/events/subscriber.rb
|
|
140
156
|
- lib/rage/ext/active_record/connection_pool.rb
|
|
141
157
|
- lib/rage/ext/setup.rb
|
|
142
158
|
- lib/rage/fiber.rb
|
|
143
159
|
- lib/rage/fiber_scheduler.rb
|
|
144
160
|
- lib/rage/hooks.rb
|
|
161
|
+
- lib/rage/internal.rb
|
|
162
|
+
- lib/rage/log_processor.rb
|
|
145
163
|
- lib/rage/logger/json_formatter.rb
|
|
146
164
|
- lib/rage/logger/logger.rb
|
|
147
165
|
- lib/rage/logger/text_formatter.rb
|
data/OVERVIEW.md
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
### Table of Contents
|
|
2
|
-
|
|
3
|
-
[API Workflow](#api-workflow)<br>
|
|
4
|
-
[Executing Controller Actions](#executing-controller-actions)<br>
|
|
5
|
-
[Cable Workflow](#cable-workflow)<br>
|
|
6
|
-
[OpenAPI Workflow](#openapi-workflow)<br>
|
|
7
|
-
[Design Principles](#design-principles)<br>
|
|
8
|
-
|
|
9
|
-
### API Workflow
|
|
10
|
-
|
|
11
|
-
The following diagram describes some of Rage's internal components and the way they interact with each other:
|
|
12
|
-
|
|
13
|
-

|
|
14
|
-
|
|
15
|
-
### Executing Controller Actions
|
|
16
|
-
|
|
17
|
-
When `Rage::Router::DSL` parses the `config/routes.rb` file and calls the `Rage::Router::Backend` class, it registers actions and stores handler procs.
|
|
18
|
-
|
|
19
|
-
Consider we have the following controller:
|
|
20
|
-
|
|
21
|
-
```ruby
|
|
22
|
-
class UsersController < RageController::API
|
|
23
|
-
before_action :find_user
|
|
24
|
-
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
|
|
25
|
-
|
|
26
|
-
def show
|
|
27
|
-
render json: @user
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
def find_user
|
|
33
|
-
@user = User.find(params[:id])
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def render_not_found(_)
|
|
37
|
-
render status: :not_found
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Before processing requests to `UsersController#show`, Rage has to [register](https://github.com/rage-rb/rage/blob/master/lib/rage/controller/api.rb#L11) the show action. Registering means defining a new method that will look like this:
|
|
43
|
-
|
|
44
|
-
```ruby
|
|
45
|
-
class UsersController
|
|
46
|
-
def __run_show
|
|
47
|
-
find_user
|
|
48
|
-
show
|
|
49
|
-
rescue ActiveRecord::RecordNotFound => e
|
|
50
|
-
render_not_found(e)
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
After that, Rage will create and store a handler proc that will look exactly like this:
|
|
56
|
-
|
|
57
|
-
```ruby
|
|
58
|
-
->(env, params) { UsersController.new(env, params).__run_show }
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
All of this happens at boot time. Once the request comes in at runtime, Rage will only need to retrieve the handler proc defined earlier and call it.
|
|
62
|
-
|
|
63
|
-
### Cable Workflow
|
|
64
|
-
|
|
65
|
-
The following diagram describes the components of a `Rage::Cable` application:
|
|
66
|
-
|
|
67
|
-

|
|
68
|
-
|
|
69
|
-
### OpenAPI Workflow
|
|
70
|
-
|
|
71
|
-
The following diagram describes the flow of `Rage::OpenAPI`:
|
|
72
|
-
|
|
73
|
-
<img width="800" src="https://github.com/user-attachments/assets/b4a87b1e-9a0f-4432-a3e9-0106ff546f3f" />
|
|
74
|
-
|
|
75
|
-
### Design Principles
|
|
76
|
-
|
|
77
|
-
* **Lean Happy Path:** we try to execute as many operations as possible during server initialization to minimize workload during request processing. Additionally, new features should be designed to avoid impacting the framework performance for users who do not utilize those features.
|
|
78
|
-
|
|
79
|
-
* **Performance Over Code Style:** we recognize the distinct requirements of framework and client code. Testability, readability, and maintainability are crucial for client code used in application development. Conversely, library code addresses different tasks and should be designed with different objectives. In library code, performance and abstraction to enable future modifications while maintaining backward compatibility take precedence over typical client code concerns, though testability and readability remain important.
|
|
80
|
-
|
|
81
|
-
* **Rails Compatibility:** Rails compatibility is a key objective to ensure a seamless transition for developers. While it may not be feasible to replicate every method implemented in Rails, the framework should function in a familiar and expected manner.
|
|
82
|
-
|
|
83
|
-
* **Single-Threaded Fiber-Based Approach:** each request is processed in a separate, isolated execution context (Fiber), pausing whenever it encounters blocking I/O. This single-threaded approach eliminates thread synchronization overhead, leading to enhanced performance and simplified code.
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
##
|
|
4
|
-
# Metadata for deferred tasks.
|
|
5
|
-
# The class encapsulates the metadata associated with a deferred task, and allows to store it without modifying the task instance.
|
|
6
|
-
#
|
|
7
|
-
class Rage::Deferred::Metadata
|
|
8
|
-
def self.build(task, args, kwargs)
|
|
9
|
-
request_id = Thread.current[:rage_logger][:tags][0] if Thread.current[:rage_logger]
|
|
10
|
-
|
|
11
|
-
[
|
|
12
|
-
task,
|
|
13
|
-
args.empty? ? nil : args,
|
|
14
|
-
kwargs.empty? ? nil : kwargs,
|
|
15
|
-
nil,
|
|
16
|
-
request_id
|
|
17
|
-
]
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def self.get_task(metadata)
|
|
21
|
-
metadata[0]
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def self.get_args(metadata)
|
|
25
|
-
metadata[1]
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def self.get_kwargs(metadata)
|
|
29
|
-
metadata[2]
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def self.get_attempts(metadata)
|
|
33
|
-
metadata[3]
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def self.get_request_id(metadata)
|
|
37
|
-
metadata[4]
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def self.inc_attempts(metadata)
|
|
41
|
-
metadata[3] = metadata[3].to_i + 1
|
|
42
|
-
end
|
|
43
|
-
end
|