rage-rb 1.5.1 → 1.7.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 +15 -0
- data/Gemfile +9 -7
- data/OVERVIEW.md +53 -0
- data/README.md +22 -8
- data/lib/rage/cli.rb +2 -1
- data/lib/rage/configuration.rb +1 -1
- data/lib/rage/controller/api.rb +55 -16
- data/lib/rage/cookies.rb +5 -7
- 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 +5 -5
- data/lib/rage/middleware/reloader.rb +1 -1
- data/lib/rage/params_parser.rb +1 -1
- data/lib/rage/router/backend.rb +10 -12
- data/lib/rage/router/constrainer.rb +3 -3
- data/lib/rage/router/dsl.rb +56 -32
- data/lib/rage/router/dsl_plugins/controller_action_options.rb +25 -0
- data/lib/rage/router/dsl_plugins/legacy_hash_notation.rb +46 -0
- data/lib/rage/router/dsl_plugins/legacy_root_notation.rb +14 -0
- data/lib/rage/router/dsl_plugins/named_route_helpers.rb +13 -0
- data/lib/rage/router/handler_storage.rb +2 -2
- data/lib/rage/router/strategies/host.rb +1 -1
- data/lib/rage/router/util.rb +8 -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 +3 -0
- data/rage.gemspec +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 61d3c256494b63668809f556d221c9dd86b1542ce30051c689e1f30a7b1aca00
|
4
|
+
data.tar.gz: 268fc8606e772b06985effe6918270f2ecb1c21b191bfa59ae9a74c0eeb8f459
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0136a9c93cb94dc161367ecac78b3d106404e4450fb1f4c6df8d6e38400da242fdaeebf54a7ed18ae5fc0037bea9e62ae94b5870883a2adb493e5721059a9eb8
|
7
|
+
data.tar.gz: c10509dbc8ae25ae0f1c19b35903664863fb07fb81409fd6cc16b962bde35d46f7bb552562994757b74fcc4d32f6ef9970a66f0893d0c36f11da5c27a7f2b4df
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.7.0] - 2024-07-30
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- Support `wrap_parameters` by [@alex-rogachev](https://github.com/alex-rogachev) (#89).
|
8
|
+
- Unknown environment error handling by [@cuneyter](https://github.com/cuneyter) (#95).
|
9
|
+
- Allow `rescue_from` handlers to not accept arguments (#93).
|
10
|
+
|
11
|
+
## [1.6.0] - 2024-07-15
|
12
|
+
|
13
|
+
### Added
|
14
|
+
|
15
|
+
- Support legacy route helpers (#90).
|
16
|
+
- Correctly handle internal Rails routes in `Rage.multi_application` (#91).
|
17
|
+
|
3
18
|
## [1.5.1] - 2024-05-26
|
4
19
|
|
5
20
|
### Fixed
|
data/Gemfile
CHANGED
@@ -8,12 +8,14 @@ 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
|
+
end
|
data/OVERVIEW.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
### Boot sequence and request lifecycle
|
2
|
+
|
3
|
+
The following diagram describes some of Rage's internal components and the way they interact with each other:
|
4
|
+
|
5
|
+
![overview](https://github.com/rage-rb/rage/assets/2270393/0d45bbe3-622c-4b17-b8d8-552c567fecb3)
|
6
|
+
|
7
|
+
### Executing controller actions
|
8
|
+
|
9
|
+
When `Rage::Router::DSL` parses the `config/routes.rb` file and calls the `Rage::Router::Backend` class, it registers actions and stores handler procs.
|
10
|
+
|
11
|
+
Consider we have the following controller:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
class UsersController < RageController::API
|
15
|
+
before_action :find_user
|
16
|
+
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
|
17
|
+
|
18
|
+
def show
|
19
|
+
render json: @user
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def find_user
|
25
|
+
@user = User.find(params[:id])
|
26
|
+
end
|
27
|
+
|
28
|
+
def render_not_found(_)
|
29
|
+
render status: :not_found
|
30
|
+
end
|
31
|
+
end
|
32
|
+
```
|
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#L10) the show action. Registering means defining a new method that will look like this:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
class UsersController
|
38
|
+
def __run_show
|
39
|
+
find_user
|
40
|
+
show
|
41
|
+
rescue ActiveRecord::RecordNotFound => e
|
42
|
+
render_not_found(e)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
After that, Rage will create and store a handler proc that will look exactly like this:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
->(env, params) { UsersController.new(env, params).__run_show }
|
51
|
+
```
|
52
|
+
|
53
|
+
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.
|
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
|
|
@@ -116,34 +116,48 @@ class PagesController < RageController::API
|
|
116
116
|
end
|
117
117
|
```
|
118
118
|
|
119
|
-
:information_source: **Note**: When using `Fiber.await`, it is important to wrap
|
119
|
+
:information_source: **Note**: When using `Fiber.await`, it is important to wrap every argument into a fiber using `Fiber.schedule`.
|
120
120
|
|
121
121
|
## Benchmarks
|
122
122
|
|
123
|
-
####
|
123
|
+
#### Hello World
|
124
124
|
|
125
125
|
```ruby
|
126
|
-
class
|
126
|
+
class BenchmarksController < ApplicationController
|
127
127
|
def index
|
128
128
|
render json: { hello: "world" }
|
129
129
|
end
|
130
130
|
end
|
131
131
|
```
|
132
|
-
![Requests per second](https://github.com/rage-rb/rage/assets/2270393/6c221903-e265-4c94-80e1-041f266c8f47)
|
133
132
|
|
134
|
-
|
133
|
+
![Requests per second](https://github.com/user-attachments/assets/a7f864ae-0dfb-4628-a420-265a10d8591d)
|
134
|
+
|
135
|
+
#### Waiting on I/O
|
135
136
|
|
136
137
|
```ruby
|
137
138
|
require "net/http"
|
138
139
|
|
139
|
-
class
|
140
|
+
class BenchmarksController < ApplicationController
|
140
141
|
def index
|
141
142
|
Net::HTTP.get(URI("<endpoint-that-responds-in-one-second>"))
|
142
143
|
head :ok
|
143
144
|
end
|
144
145
|
end
|
145
146
|
```
|
146
|
-
|
147
|
+
|
148
|
+
![Time to complete 100 requests](https://github.com/user-attachments/assets/4f4feda3-bd88-43d8-8999-268534c2f9de)
|
149
|
+
|
150
|
+
#### Using ActiveRecord
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
class BenchmarksController < ApplicationController
|
154
|
+
def show
|
155
|
+
render json: World.find(rand(10_000))
|
156
|
+
end
|
157
|
+
end
|
158
|
+
```
|
159
|
+
|
160
|
+
![Requests per second-2](https://github.com/user-attachments/assets/b7ee0bff-e7c8-4fd4-a565-ce0b67a6320e)
|
147
161
|
|
148
162
|
## Upcoming releases
|
149
163
|
|
data/lib/rage/cli.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "thor"
|
3
4
|
require "rack"
|
4
5
|
|
@@ -40,7 +41,7 @@ module Rage
|
|
40
41
|
::Iodine.start
|
41
42
|
end
|
42
43
|
|
43
|
-
desc
|
44
|
+
desc "routes", "List all routes."
|
44
45
|
option :grep, aliases: "-g", desc: "Filter routes by pattern"
|
45
46
|
option :help, aliases: "-h", desc: "Show this message."
|
46
47
|
def routes
|
data/lib/rage/configuration.rb
CHANGED
@@ -114,7 +114,7 @@ class Rage::Configuration
|
|
114
114
|
def config = self
|
115
115
|
|
116
116
|
def log_formatter=(formatter)
|
117
|
-
raise "Custom log formatter should respond to `#call`" unless formatter.respond_to?(:call)
|
117
|
+
raise ArgumentError, "Custom log formatter should respond to `#call`" unless formatter.respond_to?(:call)
|
118
118
|
@log_formatter = formatter
|
119
119
|
end
|
120
120
|
|
data/lib/rage/controller/api.rb
CHANGED
@@ -7,6 +7,7 @@ class RageController::API
|
|
7
7
|
# registering means defining a new method which calls the action, makes additional calls (e.g. before actions) and
|
8
8
|
# sends a correct response down to the server;
|
9
9
|
# returns the name of the newly defined method;
|
10
|
+
# rubocop:disable Layout/IndentationWidth, Layout/EndAlignment, Layout/HeredocIndentation
|
10
11
|
def __register_action(action)
|
11
12
|
raise Rage::Errors::RouterError, "The action '#{action}' could not be found for #{self}" unless method_defined?(action)
|
12
13
|
|
@@ -65,7 +66,7 @@ class RageController::API
|
|
65
66
|
lines = @__rescue_handlers.map do |klasses, handler|
|
66
67
|
<<~RUBY
|
67
68
|
rescue #{klasses.join(", ")} => __e
|
68
|
-
#{handler}(__e)
|
69
|
+
#{instance_method(handler).arity == 0 ? handler : "#{handler}(__e)"}
|
69
70
|
[@__status, @__headers, @__body]
|
70
71
|
RUBY
|
71
72
|
end
|
@@ -77,7 +78,24 @@ class RageController::API
|
|
77
78
|
|
78
79
|
activerecord_loaded = defined?(::ActiveRecord)
|
79
80
|
|
80
|
-
|
81
|
+
wrap_parameters_chunk = if __wrap_parameters_key
|
82
|
+
<<~RUBY
|
83
|
+
wrap_key = self.class.__wrap_parameters_key
|
84
|
+
if !@__params.key?(wrap_key) && @__env["CONTENT_TYPE"]
|
85
|
+
wrap_options = self.class.__wrap_parameters_options
|
86
|
+
wrapped_params = if wrap_options[:include].any?
|
87
|
+
@__params.slice(*wrap_options[:include])
|
88
|
+
else
|
89
|
+
params_to_exclude_by_default = %i[action controller]
|
90
|
+
@__params.except(*(wrap_options[:exclude] + params_to_exclude_by_default))
|
91
|
+
end
|
92
|
+
|
93
|
+
@__params[wrap_key] = wrapped_params
|
94
|
+
end
|
95
|
+
RUBY
|
96
|
+
end
|
97
|
+
|
98
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
81
99
|
def __run_#{action}
|
82
100
|
#{if activerecord_loaded
|
83
101
|
<<~RUBY
|
@@ -85,6 +103,7 @@ class RageController::API
|
|
85
103
|
RUBY
|
86
104
|
end}
|
87
105
|
|
106
|
+
#{wrap_parameters_chunk}
|
88
107
|
#{before_actions_chunk}
|
89
108
|
#{action}
|
90
109
|
|
@@ -119,9 +138,11 @@ class RageController::API
|
|
119
138
|
end
|
120
139
|
RUBY
|
121
140
|
end
|
141
|
+
# rubocop:enable all
|
122
142
|
|
123
143
|
# @private
|
124
144
|
attr_writer :__before_actions, :__after_actions, :__rescue_handlers
|
145
|
+
attr_accessor :__wrap_parameters_key, :__wrap_parameters_options
|
125
146
|
|
126
147
|
# @private
|
127
148
|
# pass the variable down to the child; the child will continue to use it until changes need to be made;
|
@@ -130,6 +151,8 @@ class RageController::API
|
|
130
151
|
klass.__before_actions = @__before_actions.freeze
|
131
152
|
klass.__after_actions = @__after_actions.freeze
|
132
153
|
klass.__rescue_handlers = @__rescue_handlers.freeze
|
154
|
+
klass.__wrap_parameters_key = __wrap_parameters_key
|
155
|
+
klass.__wrap_parameters_options = __wrap_parameters_options
|
133
156
|
end
|
134
157
|
|
135
158
|
# @private
|
@@ -151,24 +174,23 @@ class RageController::API
|
|
151
174
|
# Register a global exception handler. Handlers are inherited and matched from bottom to top.
|
152
175
|
#
|
153
176
|
# @param klasses [Class, Array<Class>] exception classes to watch on
|
154
|
-
# @param with [Symbol] the name of a handler method.
|
177
|
+
# @param with [Symbol] the name of a handler method. Alternatively, you can pass a block.
|
155
178
|
# @example
|
156
179
|
# rescue_from User::NotAuthorized, with: :deny_access
|
157
180
|
#
|
158
|
-
# def deny_access
|
181
|
+
# def deny_access
|
159
182
|
# head :forbidden
|
160
183
|
# end
|
161
184
|
# @example
|
162
|
-
# rescue_from User::NotAuthorized do |
|
163
|
-
#
|
185
|
+
# rescue_from User::NotAuthorized do |exception|
|
186
|
+
# render json: { message: exception.message }, status: :forbidden
|
164
187
|
# end
|
165
|
-
# @note Unlike in Rails, the handler must always take an argument. Use `_` if you don't care about the actual exception.
|
166
188
|
def rescue_from(*klasses, with: nil, &block)
|
167
189
|
unless with
|
168
190
|
if block_given?
|
169
191
|
with = define_tmp_method(block)
|
170
192
|
else
|
171
|
-
raise "No handler provided. Pass the `with` keyword argument or provide a block."
|
193
|
+
raise ArgumentError, "No handler provided. Pass the `with` keyword argument or provide a block."
|
172
194
|
end
|
173
195
|
end
|
174
196
|
|
@@ -183,7 +205,7 @@ class RageController::API
|
|
183
205
|
|
184
206
|
# Register a new `before_action` hook. Calls with the same `action_name` will overwrite the previous ones.
|
185
207
|
#
|
186
|
-
# @param action_name [
|
208
|
+
# @param action_name [Symbol, nil] the name of the callback to add
|
187
209
|
# @param [Hash] opts action options
|
188
210
|
# @option opts [Symbol, Array<Symbol>] :only restrict the callback to run only for specific actions
|
189
211
|
# @option opts [Symbol, Array<Symbol>] :except restrict the callback to run for all actions except specified
|
@@ -215,7 +237,7 @@ class RageController::API
|
|
215
237
|
|
216
238
|
if @__before_actions.nil?
|
217
239
|
@__before_actions = [action]
|
218
|
-
elsif i = @__before_actions.find_index { |a| a[:name] == action_name }
|
240
|
+
elsif (i = @__before_actions.find_index { |a| a[:name] == action_name })
|
219
241
|
@__before_actions[i] = action
|
220
242
|
else
|
221
243
|
@__before_actions << action
|
@@ -224,7 +246,7 @@ class RageController::API
|
|
224
246
|
|
225
247
|
# Register a new `after_action` hook. Calls with the same `action_name` will overwrite the previous ones.
|
226
248
|
#
|
227
|
-
# @param action_name [
|
249
|
+
# @param action_name [Symbol, nil] the name of the callback to add
|
228
250
|
# @param [Hash] opts action options
|
229
251
|
# @option opts [Symbol, Array<Symbol>] :only restrict the callback to run only for specific actions
|
230
252
|
# @option opts [Symbol, Array<Symbol>] :except restrict the callback to run for all actions except specified
|
@@ -241,7 +263,7 @@ class RageController::API
|
|
241
263
|
|
242
264
|
if @__after_actions.nil?
|
243
265
|
@__after_actions = [action]
|
244
|
-
elsif i = @__after_actions.find_index { |a| a[:name] == action_name }
|
266
|
+
elsif (i = @__after_actions.find_index { |a| a[:name] == action_name })
|
245
267
|
@__after_actions[i] = action
|
246
268
|
else
|
247
269
|
@__after_actions << action
|
@@ -250,14 +272,14 @@ class RageController::API
|
|
250
272
|
|
251
273
|
# Prevent a `before_action` hook from running.
|
252
274
|
#
|
253
|
-
# @param action_name [
|
275
|
+
# @param action_name [Symbol] the name of the callback to skip
|
254
276
|
# @param only [Symbol, Array<Symbol>] restrict the callback to be skipped only for specific actions
|
255
277
|
# @param except [Symbol, Array<Symbol>] restrict the callback to be skipped for all actions except specified
|
256
278
|
# @example
|
257
279
|
# skip_before_action :find_photo, only: :create
|
258
280
|
def skip_before_action(action_name, only: nil, except: nil)
|
259
281
|
i = @__before_actions&.find_index { |a| a[:name] == action_name }
|
260
|
-
raise "The following action was specified to be skipped but couldn't be found: #{self}##{action_name}" unless i
|
282
|
+
raise ArgumentError, "The following action was specified to be skipped but couldn't be found: #{self}##{action_name}" unless i
|
261
283
|
|
262
284
|
@__before_actions = @__before_actions.dup if @__before_actions.frozen?
|
263
285
|
|
@@ -277,6 +299,23 @@ class RageController::API
|
|
277
299
|
@__before_actions[i] = action
|
278
300
|
end
|
279
301
|
|
302
|
+
# Initialize controller params wrapping into a nested hash.
|
303
|
+
# If initialized, params wrapping logic will be added to the controller.
|
304
|
+
# Params get wrapped only if the CONTENT_TYPE header is present and params hash doesn't contain a param that
|
305
|
+
# has the same name as the wrapper key.
|
306
|
+
#
|
307
|
+
# @param key [Symbol] key that the wrapped params hash will nested under
|
308
|
+
# @param include [Array] array of params that should be included to the wrapped params hash
|
309
|
+
# @param exclude [Array] array of params that should be excluded from the wrapped params hash
|
310
|
+
# @example
|
311
|
+
# wrap_parameters :user, include: %i[name age]
|
312
|
+
# @example
|
313
|
+
# wrap_parameters :user, exclude: %i[address]
|
314
|
+
def wrap_parameters(key, include: [], exclude: [])
|
315
|
+
@__wrap_parameters_key = key
|
316
|
+
@__wrap_parameters_options = { include:, exclude: }
|
317
|
+
end
|
318
|
+
|
280
319
|
private
|
281
320
|
|
282
321
|
# used by `before_action` and `after_action`
|
@@ -284,10 +323,10 @@ class RageController::API
|
|
284
323
|
if block_given?
|
285
324
|
action_name = define_tmp_method(block)
|
286
325
|
elsif action_name.nil?
|
287
|
-
raise "No handler provided. Pass the `action_name` parameter or provide a block."
|
326
|
+
raise ArgumentError, "No handler provided. Pass the `action_name` parameter or provide a block."
|
288
327
|
end
|
289
328
|
|
290
|
-
|
329
|
+
_only, _except, _if, _unless = opts.values_at(:only, :except, :if, :unless)
|
291
330
|
|
292
331
|
action = {
|
293
332
|
name: action_name,
|
data/lib/rage/cookies.rb
CHANGED
@@ -97,7 +97,7 @@ class Rage::Cookies
|
|
97
97
|
return
|
98
98
|
end
|
99
99
|
|
100
|
-
if domain = value[:domain]
|
100
|
+
if (domain = value[:domain])
|
101
101
|
host = @env["HTTP_HOST"]
|
102
102
|
|
103
103
|
_domain = if domain.is_a?(String)
|
@@ -141,7 +141,7 @@ class Rage::Cookies
|
|
141
141
|
return @request_cookies if @parsed
|
142
142
|
|
143
143
|
@parsed = true
|
144
|
-
if cookie_header = @env["HTTP_COOKIE"]
|
144
|
+
if (cookie_header = @env["HTTP_COOKIE"])
|
145
145
|
cookie_header.split(/; */n).each do |cookie|
|
146
146
|
next if cookie.empty?
|
147
147
|
key, value = cookie.split("=", 2).yield_self { |k, _| [k.to_sym, _] }
|
@@ -193,7 +193,7 @@ class Rage::Cookies
|
|
193
193
|
nil
|
194
194
|
rescue RbNaCl::CryptoError
|
195
195
|
i ||= 0
|
196
|
-
if box = fallback_boxes[i]
|
196
|
+
if (box = fallback_boxes[i])
|
197
197
|
i += 1
|
198
198
|
retry
|
199
199
|
end
|
@@ -230,10 +230,8 @@ class Rage::Cookies
|
|
230
230
|
end
|
231
231
|
|
232
232
|
def fallback_boxes
|
233
|
-
@fallback_boxes ||=
|
234
|
-
|
235
|
-
RbNaCl::SimpleBox.from_secret_key(RbNaCl::Hash.blake2b(key, digest_size: 32, salt: SALT))
|
236
|
-
end
|
233
|
+
@fallback_boxes ||= Rage.config.fallback_secret_key_base.map do |key|
|
234
|
+
RbNaCl::SimpleBox.from_secret_key(RbNaCl::Hash.blake2b(key, digest_size: 32, salt: SALT))
|
237
235
|
end
|
238
236
|
end
|
239
237
|
end # class << self
|
@@ -97,7 +97,7 @@ module Rage::Ext::ActiveRecord::ConnectionPool
|
|
97
97
|
|
98
98
|
# Signal that the fiber is finished with the current connection and it can be returned to the pool.
|
99
99
|
def release_connection(owner = Fiber.current)
|
100
|
-
if conn = @__in_use.delete(owner)
|
100
|
+
if (conn = @__in_use.delete(owner))
|
101
101
|
conn.__idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
102
102
|
@__connections << conn
|
103
103
|
Iodine.publish("ext:ar-connection-released", "", Iodine::PubSub::PROCESS) if @__blocked.length > 0
|
data/lib/rage/fiber.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
##
|
4
4
|
# Rage provides a simple and efficient API to wait on several instances of IO at the same time - {Fiber.await}.
|
5
|
-
#
|
5
|
+
#
|
6
6
|
# Let's say we have the following controller:
|
7
7
|
# ```ruby
|
8
8
|
# class UsersController < RageController::API
|
@@ -34,7 +34,7 @@
|
|
34
34
|
# end
|
35
35
|
# ```
|
36
36
|
# With this change, if each request takes 1 second to execute, the total execution time will still be 1 second.
|
37
|
-
#
|
37
|
+
#
|
38
38
|
# ## Creating fibers
|
39
39
|
# Many developers see fibers as "lightweight threads" that should be used in conjunction with fiber pools, the same way we use thread pools for threads.<br>
|
40
40
|
# Instead, it makes sense to think of fibers as regular Ruby objects. We don't use a pool of arrays when we need to create an array - we create a new object and let Ruby and the GC do their job.<br>
|
@@ -68,7 +68,7 @@ class Fiber
|
|
68
68
|
@__rage_id = object_id.to_s
|
69
69
|
end
|
70
70
|
|
71
|
-
|
71
|
+
# @private
|
72
72
|
def __get_id
|
73
73
|
@__rage_id
|
74
74
|
end
|
data/lib/rage/fiber_scheduler.rb
CHANGED
@@ -66,7 +66,7 @@ class Rage::FiberScheduler
|
|
66
66
|
end
|
67
67
|
|
68
68
|
# TODO: GC works a little strange with this closure;
|
69
|
-
#
|
69
|
+
#
|
70
70
|
# def timeout_after(duration, exception_class = Timeout::Error, *exception_arguments, &block)
|
71
71
|
# fiber, block_status = Fiber.current, :running
|
72
72
|
# ::Iodine.run_after((duration * 1000).to_i) do
|
@@ -15,7 +15,7 @@ class Rage::JSONFormatter
|
|
15
15
|
context.each { |k, v| context_msg << "\"#{k}\":#{v.to_json}," }
|
16
16
|
end
|
17
17
|
|
18
|
-
if final = logger[:final]
|
18
|
+
if (final = logger[:final])
|
19
19
|
params, env = final[:params], final[:env]
|
20
20
|
if params && params[:controller]
|
21
21
|
return "{\"tags\":[\"#{tags[0]}\"],\"timestamp\":\"#{timestamp}\",\"pid\":\"#{@pid}\",\"level\":\"info\",\"method\":\"#{env["REQUEST_METHOD"]}\",\"path\":\"#{env["PATH_INFO"]}\",\"controller\":\"#{Rage::Router::Util.path_to_name(params[:controller])}\",\"action\":\"#{params[:action]}\",#{context_msg}\"status\":#{final[:response][0]},\"duration\":#{final[:duration]}}\n"
|
data/lib/rage/logger/logger.rb
CHANGED
@@ -8,7 +8,7 @@ require "logger"
|
|
8
8
|
# [fecbba0735355738] timestamp=2023-10-19T11:12:56+00:00 pid=1825 level=info message=hello
|
9
9
|
# ```
|
10
10
|
# In the log entry above, `timestamp`, `pid`, `level`, and `message` are keys, while `fecbba0735355738` is a tag.
|
11
|
-
#
|
11
|
+
#
|
12
12
|
# Use {tagged} to add custom tags to an entry:
|
13
13
|
# ```ruby
|
14
14
|
# Rage.logger.tagged("ApiCall") do
|
@@ -15,7 +15,7 @@ class Rage::TextFormatter
|
|
15
15
|
context.each { |k, v| context_msg << "#{k}=#{v} " }
|
16
16
|
end
|
17
17
|
|
18
|
-
if final = logger[:final]
|
18
|
+
if (final = logger[:final])
|
19
19
|
params, env = final[:params], final[:env]
|
20
20
|
if params && params[:controller]
|
21
21
|
return "[#{tags[0]}] timestamp=#{timestamp} pid=#{@pid} level=info method=#{env["REQUEST_METHOD"]} path=#{env["PATH_INFO"]} controller=#{Rage::Router::Util.path_to_name(params[:controller])} action=#{params[:action]} #{context_msg}status=#{final[:response][0]} duration=#{final[:duration]}\n"
|
data/lib/rage/middleware/cors.rb
CHANGED
@@ -19,7 +19,7 @@ class Rage::Cors
|
|
19
19
|
|
20
20
|
response
|
21
21
|
ensure
|
22
|
-
if !$! && origin = @cors_check.call(env)
|
22
|
+
if !$! && (origin = @cors_check.call(env))
|
23
23
|
headers = response[1]
|
24
24
|
headers["Access-Control-Allow-Origin"] = origin
|
25
25
|
if @origins != "*"
|
@@ -64,7 +64,7 @@ class Rage::Cors
|
|
64
64
|
@methods = if methods != "*"
|
65
65
|
methods.map! { |method| method.to_s.upcase }.tap { |m|
|
66
66
|
if (invalid_methods = m - @default_methods).any?
|
67
|
-
raise "Unsupported method passed to Rage::Cors: #{invalid_methods[0]}"
|
67
|
+
raise ArgumentError, "Unsupported method passed to Rage::Cors: #{invalid_methods[0]}"
|
68
68
|
end
|
69
69
|
}.join(", ")
|
70
70
|
elsif @allow_credentials
|
@@ -74,8 +74,8 @@ class Rage::Cors
|
|
74
74
|
end
|
75
75
|
|
76
76
|
if @allow_credentials
|
77
|
-
raise "Rage::Cors requires you to explicitly list allowed headers when using `allow_credentials: true`" if @allow_headers == "*"
|
78
|
-
raise "Rage::Cors requires you to explicitly list exposed headers when using `allow_credentials: true`" if @expose_headers == "*"
|
77
|
+
raise ArgumentError, "Rage::Cors requires you to explicitly list allowed headers when using `allow_credentials: true`" if @allow_headers == "*"
|
78
|
+
raise ArgumentError, "Rage::Cors requires you to explicitly list exposed headers when using `allow_credentials: true`" if @expose_headers == "*"
|
79
79
|
end
|
80
80
|
|
81
81
|
@origins = []
|
@@ -99,7 +99,7 @@ class Rage::Cors
|
|
99
99
|
def create_headers
|
100
100
|
headers = {
|
101
101
|
"Access-Control-Allow-Origin" => "",
|
102
|
-
"Access-Control-Allow-Methods" => @methods
|
102
|
+
"Access-Control-Allow-Methods" => @methods
|
103
103
|
}
|
104
104
|
|
105
105
|
if @allow_headers
|
data/lib/rage/params_parser.rb
CHANGED
data/lib/rage/router/backend.rb
CHANGED
@@ -20,7 +20,7 @@ class Rage::Router::Backend
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def mount(path, handler, methods)
|
23
|
-
raise "Mount handler should respond to `call`" unless handler.respond_to?(:call)
|
23
|
+
raise ArgumentError, "Mount handler should respond to `call`" unless handler.respond_to?(:call)
|
24
24
|
|
25
25
|
raw_handler = handler
|
26
26
|
is_sidekiq = handler.respond_to?(:name) && handler.name == "Sidekiq::Web"
|
@@ -51,8 +51,8 @@ class Rage::Router::Backend
|
|
51
51
|
def on(method, path, handler, constraints: {}, defaults: nil)
|
52
52
|
raise "Path could not be empty" if path&.empty?
|
53
53
|
|
54
|
-
if match_index = (path =~ OPTIONAL_PARAM_REGEXP)
|
55
|
-
raise "Optional Parameter has to be the last parameter of the path" if path.length != match_index + $&.length
|
54
|
+
if (match_index = (path =~ OPTIONAL_PARAM_REGEXP))
|
55
|
+
raise ArgumentError, "Optional Parameter has to be the last parameter of the path" if path.length != match_index + $&.length
|
56
56
|
|
57
57
|
path_full = path.sub(OPTIONAL_PARAM_REGEXP, "/#{$1}")
|
58
58
|
path_optional = path.sub(OPTIONAL_PARAM_REGEXP, "")
|
@@ -65,7 +65,7 @@ class Rage::Router::Backend
|
|
65
65
|
meta = { raw_handler: handler }
|
66
66
|
|
67
67
|
if handler.is_a?(String)
|
68
|
-
raise "Invalid route handler format, expected to match the 'controller#action' pattern" unless handler =~ STRING_HANDLER_REGEXP
|
68
|
+
raise ArgumentError, "Invalid route handler format, expected to match the 'controller#action' pattern" unless handler =~ STRING_HANDLER_REGEXP
|
69
69
|
|
70
70
|
controller, action = Rage::Router::Util.path_to_class($1), $2
|
71
71
|
|
@@ -81,7 +81,7 @@ class Rage::Router::Backend
|
|
81
81
|
handler = ->(_, _) { [404, { "X-Cascade" => "pass" }, []] }
|
82
82
|
end
|
83
83
|
else
|
84
|
-
raise "Non-string route handler should respond to `call`" unless handler.respond_to?(:call)
|
84
|
+
raise ArgumentError, "Non-string route handler should respond to `call`" unless handler.respond_to?(:call)
|
85
85
|
# while regular handlers are expected to be called with the `env` and `params` objects,
|
86
86
|
# lambda handlers expect just `env` as an argument;
|
87
87
|
# TODO: come up with something nicer?
|
@@ -189,7 +189,7 @@ class Rage::Router::Backend
|
|
189
189
|
params << "*"
|
190
190
|
current_node = current_node.create_wildcard_child
|
191
191
|
parent_node_path_index = i + 1
|
192
|
-
raise "Wildcard must be the last character in the route" if i != pattern.length - 1
|
192
|
+
raise ArgumentError, "Wildcard must be the last character in the route" if i != pattern.length - 1
|
193
193
|
end
|
194
194
|
|
195
195
|
i += 1
|
@@ -200,12 +200,10 @@ class Rage::Router::Backend
|
|
200
200
|
end
|
201
201
|
|
202
202
|
@routes.each do |existing_route|
|
203
|
-
if
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
)
|
208
|
-
raise "Method '#{method}' already declared for route '#{pattern}' with constraints '#{constraints.inspect}'"
|
203
|
+
if existing_route[:method] == method &&
|
204
|
+
existing_route[:pattern] == pattern &&
|
205
|
+
existing_route[:constraints] == constraints
|
206
|
+
raise ArgumentError, "Method '#{method}' already declared for route '#{pattern}' with constraints '#{constraints.inspect}'"
|
209
207
|
end
|
210
208
|
end
|
211
209
|
|
@@ -45,14 +45,14 @@ class Rage::Router::Constrainer
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def new_store_for_constraint(constraint)
|
48
|
-
raise "No strategy registered for constraint key '#{constraint}'" unless @strategies[constraint]
|
48
|
+
raise ArgumentError, "No strategy registered for constraint key '#{constraint}'" unless @strategies[constraint]
|
49
49
|
@strategies[constraint].storage
|
50
50
|
end
|
51
51
|
|
52
52
|
def validate_constraints(constraints)
|
53
53
|
constraints.each do |key, value|
|
54
54
|
strategy = @strategies[key]
|
55
|
-
raise "No strategy registered for constraint key '#{key}'" unless strategy
|
55
|
+
raise ArgumentError, "No strategy registered for constraint key '#{key}'" unless strategy
|
56
56
|
|
57
57
|
strategy.validate(value)
|
58
58
|
end
|
@@ -73,7 +73,7 @@ class Rage::Router::Constrainer
|
|
73
73
|
if key == :host
|
74
74
|
lines << " host: env['HTTP_HOST'.freeze],"
|
75
75
|
else
|
76
|
-
raise
|
76
|
+
raise ArgumentError, "unknown non-custom strategy for compiling constraint derivation function"
|
77
77
|
end
|
78
78
|
else
|
79
79
|
lines << " #{strategy.name}: @strategies[#{key}].derive_constraint(env),"
|
data/lib/rage/router/dsl.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "dsl_plugins/legacy_hash_notation"
|
4
|
+
require_relative "dsl_plugins/legacy_root_notation"
|
5
|
+
require_relative "dsl_plugins/named_route_helpers"
|
6
|
+
require_relative "dsl_plugins/controller_action_options"
|
7
|
+
|
3
8
|
class Rage::Router::DSL
|
4
9
|
def initialize(router)
|
5
10
|
@router = router
|
@@ -47,6 +52,11 @@ class Rage::Router::DSL
|
|
47
52
|
# resources :posts
|
48
53
|
# end
|
49
54
|
class Handler
|
55
|
+
prepend Rage::Router::DSLPlugins::ControllerActionOptions
|
56
|
+
prepend Rage::Router::DSLPlugins::NamedRouteHelpers
|
57
|
+
prepend Rage::Router::DSLPlugins::LegacyHashNotation
|
58
|
+
prepend Rage::Router::DSLPlugins::LegacyRootNotation
|
59
|
+
|
50
60
|
# @private
|
51
61
|
def initialize(router)
|
52
62
|
@router = router
|
@@ -67,12 +77,13 @@ class Rage::Router::DSL
|
|
67
77
|
# @param to [String] the route handler in the format of "controller#action"
|
68
78
|
# @param constraints [Hash] a hash of constraints for the route
|
69
79
|
# @param defaults [Hash] a hash of default parameters for the route
|
80
|
+
# @param on [nil, :member, :collection] a shorthand for wrapping routes in a specific RESTful context
|
70
81
|
# @example
|
71
82
|
# get "/photos/:id", to: "photos#show", constraints: { host: /myhost/ }
|
72
83
|
# @example
|
73
84
|
# get "/photos(/:id)", to: "photos#show", defaults: { id: "-1" }
|
74
|
-
def get(path, to: nil, constraints: nil, defaults: nil)
|
75
|
-
__on("GET", path, to, constraints, defaults)
|
85
|
+
def get(path, to: nil, constraints: nil, defaults: nil, on: nil)
|
86
|
+
__with_on_scope(on) { __on("GET", path, to, constraints, defaults) }
|
76
87
|
end
|
77
88
|
|
78
89
|
# Register a new POST route.
|
@@ -81,12 +92,13 @@ class Rage::Router::DSL
|
|
81
92
|
# @param to [String] the route handler in the format of "controller#action"
|
82
93
|
# @param constraints [Hash] a hash of constraints for the route
|
83
94
|
# @param defaults [Hash] a hash of default parameters for the route
|
95
|
+
# @param on [nil, :member, :collection] a shorthand for wrapping routes in a specific RESTful context
|
84
96
|
# @example
|
85
97
|
# post "/photos", to: "photos#create", constraints: { host: /myhost/ }
|
86
98
|
# @example
|
87
99
|
# post "/photos", to: "photos#create", defaults: { format: "jpg" }
|
88
|
-
def post(path, to: nil, constraints: nil, defaults: nil)
|
89
|
-
__on("POST", path, to, constraints, defaults)
|
100
|
+
def post(path, to: nil, constraints: nil, defaults: nil, on: nil)
|
101
|
+
__with_on_scope(on) { __on("POST", path, to, constraints, defaults) }
|
90
102
|
end
|
91
103
|
|
92
104
|
# Register a new PUT route.
|
@@ -95,12 +107,13 @@ class Rage::Router::DSL
|
|
95
107
|
# @param to [String] the route handler in the format of "controller#action"
|
96
108
|
# @param constraints [Hash] a hash of constraints for the route
|
97
109
|
# @param defaults [Hash] a hash of default parameters for the route
|
110
|
+
# @param on [nil, :member, :collection] a shorthand for wrapping routes in a specific RESTful context
|
98
111
|
# @example
|
99
112
|
# put "/photos/:id", to: "photos#update", constraints: { host: /myhost/ }
|
100
113
|
# @example
|
101
114
|
# put "/photos(/:id)", to: "photos#update", defaults: { id: "-1" }
|
102
|
-
def put(path, to: nil, constraints: nil, defaults: nil)
|
103
|
-
__on("PUT", path, to, constraints, defaults)
|
115
|
+
def put(path, to: nil, constraints: nil, defaults: nil, on: nil)
|
116
|
+
__with_on_scope(on) { __on("PUT", path, to, constraints, defaults) }
|
104
117
|
end
|
105
118
|
|
106
119
|
# Register a new PATCH route.
|
@@ -109,12 +122,13 @@ class Rage::Router::DSL
|
|
109
122
|
# @param to [String] the route handler in the format of "controller#action"
|
110
123
|
# @param constraints [Hash] a hash of constraints for the route
|
111
124
|
# @param defaults [Hash] a hash of default parameters for the route
|
125
|
+
# @param on [nil, :member, :collection] a shorthand for wrapping routes in a specific RESTful context
|
112
126
|
# @example
|
113
127
|
# patch "/photos/:id", to: "photos#update", constraints: { host: /myhost/ }
|
114
128
|
# @example
|
115
129
|
# patch "/photos(/:id)", to: "photos#update", defaults: { id: "-1" }
|
116
|
-
def patch(path, to: nil, constraints: nil, defaults: nil)
|
117
|
-
__on("PATCH", path, to, constraints, defaults)
|
130
|
+
def patch(path, to: nil, constraints: nil, defaults: nil, on: nil)
|
131
|
+
__with_on_scope(on) { __on("PATCH", path, to, constraints, defaults) }
|
118
132
|
end
|
119
133
|
|
120
134
|
# Register a new DELETE route.
|
@@ -123,12 +137,13 @@ class Rage::Router::DSL
|
|
123
137
|
# @param to [String] the route handler in the format of "controller#action"
|
124
138
|
# @param constraints [Hash] a hash of constraints for the route
|
125
139
|
# @param defaults [Hash] a hash of default parameters for the route
|
140
|
+
# @param on [nil, :member, :collection] a shorthand for wrapping routes in a specific RESTful context
|
126
141
|
# @example
|
127
142
|
# delete "/photos/:id", to: "photos#destroy", constraints: { host: /myhost/ }
|
128
143
|
# @example
|
129
144
|
# delete "/photos(/:id)", to: "photos#destroy", defaults: { id: "-1" }
|
130
|
-
def delete(path, to: nil, constraints: nil, defaults: nil)
|
131
|
-
__on("DELETE", path, to, constraints, defaults)
|
145
|
+
def delete(path, to: nil, constraints: nil, defaults: nil, on: nil)
|
146
|
+
__with_on_scope(on) { __on("DELETE", path, to, constraints, defaults) }
|
132
147
|
end
|
133
148
|
|
134
149
|
# Register a new route pointing to '/'.
|
@@ -198,7 +213,7 @@ class Rage::Router::DSL
|
|
198
213
|
@path_prefixes << path_prefix
|
199
214
|
@module_prefixes << module_prefix
|
200
215
|
|
201
|
-
instance_eval
|
216
|
+
instance_eval(&block)
|
202
217
|
|
203
218
|
@path_prefixes.pop
|
204
219
|
@module_prefixes.pop
|
@@ -207,9 +222,9 @@ class Rage::Router::DSL
|
|
207
222
|
# Scopes a set of routes to the given default options.
|
208
223
|
#
|
209
224
|
# @param [Hash] opts scope options.
|
210
|
-
# @option opts [String] :module
|
211
|
-
# @option opts [String] :path path
|
212
|
-
# @option opts [String] :controller controller
|
225
|
+
# @option opts [String] :module the namespace for the controller
|
226
|
+
# @option opts [String] :path the path prefix for the routes
|
227
|
+
# @option opts [String] :controller scopes routes to a specific controller
|
213
228
|
# @example Route `/photos` to `Api::PhotosController`
|
214
229
|
# scope module: "api" do
|
215
230
|
# get "photos", to: "photos#index"
|
@@ -238,7 +253,7 @@ class Rage::Router::DSL
|
|
238
253
|
@module_prefixes << opts[:module] if opts[:module]
|
239
254
|
@controllers << opts[:controller] if opts[:controller]
|
240
255
|
|
241
|
-
instance_eval
|
256
|
+
instance_eval(&block)
|
242
257
|
|
243
258
|
@path_prefixes.pop if opts[:path]
|
244
259
|
@module_prefixes.pop if opts[:module]
|
@@ -254,7 +269,7 @@ class Rage::Router::DSL
|
|
254
269
|
# end
|
255
270
|
def defaults(defaults, &block)
|
256
271
|
@defaults << defaults
|
257
|
-
instance_eval
|
272
|
+
instance_eval(&block)
|
258
273
|
@defaults.pop
|
259
274
|
end
|
260
275
|
|
@@ -267,7 +282,7 @@ class Rage::Router::DSL
|
|
267
282
|
# end
|
268
283
|
def controller(controller, &block)
|
269
284
|
@controllers << controller
|
270
|
-
instance_eval
|
285
|
+
instance_eval(&block)
|
271
286
|
@controllers.pop
|
272
287
|
end
|
273
288
|
|
@@ -282,7 +297,7 @@ class Rage::Router::DSL
|
|
282
297
|
def collection(&block)
|
283
298
|
orig_path_prefixes = @path_prefixes
|
284
299
|
@path_prefixes = @path_prefixes[0...-1] if @path_prefixes.last&.start_with?(":")
|
285
|
-
instance_eval
|
300
|
+
instance_eval(&block)
|
286
301
|
@path_prefixes = orig_path_prefixes
|
287
302
|
end
|
288
303
|
|
@@ -302,13 +317,19 @@ class Rage::Router::DSL
|
|
302
317
|
@path_prefixes = [*@path_prefixes[0...-1], ":#{member_prefix}"]
|
303
318
|
end
|
304
319
|
|
305
|
-
instance_eval
|
320
|
+
instance_eval(&block)
|
306
321
|
|
307
322
|
@path_prefixes = orig_path_prefixes
|
308
323
|
end
|
309
324
|
|
310
325
|
# Automatically create REST routes for a resource.
|
311
326
|
#
|
327
|
+
# @param [Hash] opts resource options
|
328
|
+
# @option opts [String] :module the namespace for the controller
|
329
|
+
# @option opts [String] :path the path prefix for the routes
|
330
|
+
# @option opts [Symbol, Array<Symbol>] :only only generate routes for the given actions
|
331
|
+
# @option opts [Symbol, Array<Symbol>] :except generate all routes except for the given actions
|
332
|
+
# @option opts [String] :param overrides the default param name of `:id` in the URL
|
312
333
|
# @example Create five REST routes, all mapping to the `Photos` controller:
|
313
334
|
# resources :photos
|
314
335
|
# # GET /photos => photos#index
|
@@ -325,7 +346,7 @@ class Rage::Router::DSL
|
|
325
346
|
end
|
326
347
|
|
327
348
|
_module, _path, _only, _except, _param = opts.values_at(:module, :path, :only, :except, :param)
|
328
|
-
raise ":param option can't contain colons" if _param.to_s.include?(":")
|
349
|
+
raise ArgumentError, ":param option can't contain colons" if _param.to_s.include?(":")
|
329
350
|
|
330
351
|
_only = Array(_only) if _only
|
331
352
|
_except = Array(_except) if _except
|
@@ -358,17 +379,7 @@ class Rage::Router::DSL
|
|
358
379
|
# mount Sidekiq::Web => "/sidekiq"
|
359
380
|
# @example
|
360
381
|
# mount Sidekiq::Web, at: "/sidekiq", via: :get
|
361
|
-
def mount(
|
362
|
-
if args.first.is_a?(Hash)
|
363
|
-
app = args.first.keys.first
|
364
|
-
at = args.first.values.first
|
365
|
-
via = args[0][:via]
|
366
|
-
else
|
367
|
-
app = args.first
|
368
|
-
at = args[1][:at]
|
369
|
-
via = args[1][:via]
|
370
|
-
end
|
371
|
-
|
382
|
+
def mount(app, at:, via: :all)
|
372
383
|
at = "/#{at}" unless at.start_with?("/")
|
373
384
|
at = at.delete_suffix("/") if at.end_with?("/")
|
374
385
|
|
@@ -395,7 +406,7 @@ class Rage::Router::DSL
|
|
395
406
|
if @controllers.any?
|
396
407
|
to = "#{@controllers.last}##{path}"
|
397
408
|
else
|
398
|
-
raise "Missing :to key on routes definition, please check your routes."
|
409
|
+
raise ArgumentError, "Missing :to key on routes definition, please check your routes."
|
399
410
|
end
|
400
411
|
end
|
401
412
|
|
@@ -421,6 +432,19 @@ class Rage::Router::DSL
|
|
421
432
|
end
|
422
433
|
end
|
423
434
|
|
435
|
+
def __with_on_scope(on, &block)
|
436
|
+
case on
|
437
|
+
when nil
|
438
|
+
block.call
|
439
|
+
when :member
|
440
|
+
member(&block)
|
441
|
+
when :collection
|
442
|
+
collection(&block)
|
443
|
+
else
|
444
|
+
raise ArgumentError, "Unknown scope :#{on} given to :on"
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
424
448
|
def to_singular(str)
|
425
449
|
@active_support_loaded ||= str.respond_to?(:singularize) || :false
|
426
450
|
return str.singularize if @active_support_loaded != :false
|
@@ -0,0 +1,25 @@
|
|
1
|
+
##
|
2
|
+
# Support the `:controller` and `:action` options.
|
3
|
+
#
|
4
|
+
# @example
|
5
|
+
# get :admins, controller: :users
|
6
|
+
# @example
|
7
|
+
# post :search, action: :index
|
8
|
+
module Rage::Router::DSLPlugins::ControllerActionOptions
|
9
|
+
%i(get post put patch delete).each do |action_name|
|
10
|
+
define_method(action_name) do |*args, **kwargs|
|
11
|
+
if args.length == 1 && !kwargs.has_key?(:to) && (kwargs.has_key?(:controller) || kwargs.has_key?(:action))
|
12
|
+
path = args[0]
|
13
|
+
controller = kwargs.delete(:controller) || @controllers.last || raise(ArgumentError, "Could not derive the controller value from the route definitions")
|
14
|
+
action = kwargs.delete(:action) || path.split("/").last
|
15
|
+
end
|
16
|
+
|
17
|
+
if controller && action
|
18
|
+
kwargs[:to] = "#{controller}##{action}"
|
19
|
+
super(path, **kwargs)
|
20
|
+
else
|
21
|
+
super(*args, **kwargs)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
##
|
2
|
+
# Support legacy URL helpers that use hashes instead of the `:to` keyword argument.
|
3
|
+
#
|
4
|
+
# @example
|
5
|
+
# get "/photos/:id" => "photos#show"
|
6
|
+
# @example
|
7
|
+
# mount Sidekiq::Web => "/sidekiq"
|
8
|
+
# @example
|
9
|
+
# get "search" => :index
|
10
|
+
# @example
|
11
|
+
# get "admin_users" => "users"
|
12
|
+
module Rage::Router::DSLPlugins::LegacyHashNotation
|
13
|
+
%i(get post put patch delete).each do |action_name|
|
14
|
+
define_method(action_name) do |*args, **kwargs|
|
15
|
+
if args.empty? && !kwargs.empty?
|
16
|
+
path, handler = kwargs.first
|
17
|
+
|
18
|
+
to = if handler.is_a?(Symbol)
|
19
|
+
raise ArgumentError, "Could not derive the controller value from the route definitions" if @controllers.empty?
|
20
|
+
"#{@controllers.last}##{handler}"
|
21
|
+
elsif handler.is_a?(String) && !handler.include?("#")
|
22
|
+
"#{handler}##{path.split("/").last}"
|
23
|
+
elsif handler.is_a?(String)
|
24
|
+
handler
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
if path && to
|
29
|
+
options = kwargs.except(path).merge(to: to)
|
30
|
+
super(path, **options)
|
31
|
+
else
|
32
|
+
super(*args, **kwargs)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def mount(*args, **kwargs)
|
38
|
+
if args.empty? && !kwargs.empty?
|
39
|
+
app, at = kwargs.first
|
40
|
+
options = kwargs.except(app).merge(at: at)
|
41
|
+
super(app, **options)
|
42
|
+
else
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
##
|
2
|
+
# Support legacy root helpers that don't use the `:to` keyword argument.
|
3
|
+
#
|
4
|
+
# @example
|
5
|
+
# root "photos#index"
|
6
|
+
module Rage::Router::DSLPlugins::LegacyRootNotation
|
7
|
+
def root(*args, **kwargs)
|
8
|
+
if args.length == 1 && args[0].is_a?(String) && kwargs.empty?
|
9
|
+
super(to: args[0])
|
10
|
+
else
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
##
|
2
|
+
# Support the `as` option. As Rage currently doesn't generate named route helpers, we simply ignore it.
|
3
|
+
#
|
4
|
+
# @example
|
5
|
+
# get "/photos/:id", to: "photos#show", as: :user_photos
|
6
|
+
module Rage::Router::DSLPlugins::NamedRouteHelpers
|
7
|
+
%i(get post put patch delete).each do |action_name|
|
8
|
+
define_method(action_name) do |*args, **kwargs|
|
9
|
+
kwargs.delete(:as)
|
10
|
+
super(*args, **kwargs)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -35,7 +35,7 @@ class Rage::Router::HandlerStorage
|
|
35
35
|
end
|
36
36
|
|
37
37
|
if @handlers.length >= 32
|
38
|
-
raise "Limit reached: a maximum of 32 route handlers per node allowed when there are constraints"
|
38
|
+
raise ArgumentError, "Limit reached: a maximum of 32 route handlers per node allowed when there are constraints"
|
39
39
|
end
|
40
40
|
|
41
41
|
@handlers << handler_object
|
@@ -64,7 +64,7 @@ class Rage::Router::HandlerStorage
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
-
eval "->(param_values) { { #{lines.join(
|
67
|
+
eval "->(param_values) { { #{lines.join(",")} } }"
|
68
68
|
end
|
69
69
|
|
70
70
|
def get_handler_matching_constraints(_derived_constraints)
|
data/lib/rage/router/util.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Rage::Router::Util
|
2
4
|
class << self
|
3
5
|
# converts controller name in a path form into a class
|
@@ -40,9 +42,13 @@ class Rage::Router::Util
|
|
40
42
|
|
41
43
|
def call(env)
|
42
44
|
result = @rage_app.call(env)
|
43
|
-
return result if result[0] == :__http_defer__
|
45
|
+
return result if result[0] == :__http_defer__
|
44
46
|
|
45
|
-
|
47
|
+
if result[1]["X-Cascade"] == "pass" || env["PATH_INFO"].start_with?("/rails/")
|
48
|
+
@rails_app.call(env)
|
49
|
+
else
|
50
|
+
result
|
51
|
+
end
|
46
52
|
end
|
47
53
|
end
|
48
54
|
end
|
data/lib/rage/setup.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
Iodine.patch_rack
|
2
2
|
|
3
|
-
|
3
|
+
begin
|
4
|
+
require_relative "#{Rage.root}/config/environments/#{Rage.env}"
|
5
|
+
rescue LoadError
|
6
|
+
raise LoadError, "The <#{Rage.env}> environment could not be found. Please check the environment name."
|
7
|
+
end
|
4
8
|
|
5
9
|
# Run application initializers
|
6
10
|
Dir["#{Rage.root}/config/initializers/**/*.rb"].each { |initializer| load(initializer) }
|
data/lib/rage/sidekiq_session.rb
CHANGED
data/lib/rage/version.rb
CHANGED
data/lib/rage-rb.rb
CHANGED
data/rage.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
21
|
spec.files = Dir.chdir(__dir__) do
|
22
22
|
`git ls-files -z`.split("\x0").reject do |f|
|
23
|
-
(File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
|
23
|
+
(File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor .rubocop])
|
24
24
|
end
|
25
25
|
end
|
26
26
|
spec.bindir = "exe"
|
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: 1.
|
4
|
+
version: 1.7.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: 2024-
|
11
|
+
date: 2024-07-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -94,6 +94,7 @@ files:
|
|
94
94
|
- CODE_OF_CONDUCT.md
|
95
95
|
- Gemfile
|
96
96
|
- LICENSE.txt
|
97
|
+
- OVERVIEW.md
|
97
98
|
- README.md
|
98
99
|
- Rakefile
|
99
100
|
- exe/rage
|
@@ -126,6 +127,10 @@ files:
|
|
126
127
|
- lib/rage/router/backend.rb
|
127
128
|
- lib/rage/router/constrainer.rb
|
128
129
|
- lib/rage/router/dsl.rb
|
130
|
+
- lib/rage/router/dsl_plugins/controller_action_options.rb
|
131
|
+
- lib/rage/router/dsl_plugins/legacy_hash_notation.rb
|
132
|
+
- lib/rage/router/dsl_plugins/legacy_root_notation.rb
|
133
|
+
- lib/rage/router/dsl_plugins/named_route_helpers.rb
|
129
134
|
- lib/rage/router/handler_storage.rb
|
130
135
|
- lib/rage/router/node.rb
|
131
136
|
- lib/rage/router/strategies/host.rb
|