rage-rb 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +5 -1
- data/lib/rage/all.rb +1 -0
- data/lib/rage/configuration.rb +2 -2
- data/lib/rage/fiber.rb +52 -1
- data/lib/rage/logger/json_formatter.rb +2 -2
- data/lib/rage/logger/logger.rb +17 -7
- data/lib/rage/logger/text_formatter.rb +2 -2
- data/lib/rage/rails.rb +0 -6
- data/lib/rage/router/backend.rb +1 -19
- data/lib/rage/router/handler_storage.rb +5 -4
- data/lib/rage/router/util.rb +33 -0
- data/lib/rage/rspec.rb +2 -2
- data/lib/rage/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fd10469efc73f6134f72f79fe21871238b8860e958c085499c47b312adf91764
|
4
|
+
data.tar.gz: 34990a4885df4a4acd55fcbabf8d3a67e57e0609db406ee55d5f71cab0655ffb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc28afd194f122bf436c215cb6d88dfe3d923874f4e07c3c1db5dd3f33a4676354731fad3f6ffc29326423086b59dbb0d1860ecb5cb71cf4c7f0412a252af16f
|
7
|
+
data.tar.gz: 58cd0cea6daba63d67f9285d3b616980d2e69c778620c54b14c7c5cb1eccf093bfbc0644dec081c325d4cdc5f6c032b7f399ccb8ea5b052c1da401dcde08ed6e
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.1.0] - 2024-03-25
|
4
|
+
|
5
|
+
### Changed
|
6
|
+
|
7
|
+
- Change the way controller names are logged (#72).
|
8
|
+
- Use formatters in console (#71).
|
9
|
+
|
10
|
+
### Fixed
|
11
|
+
|
12
|
+
- Fix Fiber.await behavior in RSpec (#70).
|
13
|
+
|
3
14
|
## [1.0.0] - 2024-03-13
|
4
15
|
|
5
16
|
### Added
|
data/README.md
CHANGED
@@ -53,8 +53,12 @@ Check out in-depth API docs for more information:
|
|
53
53
|
- [Fiber API](https://rage-rb.pages.dev/Fiber)
|
54
54
|
- [Logger API](https://rage-rb.pages.dev/Rage/Logger)
|
55
55
|
- [Configuration API](https://rage-rb.pages.dev/Rage/Configuration)
|
56
|
+
- [CORS middleware](https://rage-rb.pages.dev/Rage/Cors)
|
56
57
|
|
57
|
-
Also, see the
|
58
|
+
Also, see the following integration guides:
|
59
|
+
|
60
|
+
- [Rails integration](https://github.com/rage-rb/rage/wiki/Rails-integration)
|
61
|
+
- [RSpec integration](https://github.com/rage-rb/rage/wiki/RSpec-integration)
|
58
62
|
|
59
63
|
### Example
|
60
64
|
|
data/lib/rage/all.rb
CHANGED
data/lib/rage/configuration.rb
CHANGED
@@ -19,7 +19,7 @@
|
|
19
19
|
#
|
20
20
|
# • _config.middleware.use_
|
21
21
|
#
|
22
|
-
# > Adds a middleware to the top of the middleware stack. **This is the
|
22
|
+
# > Adds a middleware to the top of the middleware stack. **This is the recommended way of adding a middleware.**
|
23
23
|
# > ```
|
24
24
|
# config.middleware.use Rack::Cors do
|
25
25
|
# allow do
|
@@ -166,7 +166,7 @@ class Rage::Configuration
|
|
166
166
|
|
167
167
|
# @private
|
168
168
|
class Internal
|
169
|
-
attr_accessor :rails_mode
|
169
|
+
attr_accessor :rails_mode
|
170
170
|
|
171
171
|
def inspect
|
172
172
|
"#<#{self.class.name}>"
|
data/lib/rage/fiber.rb
CHANGED
@@ -1,5 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
##
|
4
|
+
# Rage provides a simple and efficient API to wait on several instances of IO at the same time - {Fiber.await}.
|
5
|
+
#
|
6
|
+
# Let's say we have the following controller:
|
7
|
+
# ```ruby
|
8
|
+
# class UsersController < RageController::API
|
9
|
+
# def show
|
10
|
+
# user = Net::HTTP.get(URI("http://users.service/users/#{params[:id]}"))
|
11
|
+
# bookings = Net::HTTP.get(URI("http://bookings.service/bookings?user_id=#{params[:id]}"))
|
12
|
+
# render json: { user: user, bookings: bookings }
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
# ```
|
16
|
+
# This code will fire two consecutive HTTP requests. If each request takes 1 second to execute, the total execution time will be 2 seconds.<br>
|
17
|
+
# With {Fiber.await}, we can significantly decrease the overall execution time by changing the code to fire the requests concurrently.
|
18
|
+
#
|
19
|
+
# To do this, we will need to:
|
20
|
+
#
|
21
|
+
# 1. Wrap every request in a separate fiber using {Fiber.schedule};
|
22
|
+
# 2. Pass newly created fibers into {Fiber.await};
|
23
|
+
#
|
24
|
+
# ```ruby
|
25
|
+
# class UsersController < RageController::API
|
26
|
+
# def show
|
27
|
+
# user, bookings = Fiber.await([
|
28
|
+
# Fiber.schedule { Net::HTTP.get(URI("http://users.service/users/#{params[:id]}")) },
|
29
|
+
# Fiber.schedule { Net::HTTP.get(URI("http://bookings.service/bookings?user_id=#{params[:id]}")) }
|
30
|
+
# ])
|
31
|
+
#
|
32
|
+
# render json: { user: user, bookings: bookings }
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
# ```
|
36
|
+
# With this change, if each request takes 1 second to execute, the total execution time will still be 1 second.
|
37
|
+
#
|
38
|
+
# ## Creating fibers
|
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
|
+
# 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>
|
41
|
+
# Same applies to fibers. Feel free to create as many fibers as you need on demand.
|
3
42
|
class Fiber
|
4
43
|
# @private
|
5
44
|
AWAIT_ERROR_MESSAGE = "err"
|
@@ -58,7 +97,7 @@ class Fiber
|
|
58
97
|
end
|
59
98
|
|
60
99
|
# Wait on several fibers at the same time. Calling this method will automatically pause the current fiber, allowing the
|
61
|
-
#
|
100
|
+
# server to process other requests. Once all fibers have completed, the current fiber will be automatically resumed.
|
62
101
|
#
|
63
102
|
# @param fibers [Fiber, Array<Fiber>] one or several fibers to wait on. The fibers must be created using the `Fiber.schedule` call.
|
64
103
|
# @example
|
@@ -109,4 +148,16 @@ class Fiber
|
|
109
148
|
fibers.map!(&:__get_result)
|
110
149
|
end
|
111
150
|
end
|
151
|
+
|
152
|
+
# @!method self.schedule(&block)
|
153
|
+
# Create a non-blocking fiber. Should mostly be used in conjunction with `Fiber.await`.
|
154
|
+
# @example
|
155
|
+
# Fiber.await([
|
156
|
+
# Fiber.schedule { request_1 },
|
157
|
+
# Fiber.schedule { request_2 }
|
158
|
+
# ])
|
159
|
+
# @example
|
160
|
+
# fiber_1 = Fiber.schedule { request_1 }
|
161
|
+
# fiber_2 = Fiber.schedule { request_2 }
|
162
|
+
# Fiber.await([fiber_1, fiber_2])
|
112
163
|
end
|
@@ -17,8 +17,8 @@ class Rage::JSONFormatter
|
|
17
17
|
|
18
18
|
if final = logger[:final]
|
19
19
|
params, env = final[:params], final[:env]
|
20
|
-
if params
|
21
|
-
return "{\"tags\":[\"#{tags[0]}\"],\"timestamp\":\"#{timestamp}\",\"pid\":\"#{@pid}\",\"level\":\"info\",\"method\":\"#{env["REQUEST_METHOD"]}\",\"path\":\"#{env["PATH_INFO"]}\",\"controller\":\"#{params[:controller]}\",\"action\":\"#{params[:action]}\",#{context_msg}\"status\":#{final[:response][0]},\"duration\":#{final[:duration]}}\n"
|
20
|
+
if params && params[:controller]
|
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"
|
22
22
|
else
|
23
23
|
# no controller/action keys are written if there are no params
|
24
24
|
return "{\"tags\":[\"#{tags[0]}\"],\"timestamp\":\"#{timestamp}\",\"pid\":\"#{@pid}\",\"level\":\"info\",\"method\":\"#{env["REQUEST_METHOD"]}\",\"path\":\"#{env["PATH_INFO"]}\",#{context_msg}\"status\":#{final[:response][0]},\"duration\":#{final[:duration]}}\n"
|
data/lib/rage/logger/logger.rb
CHANGED
@@ -33,6 +33,23 @@ require "logger"
|
|
33
33
|
# Rage.logger.info("Initializing")
|
34
34
|
# Rage.logger.debug { "This is a " + potentially + " expensive operation" }
|
35
35
|
# ```
|
36
|
+
#
|
37
|
+
# ## Using the logger
|
38
|
+
# The recommended approach to logging with Rage is to make sure your code always logs the same message no matter what the input is.
|
39
|
+
# You can achieve this by using the {with_context} and {tagged} methods. So, a code like this:
|
40
|
+
# ```ruby
|
41
|
+
# def process_purchase(user_id:, product_id:)
|
42
|
+
# Rage.logger.info "processing purchase with user_id = #{user_id}; product_id = #{product_id}"
|
43
|
+
# end
|
44
|
+
# ```
|
45
|
+
# turns into this:
|
46
|
+
# ```ruby
|
47
|
+
# def process_purchase(user_id:, product_id:)
|
48
|
+
# Rage.logger.with_context(user_id: user_id, product_id: product_id) do
|
49
|
+
# Rage.logger.info "processing purchase"
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
# ```
|
36
53
|
class Rage::Logger
|
37
54
|
METHODS_MAP = {
|
38
55
|
"debug" => Logger::DEBUG,
|
@@ -129,13 +146,6 @@ class Rage::Logger
|
|
129
146
|
false
|
130
147
|
end
|
131
148
|
RUBY
|
132
|
-
elsif (Rage.config.internal.rails_mode ? Rage.config.internal.rails_console : defined?(IRB))
|
133
|
-
# the call was made from the console - don't use the formatter
|
134
|
-
<<-RUBY
|
135
|
-
def #{level_name}(msg = nil)
|
136
|
-
@logdev.write((msg || yield) + "\n")
|
137
|
-
end
|
138
|
-
RUBY
|
139
149
|
elsif @formatter.class.name.start_with?("Rage::")
|
140
150
|
# the call was made from within the application and a built-in formatter is used;
|
141
151
|
# in such case we use the `gen_timestamp` method which is much faster than `Time.now.strftime`;
|
@@ -17,8 +17,8 @@ class Rage::TextFormatter
|
|
17
17
|
|
18
18
|
if final = logger[:final]
|
19
19
|
params, env = final[:params], final[:env]
|
20
|
-
if params
|
21
|
-
return "[#{tags[0]}] timestamp=#{timestamp} pid=#{@pid} level=info method=#{env["REQUEST_METHOD"]} path=#{env["PATH_INFO"]} controller=#{params[:controller]} action=#{params[:action]} #{context_msg}status=#{final[:response][0]} duration=#{final[:duration]}\n"
|
20
|
+
if params && params[:controller]
|
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"
|
22
22
|
else
|
23
23
|
# no controller/action keys are written if there are no params
|
24
24
|
return "[#{tags[0]}] timestamp=#{timestamp} pid=#{@pid} level=info method=#{env["REQUEST_METHOD"]} path=#{env["PATH_INFO"]} #{context_msg}status=#{final[:response][0]} duration=#{final[:duration]}\n"
|
data/lib/rage/rails.rb
CHANGED
@@ -11,12 +11,6 @@ Iodine.patch_rack
|
|
11
11
|
# configure the framework
|
12
12
|
Rage.config.internal.rails_mode = true
|
13
13
|
|
14
|
-
# make sure log formatter is not used in console
|
15
|
-
Rails.application.console do
|
16
|
-
Rage.config.internal.rails_console = true
|
17
|
-
Rage.logger.level = Rage.logger.level if Rage.logger # trigger redefining log methods
|
18
|
-
end
|
19
|
-
|
20
14
|
# patch ActiveRecord's connection pool
|
21
15
|
if defined?(ActiveRecord)
|
22
16
|
Rails.configuration.after_initialize do
|
data/lib/rage/router/backend.rb
CHANGED
@@ -67,7 +67,7 @@ class Rage::Router::Backend
|
|
67
67
|
if handler.is_a?(String)
|
68
68
|
raise "Invalid route handler format, expected to match the 'controller#action' pattern" unless handler =~ STRING_HANDLER_REGEXP
|
69
69
|
|
70
|
-
controller, action =
|
70
|
+
controller, action = Rage::Router::Util.path_to_class($1), $2
|
71
71
|
run_action_method_name = controller.__register_action(action.to_sym)
|
72
72
|
|
73
73
|
meta[:controller] = $1
|
@@ -273,22 +273,4 @@ class Rage::Router::Backend
|
|
273
273
|
end
|
274
274
|
end
|
275
275
|
end
|
276
|
-
|
277
|
-
def to_controller_class(str)
|
278
|
-
str.capitalize!
|
279
|
-
str.gsub!(/([\/_])([a-zA-Z0-9]+)/) do
|
280
|
-
if $1 == "/"
|
281
|
-
"::#{$2.capitalize}"
|
282
|
-
else
|
283
|
-
$2.capitalize
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
|
-
klass = "#{str}Controller"
|
288
|
-
if Object.const_defined?(klass)
|
289
|
-
Object.const_get(klass)
|
290
|
-
else
|
291
|
-
raise Rage::Errors::RouterError, "Routing error: could not find the #{klass} class"
|
292
|
-
end
|
293
|
-
end
|
294
276
|
end
|
@@ -48,10 +48,11 @@ class Rage::Router::HandlerStorage
|
|
48
48
|
private
|
49
49
|
|
50
50
|
def compile_create_params_object(param_keys, defaults, meta)
|
51
|
-
lines = [
|
52
|
-
":controller => '#{meta[:controller]}'.freeze",
|
53
|
-
|
54
|
-
|
51
|
+
lines = if meta[:controller]
|
52
|
+
[":controller => '#{meta[:controller]}'.freeze", ":action => '#{meta[:action]}'.freeze"]
|
53
|
+
else
|
54
|
+
[]
|
55
|
+
end
|
55
56
|
|
56
57
|
param_keys.each_with_index do |key, i|
|
57
58
|
lines << ":#{key} => param_values[#{i}]"
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Rage::Router::Util
|
2
|
+
class << self
|
3
|
+
# converts controller name in a path form into a class
|
4
|
+
# `api/v1/users` => `Api::V1::UsersController`
|
5
|
+
def path_to_class(str)
|
6
|
+
str = str.capitalize
|
7
|
+
str.gsub!(/([\/_])([a-zA-Z0-9]+)/) do
|
8
|
+
if $1 == "/"
|
9
|
+
"::#{$2.capitalize}"
|
10
|
+
else
|
11
|
+
$2.capitalize
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
klass = "#{str}Controller"
|
16
|
+
if Object.const_defined?(klass)
|
17
|
+
Object.const_get(klass)
|
18
|
+
else
|
19
|
+
raise Rage::Errors::RouterError, "Routing error: could not find the #{klass} class"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
@@names_map = {}
|
24
|
+
|
25
|
+
# converts controller name in a path form into a string representation of a class
|
26
|
+
# `api/v1/users` => `"Api::V1::UsersController"`
|
27
|
+
def path_to_name(str)
|
28
|
+
@@names_map[str] || begin
|
29
|
+
@@names_map[str] = path_to_class(str).name
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/rage/rspec.rb
CHANGED
data/lib/rage/version.rb
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: 1.
|
4
|
+
version: 1.1.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-03-
|
11
|
+
date: 2024-03-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -126,6 +126,7 @@ files:
|
|
126
126
|
- lib/rage/router/handler_storage.rb
|
127
127
|
- lib/rage/router/node.rb
|
128
128
|
- lib/rage/router/strategies/host.rb
|
129
|
+
- lib/rage/router/util.rb
|
129
130
|
- lib/rage/rspec.rb
|
130
131
|
- lib/rage/setup.rb
|
131
132
|
- lib/rage/sidekiq_session.rb
|