rage-rb 1.6.0 → 1.8.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 +14 -0
- data/Gemfile +10 -7
- data/OVERVIEW.md +1 -1
- data/README.md +25 -9
- data/lib/rage/all.rb +1 -0
- data/lib/rage/cable/cable.rb +130 -0
- data/lib/rage/cable/channel.rb +452 -0
- data/lib/rage/cable/connection.rb +78 -0
- data/lib/rage/cable/protocol/actioncable_v1_json.rb +167 -0
- data/lib/rage/cable/router.rb +138 -0
- data/lib/rage/cli.rb +2 -1
- data/lib/rage/code_loader.rb +9 -0
- data/lib/rage/configuration.rb +53 -0
- data/lib/rage/controller/api.rb +51 -13
- data/lib/rage/cookies.rb +7 -9
- data/lib/rage/ext/active_record/connection_pool.rb +1 -1
- data/lib/rage/fiber.rb +3 -3
- data/lib/rage/fiber_scheduler.rb +1 -1
- data/lib/rage/logger/json_formatter.rb +1 -1
- data/lib/rage/logger/logger.rb +1 -1
- data/lib/rage/logger/text_formatter.rb +1 -1
- data/lib/rage/middleware/cors.rb +2 -2
- data/lib/rage/middleware/fiber_wrapper.rb +3 -1
- data/lib/rage/middleware/origin_validator.rb +38 -0
- data/lib/rage/middleware/reloader.rb +1 -1
- data/lib/rage/params_parser.rb +1 -1
- data/lib/rage/router/backend.rb +4 -6
- data/lib/rage/router/constrainer.rb +1 -1
- data/lib/rage/router/dsl.rb +7 -7
- data/lib/rage/router/dsl_plugins/legacy_hash_notation.rb +1 -1
- data/lib/rage/router/dsl_plugins/legacy_root_notation.rb +1 -1
- data/lib/rage/router/handler_storage.rb +1 -1
- data/lib/rage/session.rb +2 -2
- data/lib/rage/setup.rb +5 -1
- data/lib/rage/sidekiq_session.rb +1 -1
- data/lib/rage/version.rb +1 -1
- data/lib/rage-rb.rb +23 -15
- data/rage.gemspec +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4adc4048bfa84558a6b86b15cbcab8e6153858878425bde66db78a975fa46f46
|
4
|
+
data.tar.gz: 46bef9f42fdd681bdf593f037a73ca8ae2d435acad9eed5ddf4b3740bf61b601
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 754b954d46d065020e0c2184c04f1a13d0c0a64ddf11d1b034062df4cc669460c259032a8bf40a61c876579f1774e50e9b57181d4d3e1301c4610577e11c6734
|
7
|
+
data.tar.gz: 4207556922f8fd2c82d07ba8664f53847dc1e5afb6bb5d38260ac0ccd428dd011fd8c6bf34f69aa75af97044a838df23c6c36b5baae1ae7cd3d4c08455fc699b
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.8.0] - 2024-08-06
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- Support WebSockets (#88).
|
8
|
+
|
9
|
+
## [1.7.0] - 2024-07-30
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- Support `wrap_parameters` by [@alex-rogachev](https://github.com/alex-rogachev) (#89).
|
14
|
+
- Unknown environment error handling by [@cuneyter](https://github.com/cuneyter) (#95).
|
15
|
+
- Allow `rescue_from` handlers to not accept arguments (#93).
|
16
|
+
|
3
17
|
## [1.6.0] - 2024-07-15
|
4
18
|
|
5
19
|
### Added
|
data/Gemfile
CHANGED
@@ -8,12 +8,15 @@ gemspec
|
|
8
8
|
gem "rake", "~> 13.0"
|
9
9
|
|
10
10
|
gem "rspec", "~> 3.0"
|
11
|
-
gem "http"
|
12
11
|
gem "yard"
|
12
|
+
gem "rubocop", "~> 1.65", require: false
|
13
13
|
|
14
|
-
|
15
|
-
gem "
|
16
|
-
gem "
|
17
|
-
|
18
|
-
gem "
|
19
|
-
gem "
|
14
|
+
group :test do
|
15
|
+
gem "http"
|
16
|
+
gem "pg"
|
17
|
+
gem "mysql2"
|
18
|
+
gem "connection_pool", "~> 2.0"
|
19
|
+
gem "rbnacl"
|
20
|
+
gem "domain_name"
|
21
|
+
gem "websocket-client-simple"
|
22
|
+
end
|
data/OVERVIEW.md
CHANGED
@@ -31,7 +31,7 @@ class UsersController < RageController::API
|
|
31
31
|
end
|
32
32
|
```
|
33
33
|
|
34
|
-
Before processing requests to `UsersController#show`, Rage has to [register](https://github.com/rage-rb/rage/blob/master/lib/rage/controller/api.rb#
|
34
|
+
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:
|
35
35
|
|
36
36
|
```ruby
|
37
37
|
class UsersController
|
data/README.md
CHANGED
@@ -15,7 +15,7 @@ Inspired by [Deno](https://deno.com) and built on top of [Iodine](https://github
|
|
15
15
|
|
16
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.
|
17
17
|
|
18
|
-
* **Acceptance of modern Ruby** - the framework includes a fiber scheduler, which means your code never blocks while waiting on
|
18
|
+
* **Acceptance of modern Ruby** - the framework includes a fiber scheduler, which means your code never blocks while waiting on I/O.
|
19
19
|
|
20
20
|
## Installation
|
21
21
|
|
@@ -44,7 +44,7 @@ Start coding!
|
|
44
44
|
|
45
45
|
## Getting Started
|
46
46
|
|
47
|
-
This gem is designed to be a drop-in replacement for Rails in API mode. Public API is
|
47
|
+
This gem is designed to be a drop-in replacement for Rails in API mode. Public API is expected to fully match Rails.
|
48
48
|
|
49
49
|
Check out in-depth API docs for more information:
|
50
50
|
|
@@ -60,6 +60,8 @@ Also, see the following integration guides:
|
|
60
60
|
- [Rails integration](https://github.com/rage-rb/rage/wiki/Rails-integration)
|
61
61
|
- [RSpec integration](https://github.com/rage-rb/rage/wiki/RSpec-integration)
|
62
62
|
|
63
|
+
If you are a first-time contributor, make sure to check the [overview doc](https://github.com/rage-rb/rage/blob/master/OVERVIEW.md) that shows how Rage's core components interact with each other.
|
64
|
+
|
63
65
|
### Example
|
64
66
|
|
65
67
|
A sample controller could look like this:
|
@@ -116,34 +118,48 @@ class PagesController < RageController::API
|
|
116
118
|
end
|
117
119
|
```
|
118
120
|
|
119
|
-
:information_source: **Note**: When using `Fiber.await`, it is important to wrap
|
121
|
+
:information_source: **Note**: When using `Fiber.await`, it is important to wrap every argument into a fiber using `Fiber.schedule`.
|
120
122
|
|
121
123
|
## Benchmarks
|
122
124
|
|
123
|
-
####
|
125
|
+
#### Hello World
|
124
126
|
|
125
127
|
```ruby
|
126
|
-
class
|
128
|
+
class BenchmarksController < ApplicationController
|
127
129
|
def index
|
128
130
|
render json: { hello: "world" }
|
129
131
|
end
|
130
132
|
end
|
131
133
|
```
|
132
|
-
![Requests per second](https://github.com/rage-rb/rage/assets/2270393/6c221903-e265-4c94-80e1-041f266c8f47)
|
133
134
|
|
134
|
-
|
135
|
+
![Requests per second](https://github.com/user-attachments/assets/a7f864ae-0dfb-4628-a420-265a10d8591d)
|
136
|
+
|
137
|
+
#### Waiting on I/O
|
135
138
|
|
136
139
|
```ruby
|
137
140
|
require "net/http"
|
138
141
|
|
139
|
-
class
|
142
|
+
class BenchmarksController < ApplicationController
|
140
143
|
def index
|
141
144
|
Net::HTTP.get(URI("<endpoint-that-responds-in-one-second>"))
|
142
145
|
head :ok
|
143
146
|
end
|
144
147
|
end
|
145
148
|
```
|
146
|
-
|
149
|
+
|
150
|
+
![Time to complete 100 requests](https://github.com/user-attachments/assets/4f4feda3-bd88-43d8-8999-268534c2f9de)
|
151
|
+
|
152
|
+
#### Using ActiveRecord
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
class BenchmarksController < ApplicationController
|
156
|
+
def show
|
157
|
+
render json: World.find(rand(10_000))
|
158
|
+
end
|
159
|
+
end
|
160
|
+
```
|
161
|
+
|
162
|
+
![Requests per second](https://github.com/user-attachments/assets/04678788-0034-4db4-9582-d0bc16fd9e28)
|
147
163
|
|
148
164
|
## Upcoming releases
|
149
165
|
|
data/lib/rage/all.rb
CHANGED
@@ -26,6 +26,7 @@ require_relative "logger/text_formatter"
|
|
26
26
|
require_relative "logger/json_formatter"
|
27
27
|
require_relative "logger/logger"
|
28
28
|
|
29
|
+
require_relative "middleware/origin_validator"
|
29
30
|
require_relative "middleware/fiber_wrapper"
|
30
31
|
require_relative "middleware/cors"
|
31
32
|
require_relative "middleware/reloader"
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rage::Cable
|
4
|
+
# Create a new Cable application.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# map "/cable" do
|
8
|
+
# run Rage.cable.application
|
9
|
+
# end
|
10
|
+
def self.application
|
11
|
+
protocol = Rage.config.cable.protocol
|
12
|
+
protocol.init(__router)
|
13
|
+
|
14
|
+
handler = __build_handler(protocol)
|
15
|
+
accept_response = [0, protocol.protocol_definition, []]
|
16
|
+
|
17
|
+
application = ->(env) do
|
18
|
+
if env["rack.upgrade?"] == :websocket
|
19
|
+
env["rack.upgrade"] = handler
|
20
|
+
accept_response
|
21
|
+
else
|
22
|
+
[426, { "Connection" => "Upgrade", "Upgrade" => "websocket" }, []]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
Rage.with_middlewares(application, Rage.config.cable.middlewares)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @private
|
30
|
+
def self.__router
|
31
|
+
@__router ||= Router.new
|
32
|
+
end
|
33
|
+
|
34
|
+
# @private
|
35
|
+
def self.__build_handler(protocol)
|
36
|
+
klass = Class.new do
|
37
|
+
def initialize(protocol)
|
38
|
+
Iodine.on_state(:on_start) do
|
39
|
+
unless Fiber.scheduler
|
40
|
+
Fiber.set_scheduler(Rage::FiberScheduler.new)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
@protocol = protocol
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_open(connection)
|
48
|
+
Fiber.schedule do
|
49
|
+
@protocol.on_open(connection)
|
50
|
+
rescue => e
|
51
|
+
log_error(e)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def on_message(connection, data)
|
56
|
+
Fiber.schedule do
|
57
|
+
@protocol.on_message(connection, data)
|
58
|
+
rescue => e
|
59
|
+
log_error(e)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if protocol.respond_to?(:on_close)
|
64
|
+
def on_close(connection)
|
65
|
+
return unless ::Iodine.running?
|
66
|
+
|
67
|
+
Fiber.schedule do
|
68
|
+
@protocol.on_close(connection)
|
69
|
+
rescue => e
|
70
|
+
log_error(e)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
if protocol.respond_to?(:on_shutdown)
|
76
|
+
def on_shutdown(connection)
|
77
|
+
@protocol.on_shutdown(connection)
|
78
|
+
rescue => e
|
79
|
+
log_error(e)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def log_error(e)
|
86
|
+
Rage.logger.error("Unhandled exception has occured - #{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
klass.new(protocol)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Broadcast data directly to a named stream.
|
94
|
+
#
|
95
|
+
# @param stream [String] the name of the stream
|
96
|
+
# @param data [Object] the object to send to the clients. This will later be encoded according to the protocol used.
|
97
|
+
# @example
|
98
|
+
# Rage.cable.broadcast("chat", { message: "A new member has joined!" })
|
99
|
+
def self.broadcast(stream, data)
|
100
|
+
Rage.config.cable.protocol.broadcast(stream, data)
|
101
|
+
end
|
102
|
+
|
103
|
+
# @!parse [ruby]
|
104
|
+
# # @abstract
|
105
|
+
# class WebSocketConnection
|
106
|
+
# # Write data to the connection.
|
107
|
+
# #
|
108
|
+
# # @param data [String] the data to write
|
109
|
+
# def write(data)
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
# # Subscribe to a channel.
|
113
|
+
# #
|
114
|
+
# # @param name [String] the channel name
|
115
|
+
# def subscribe(name)
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
# # Close the connection.
|
119
|
+
# def close
|
120
|
+
# end
|
121
|
+
# end
|
122
|
+
|
123
|
+
module Protocol
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
require_relative "protocol/actioncable_v1_json"
|
128
|
+
require_relative "channel"
|
129
|
+
require_relative "connection"
|
130
|
+
require_relative "router"
|