rage-rb 0.4.0 → 0.5.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 +29 -10
- data/Gemfile +2 -0
- data/README.md +10 -8
- data/lib/rage/all.rb +7 -0
- data/lib/rage/application.rb +33 -5
- data/lib/rage/cli.rb +16 -9
- data/lib/rage/configuration.rb +22 -6
- data/lib/rage/controller/api.rb +1 -1
- data/lib/rage/fiber.rb +58 -10
- data/lib/rage/fiber_scheduler.rb +59 -32
- data/lib/rage/logger/logger.rb +164 -0
- data/lib/rage/logger/text_formatter.rb +46 -0
- data/lib/rage/router/backend.rb +37 -4
- data/lib/rage/router/dsl.rb +14 -3
- data/lib/rage/router/handler_storage.rb +7 -4
- data/lib/rage/sidekiq_session.rb +72 -0
- data/lib/rage/templates/Rakefile +1 -0
- data/lib/rage/templates/config-application.rb +5 -0
- data/lib/rage/templates/config-environments-development.rb +6 -3
- data/lib/rage/templates/config-environments-production.rb +7 -3
- data/lib/rage/templates/config-environments-test.rb +6 -3
- data/lib/rage/version.rb +1 -1
- data/lib/rage-rb.rb +7 -2
- data/rage.gemspec +1 -1
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22f98c39badb0c2128b270ce3092d183f62db4bead34e8df627cf322a3697c71
|
4
|
+
data.tar.gz: ea49aeae3da7630f52e8c704bc042752a66b0fefc03a67d713ead33eec1fd006
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e9e57e1eafda6e41112b8af0cc8d23eac629e0964b441622c48173f936f45aac95d0482b3545c5feb047dee755889c30ac63d0f63332e59d36cdff5cb1800198
|
7
|
+
data.tar.gz: 481b9e52062350a9923c3b0195aea555ca0b8cb0c95fa95e76d3e7cd45a27e21a817ad259aed6e1cf9d90c74c17e99b8398066e84deda7e422247a1ea59254b9
|
data/CHANGELOG.md
CHANGED
@@ -1,25 +1,44 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.5.0] - 2023-11-25
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- Add sessions for compatibility with `Sidekiq::Web` (#35).
|
8
|
+
- Add logger (#33).
|
9
|
+
|
10
|
+
### Fixed
|
11
|
+
|
12
|
+
- Fixes for `FiberScheduler#io_wait` and `FiberScheduler#io_read` (#32).
|
13
|
+
- Correctly handle exceptions in inner fibers (#34).
|
14
|
+
- Fixes for `FiberScheduler#kernel_sleep` (#36).
|
15
|
+
|
16
|
+
### Changed
|
17
|
+
|
18
|
+
- Use config namespaces (#25).
|
19
|
+
- Update `Fiber.await` signature (#36).
|
20
|
+
|
3
21
|
## [0.4.0] - 2023-10-31
|
4
22
|
|
5
23
|
### Added
|
6
24
|
|
7
|
-
- Expose the `params` object.
|
8
|
-
- Support header authentication with `authenticate_with_http_token
|
9
|
-
- Add the `resources`
|
10
|
-
- Add the `
|
11
|
-
-
|
25
|
+
- Expose the `params` object (#23).
|
26
|
+
- Support header authentication with `authenticate_with_http_token` (#21).
|
27
|
+
- Add the `resources` route helper (#20).
|
28
|
+
- Add the `namespace` route helper by [@arikarim](https://github.com/arikarim) (#17).
|
29
|
+
- Add the `mount` and `match` route helpers by [@arikarim](https://github.com/arikarim) (#18) (#14).
|
30
|
+
- Allow to access request headers by [@arikarim](https://github.com/arikarim) (#15).
|
12
31
|
- Support custom ports when starting the app with `rage s`.
|
13
32
|
|
14
33
|
## [0.3.0] - 2023-10-08
|
15
34
|
|
16
35
|
### Added
|
17
36
|
|
18
|
-
- CLI `routes` task.
|
19
|
-
- CLI `console` task.
|
20
|
-
- `:if` and `:unless` options in `before_action
|
21
|
-
- Allow to set response headers.
|
22
|
-
- Block version of `before_action
|
37
|
+
- CLI `routes` task by [@arikarim](https://github.com/arikarim) (#9).
|
38
|
+
- CLI `console` task (#12).
|
39
|
+
- `:if` and `:unless` options in `before_action` (#10).
|
40
|
+
- Allow to set response headers (#11).
|
41
|
+
- Block version of `before_action` by [@heysyam99](https://github.com/heysyam99) (#8).
|
23
42
|
|
24
43
|
## [0.2.0] - 2023-09-27
|
25
44
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -49,6 +49,7 @@ Check out in-depth API docs for more information:
|
|
49
49
|
- [Controller API](https://rage-rb.pages.dev/RageController/API)
|
50
50
|
- [Routing API](https://rage-rb.pages.dev/Rage/Router/DSL/Handler)
|
51
51
|
- [Fiber API](https://rage-rb.pages.dev/Fiber)
|
52
|
+
- [Logger API](https://rage-rb.pages.dev/Rage/Logger)
|
52
53
|
|
53
54
|
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.
|
54
55
|
|
@@ -98,10 +99,10 @@ require "net/http"
|
|
98
99
|
|
99
100
|
class PagesController < RageController::API
|
100
101
|
def index
|
101
|
-
pages = Fiber.await(
|
102
|
+
pages = Fiber.await([
|
102
103
|
Fiber.schedule { Net::HTTP.get(URI("https://httpbin.org/json")) },
|
103
104
|
Fiber.schedule { Net::HTTP.get(URI("https://httpbin.org/html")) },
|
104
|
-
)
|
105
|
+
])
|
105
106
|
|
106
107
|
render json: { pages: pages }
|
107
108
|
end
|
@@ -143,12 +144,13 @@ Version | Changes
|
|
143
144
|
------- |------------
|
144
145
|
0.2 :white_check_mark: | ~~Gem configuration by env.<br>Add `skip_before_action`.<br>Add `rescue_from`.<br>Router updates:<br> • make the `root` helper work correctly with `scope`;<br> • support the `defaults` option;~~
|
145
146
|
0.3 :white_check_mark: | ~~CLI updates:<br> • `routes` task;<br> • `console` task;<br>Support the `:if` and `:unless` options in `before_action`.<br>Allow to set response headers.~~
|
146
|
-
0.4 :white_check_mark: | ~~Expose the `params` object.<br>Support header authentication with `authenticate_with_http_token`.<br>Router updates:<br> • add the `resources` route helper;<br> • add the `namespace` route helper
|
147
|
-
0.5 |
|
148
|
-
0.6 |
|
149
|
-
0.7 |
|
150
|
-
0.8 |
|
151
|
-
0.9 |
|
147
|
+
0.4 :white_check_mark: | ~~Expose the `params` object.<br>Support header authentication with `authenticate_with_http_token`.<br>Router updates:<br> • add the `resources` route helper;<br> • add the `namespace` route helper;~~
|
148
|
+
0.5 :white_check_mark: | ~~Add request logging.~~
|
149
|
+
0.6 | Automatic code reloading in development with Zeitwerk.
|
150
|
+
0.7 | Expose the `send_data` and `send_file` methods.
|
151
|
+
0.8 | Support conditional get with `etag` and `last_modified`.
|
152
|
+
0.9 | Expose the `cookies` and `session` objects.
|
153
|
+
1.0 | Implement Iodine-based equivalent of Action Cable.
|
152
154
|
|
153
155
|
## Development
|
154
156
|
|
data/lib/rage/all.rb
CHANGED
@@ -18,3 +18,10 @@ require_relative "router/handler_storage"
|
|
18
18
|
require_relative "router/node"
|
19
19
|
|
20
20
|
require_relative "controller/api"
|
21
|
+
|
22
|
+
require_relative "logger/text_formatter"
|
23
|
+
require_relative "logger/logger"
|
24
|
+
|
25
|
+
if defined?(Sidekiq)
|
26
|
+
require_relative "sidekiq_session"
|
27
|
+
end
|
data/lib/rage/application.rb
CHANGED
@@ -10,21 +10,25 @@ class Rage::Application
|
|
10
10
|
|
11
11
|
def call(env)
|
12
12
|
fiber = Fiber.schedule do
|
13
|
+
init_logger
|
14
|
+
|
13
15
|
handler = @router.lookup(env)
|
14
16
|
|
15
|
-
if handler
|
17
|
+
response = if handler
|
16
18
|
params = Rage::ParamsParser.prepare(env, handler[:params])
|
17
19
|
handler[:handler].call(env, params)
|
18
20
|
else
|
19
21
|
[404, {}, ["Not Found"]]
|
20
22
|
end
|
21
23
|
|
22
|
-
rescue => e
|
23
|
-
|
24
|
+
rescue Exception => e
|
25
|
+
exception_str = "#{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}"
|
26
|
+
Rage.logger.error(exception_str)
|
27
|
+
response = [500, {}, [exception_str]]
|
24
28
|
|
25
29
|
ensure
|
26
|
-
|
27
|
-
Iodine.publish(env["IODINE_REQUEST_ID"], "")
|
30
|
+
finalize_logger(env, response, params)
|
31
|
+
Iodine.publish(env["IODINE_REQUEST_ID"], "") # notify Iodine the request can now be served
|
28
32
|
end
|
29
33
|
|
30
34
|
# the fiber encountered blocking IO and yielded; instruct Iodine to pause the request;
|
@@ -34,4 +38,28 @@ class Rage::Application
|
|
34
38
|
fiber.__get_result
|
35
39
|
end
|
36
40
|
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
DEFAULT_LOG_CONTEXT = {}.freeze
|
45
|
+
private_constant :DEFAULT_LOG_CONTEXT
|
46
|
+
|
47
|
+
def init_logger
|
48
|
+
Thread.current[:rage_logger] = {
|
49
|
+
tags: [Iodine::Rack::Utils.gen_request_tag],
|
50
|
+
context: DEFAULT_LOG_CONTEXT,
|
51
|
+
request_start: Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def finalize_logger(env, response, params)
|
56
|
+
logger = Thread.current[:rage_logger]
|
57
|
+
|
58
|
+
duration = (
|
59
|
+
(Process.clock_gettime(Process::CLOCK_MONOTONIC) - logger[:request_start]) * 1000
|
60
|
+
).round(2)
|
61
|
+
|
62
|
+
logger[:final] = { env:, params:, response:, duration: }
|
63
|
+
Rage.logger.info("")
|
64
|
+
end
|
37
65
|
end
|
data/lib/rage/cli.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "thor"
|
3
|
-
require "
|
4
|
-
require "irb"
|
3
|
+
require "rack"
|
5
4
|
|
6
5
|
module Rage
|
7
6
|
class CLI < Thor
|
@@ -11,6 +10,7 @@ module Rage
|
|
11
10
|
|
12
11
|
desc "new PATH", "Create a new application."
|
13
12
|
def new(path)
|
13
|
+
require "rage/all"
|
14
14
|
NewAppGenerator.start([path])
|
15
15
|
end
|
16
16
|
|
@@ -20,9 +20,9 @@ module Rage
|
|
20
20
|
app = ::Rack::Builder.parse_file("config.ru")
|
21
21
|
app = app[0] if app.is_a?(Array)
|
22
22
|
|
23
|
-
::Iodine.listen service: :http, handler: app, port: options[:port] || Rage.config.port
|
24
|
-
::Iodine.threads = Rage.config.threads_count
|
25
|
-
::Iodine.workers = Rage.config.workers_count
|
23
|
+
::Iodine.listen service: :http, handler: app, port: options[:port] || Rage.config.server.port
|
24
|
+
::Iodine.threads = Rage.config.server.threads_count
|
25
|
+
::Iodine.workers = Rage.config.server.workers_count
|
26
26
|
|
27
27
|
::Iodine.start
|
28
28
|
end
|
@@ -39,14 +39,20 @@ module Rage
|
|
39
39
|
|
40
40
|
routes = Rage.__router.routes
|
41
41
|
pattern = options[:grep]
|
42
|
-
routes.unshift({ method: "Verb", path: "Path", raw_handler: "Controller#Action" })
|
42
|
+
routes.unshift({ method: "Verb", path: "Path", meta: { raw_handler: "Controller#Action" } })
|
43
43
|
|
44
44
|
grouped_routes = routes.each_with_object({}) do |route, memo|
|
45
45
|
if pattern && !memo.empty?
|
46
|
-
next unless route[:path].match?(pattern) || route[:raw_handler].to_s.match?(pattern) || route[:method].match?(pattern)
|
46
|
+
next unless route[:path].match?(pattern) || route[:meta][:raw_handler].to_s.match?(pattern) || route[:method].match?(pattern)
|
47
|
+
end
|
48
|
+
|
49
|
+
key = [route[:path], route[:meta][:raw_handler]]
|
50
|
+
|
51
|
+
if route[:meta][:mount]
|
52
|
+
memo[key] = route.merge(method: "") unless route[:path].end_with?("*")
|
53
|
+
next
|
47
54
|
end
|
48
55
|
|
49
|
-
key = [route[:path], route[:raw_handler]]
|
50
56
|
if memo[key]
|
51
57
|
memo[key][:method] += "|#{route[:method]}"
|
52
58
|
else
|
@@ -68,7 +74,7 @@ module Rage
|
|
68
74
|
meta = route[:constraints]
|
69
75
|
meta.merge!(route[:defaults]) if route[:defaults]
|
70
76
|
|
71
|
-
handler = route[:raw_handler]
|
77
|
+
handler = route[:meta][:raw_handler]
|
72
78
|
handler = "#{handler} #{meta}" unless meta&.empty?
|
73
79
|
|
74
80
|
puts format("%-#{longest_method}s%-#{longest_path}s%s", route[:method], route[:path], handler)
|
@@ -78,6 +84,7 @@ module Rage
|
|
78
84
|
|
79
85
|
desc "c", "Start the app console."
|
80
86
|
def console
|
87
|
+
require "irb"
|
81
88
|
environment
|
82
89
|
ARGV.clear
|
83
90
|
IRB.start
|
data/lib/rage/configuration.rb
CHANGED
@@ -1,10 +1,26 @@
|
|
1
1
|
class Rage::Configuration
|
2
|
-
attr_accessor :
|
3
|
-
attr_reader :threads_count
|
2
|
+
attr_accessor :logger, :log_formatter, :log_level
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
# used in DSL
|
5
|
+
def config = self
|
6
|
+
|
7
|
+
def server
|
8
|
+
@server ||= Server.new
|
9
|
+
end
|
10
|
+
|
11
|
+
class Server
|
12
|
+
attr_accessor :port, :workers_count
|
13
|
+
attr_reader :threads_count
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@threads_count = 1
|
17
|
+
@workers_count = -1
|
18
|
+
@port = 3000
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def __finalize
|
23
|
+
@logger.formatter = @log_formatter if @logger && @log_formatter
|
24
|
+
@logger.level = @log_level if @logger && @log_level
|
9
25
|
end
|
10
26
|
end
|
data/lib/rage/controller/api.rb
CHANGED
data/lib/rage/fiber.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Fiber
|
4
|
+
AWAIT_ERROR_MESSAGE = "err"
|
5
|
+
|
2
6
|
# @private
|
3
7
|
def __set_result(result)
|
4
8
|
@__result = result
|
@@ -9,30 +13,74 @@ class Fiber
|
|
9
13
|
@__result
|
10
14
|
end
|
11
15
|
|
16
|
+
# @private
|
17
|
+
def __set_err(err)
|
18
|
+
@__err = err
|
19
|
+
end
|
20
|
+
|
21
|
+
# @private
|
22
|
+
def __get_err
|
23
|
+
@__err
|
24
|
+
end
|
25
|
+
|
26
|
+
# @private
|
27
|
+
# pause a fiber and resume in the next iteration of the event loop
|
28
|
+
def self.pause
|
29
|
+
f = Fiber.current
|
30
|
+
Iodine.defer { f.resume }
|
31
|
+
Fiber.yield
|
32
|
+
end
|
33
|
+
|
12
34
|
# Wait on several fibers at the same time. Calling this method will automatically pause the current fiber, allowing the
|
13
35
|
# server to process other requests. Once all fibers have completed, the current fiber will be automatically resumed.
|
14
36
|
#
|
15
37
|
# @param fibers [Fiber, Array<Fiber>] one or several fibers to wait on. The fibers must be created using the `Fiber.schedule` call.
|
16
38
|
# @example
|
17
|
-
# Fiber.await(
|
39
|
+
# Fiber.await([
|
18
40
|
# Fiber.schedule { request_1 },
|
19
41
|
# Fiber.schedule { request_2 },
|
20
|
-
# )
|
42
|
+
# ])
|
21
43
|
# @note This method should only be used when multiple fibers have to be processed in parallel. There's no need to use `Fiber.await` for single IO calls.
|
22
|
-
def self.await(
|
23
|
-
f = Fiber.current
|
44
|
+
def self.await(fibers)
|
45
|
+
f, fibers = Fiber.current, Array(fibers)
|
24
46
|
|
25
|
-
|
26
|
-
|
47
|
+
# check which fibers are alive (i.e. have yielded) and which have errored out
|
48
|
+
i, err, num_wait_for = 0, nil, 0
|
49
|
+
while i < fibers.length
|
50
|
+
if fibers[i].alive?
|
51
|
+
num_wait_for += 1
|
52
|
+
else
|
53
|
+
err = fibers[i].__get_err
|
54
|
+
break if err
|
55
|
+
end
|
56
|
+
i += 1
|
57
|
+
end
|
58
|
+
|
59
|
+
# raise if one of the fibers has errored out or return the result if none have yielded
|
60
|
+
if err
|
61
|
+
raise err
|
62
|
+
elsif num_wait_for == 0
|
63
|
+
return fibers.map!(&:__get_result)
|
64
|
+
end
|
27
65
|
|
28
|
-
|
29
|
-
|
30
|
-
|
66
|
+
# wait on async fibers; resume right away if one of the fibers errors out
|
67
|
+
Iodine.subscribe("await:#{f.object_id}") do |_, err|
|
68
|
+
if err == AWAIT_ERROR_MESSAGE
|
69
|
+
f.resume
|
70
|
+
else
|
71
|
+
num_wait_for -= 1
|
72
|
+
f.resume if num_wait_for == 0
|
73
|
+
end
|
31
74
|
end
|
32
75
|
|
33
76
|
Fiber.yield
|
34
77
|
Iodine.defer { Iodine.unsubscribe("await:#{f.object_id}") }
|
35
78
|
|
36
|
-
|
79
|
+
# if num_wait_for is not 0 means we exited prematurely because of an error
|
80
|
+
if num_wait_for > 0
|
81
|
+
raise fibers.find(&:__get_err).__get_err
|
82
|
+
else
|
83
|
+
fibers.map!(&:__get_result)
|
84
|
+
end
|
37
85
|
end
|
38
86
|
end
|
data/lib/rage/fiber_scheduler.rb
CHANGED
@@ -3,41 +3,50 @@
|
|
3
3
|
require "resolv"
|
4
4
|
|
5
5
|
class Rage::FiberScheduler
|
6
|
+
MAX_READ = 65536
|
7
|
+
|
6
8
|
def initialize
|
7
9
|
@root_fiber = Fiber.current
|
8
10
|
end
|
9
11
|
|
10
12
|
def io_wait(io, events, timeout = nil)
|
11
13
|
f = Fiber.current
|
12
|
-
::Iodine::Scheduler.attach(io.fileno, events, timeout&.ceil || 0) { f.resume }
|
13
|
-
Fiber.yield
|
14
|
+
::Iodine::Scheduler.attach(io.fileno, events, timeout&.ceil || 0) { |err| f.resume(err) }
|
14
15
|
|
15
|
-
|
16
|
+
err = Fiber.yield
|
17
|
+
if err == Errno::ETIMEDOUT::Errno
|
18
|
+
0
|
19
|
+
else
|
20
|
+
events
|
21
|
+
end
|
16
22
|
end
|
17
23
|
|
18
|
-
# TODO: this is more synchronous than asynchronous right now
|
19
24
|
def io_read(io, buffer, length, offset = 0)
|
20
|
-
|
21
|
-
|
25
|
+
length_to_read = if length == 0
|
26
|
+
buffer.size > MAX_READ ? MAX_READ : buffer.size
|
27
|
+
else
|
28
|
+
length
|
29
|
+
end
|
30
|
+
|
31
|
+
while true
|
32
|
+
string = ::Iodine::Scheduler.read(io.fileno, length_to_read, offset)
|
22
33
|
|
23
34
|
if string.nil?
|
24
35
|
return offset
|
25
36
|
end
|
26
37
|
|
27
38
|
if string.empty?
|
28
|
-
|
29
|
-
next
|
39
|
+
return -Errno::EAGAIN::Errno
|
30
40
|
end
|
31
41
|
|
32
42
|
buffer.set_string(string, offset)
|
33
|
-
offset += string.bytesize
|
34
43
|
|
35
44
|
size = string.bytesize
|
36
|
-
|
37
|
-
|
38
|
-
end
|
45
|
+
offset += size
|
46
|
+
return offset if size < length_to_read || size >= buffer.size
|
39
47
|
|
40
|
-
|
48
|
+
Fiber.pause
|
49
|
+
end
|
41
50
|
end
|
42
51
|
|
43
52
|
def io_write(io, buffer, length, offset = 0)
|
@@ -46,15 +55,11 @@ class Rage::FiberScheduler
|
|
46
55
|
|
47
56
|
::Iodine::Scheduler.write(io.fileno, buffer.get_string, bytes_to_write, offset)
|
48
57
|
|
49
|
-
|
58
|
+
bytes_to_write - offset
|
50
59
|
end
|
51
60
|
|
52
61
|
def kernel_sleep(duration = nil)
|
53
|
-
|
54
|
-
f = Fiber.current
|
55
|
-
::Iodine.run_after((duration * 1000).to_i) { f.resume }
|
56
|
-
Fiber.yield
|
57
|
-
end
|
62
|
+
block(nil, duration || 0)
|
58
63
|
end
|
59
64
|
|
60
65
|
# TODO: GC works a little strange with this closure;
|
@@ -75,13 +80,22 @@ class Rage::FiberScheduler
|
|
75
80
|
Resolv.getaddresses(hostname)
|
76
81
|
end
|
77
82
|
|
78
|
-
def block(
|
79
|
-
f = Fiber.current
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
+
def block(_blocker, timeout = nil)
|
84
|
+
f, fulfilled, channel = Fiber.current, false, "unblock:#{Fiber.current.object_id}"
|
85
|
+
|
86
|
+
resume_fiber_block = proc do
|
87
|
+
unless fulfilled
|
88
|
+
fulfilled = true
|
89
|
+
::Iodine.defer { ::Iodine.unsubscribe(channel) }
|
90
|
+
f.resume
|
91
|
+
end
|
83
92
|
end
|
84
|
-
|
93
|
+
|
94
|
+
::Iodine.subscribe(channel, &resume_fiber_block)
|
95
|
+
if timeout
|
96
|
+
::Iodine.run_after((timeout * 1000).to_i, &resume_fiber_block)
|
97
|
+
end
|
98
|
+
|
85
99
|
Fiber.yield
|
86
100
|
end
|
87
101
|
|
@@ -90,15 +104,28 @@ class Rage::FiberScheduler
|
|
90
104
|
end
|
91
105
|
|
92
106
|
def fiber(&block)
|
93
|
-
|
94
|
-
inner_schedule = f != @root_fiber
|
107
|
+
parent = Fiber.current
|
95
108
|
|
96
|
-
fiber =
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
109
|
+
fiber = if parent == @root_fiber
|
110
|
+
# the fiber to wrap a request in
|
111
|
+
Fiber.new(blocking: false) do
|
112
|
+
Fiber.current.__set_result(block.call)
|
113
|
+
end
|
114
|
+
else
|
115
|
+
# the fiber was created in the user code
|
116
|
+
logger = Thread.current[:rage_logger]
|
117
|
+
|
118
|
+
Fiber.new(blocking: false) do
|
119
|
+
Thread.current[:rage_logger] = logger
|
120
|
+
Fiber.current.__set_result(block.call)
|
121
|
+
# send a message for `Fiber.await` to work
|
122
|
+
Iodine.publish("await:#{parent.object_id}", "") if parent.alive?
|
123
|
+
rescue Exception => e
|
124
|
+
Fiber.current.__set_err(e)
|
125
|
+
Iodine.publish("await:#{parent.object_id}", Fiber::AWAIT_ERROR_MESSAGE) if parent.alive?
|
126
|
+
end
|
101
127
|
end
|
128
|
+
|
102
129
|
fiber.resume
|
103
130
|
|
104
131
|
fiber
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "logger"
|
4
|
+
|
5
|
+
##
|
6
|
+
# All logs in `rage` consist of two parts: keys and tags. A sample log entry might look like this:
|
7
|
+
# ```
|
8
|
+
# [fecbba0735355738] timestamp=2023-10-19T11:12:56+00:00 pid=1825 level=info message=hello
|
9
|
+
# ```
|
10
|
+
# In the log entry above, `timestamp`, `pid`, `level`, and `message` are keys, while `fecbba0735355738` is a tag.
|
11
|
+
#
|
12
|
+
# Use {tagged} to add custom tags to an entry:
|
13
|
+
# ```ruby
|
14
|
+
# Rage.logger.tagged("ApiCall") do
|
15
|
+
# perform_api_call
|
16
|
+
# Rage.logger.info "success"
|
17
|
+
# end
|
18
|
+
# # => [fecbba0735355738][ApiCall] timestamp=2023-10-19T11:12:56+00:00 pid=1825 level=info message=success
|
19
|
+
# ```
|
20
|
+
#
|
21
|
+
# {with_context} can be used to add custom keys:
|
22
|
+
# ```ruby
|
23
|
+
# cache_key = "mykey"
|
24
|
+
# Rage.logger.with_context(cache_key: cache_key) do
|
25
|
+
# get_from_cache(cache_key)
|
26
|
+
# Rage.logger.info "cache miss"
|
27
|
+
# end
|
28
|
+
# # => [fecbba0735355738] timestamp=2023-10-19T11:12:56+00:00 pid=1825 level=info cache_key=mykey message=cache miss
|
29
|
+
# ```
|
30
|
+
#
|
31
|
+
# `Rage::Logger` also implements the interface of Ruby's native {https://ruby-doc.org/3.2.2/stdlibs/logger/Logger.html Logger}:
|
32
|
+
# ```ruby
|
33
|
+
# Rage.logger.info("Initializing")
|
34
|
+
# Rage.logger.debug { "This is a " + potentially + " expensive operation" }
|
35
|
+
# ```
|
36
|
+
class Rage::Logger
|
37
|
+
METHODS_MAP = {
|
38
|
+
"debug" => Logger::DEBUG,
|
39
|
+
"info" => Logger::INFO,
|
40
|
+
"warn" => Logger::WARN,
|
41
|
+
"error" => Logger::ERROR,
|
42
|
+
"fatal" => Logger::FATAL,
|
43
|
+
"unknown" => Logger::UNKNOWN
|
44
|
+
}
|
45
|
+
private_constant :METHODS_MAP
|
46
|
+
|
47
|
+
attr_reader :level, :formatter
|
48
|
+
|
49
|
+
# Create a new logger.
|
50
|
+
#
|
51
|
+
# @param log [Object] a filename (`String`), IO object (typically `STDOUT`, `STDERR`, or an open file), `nil` (it writes nothing) or `File::NULL` (same as `nil`)
|
52
|
+
# @param level [Integer] logging severity threshold
|
53
|
+
# @param formatter [#call] logging formatter
|
54
|
+
# @param shift_age [Integer, String] number of old log files to keep, or frequency of rotation (`"daily"`, `"weekly"` or `"monthly"`). Default value is `0`, which disables log file rotation
|
55
|
+
# @param shift_size [Integer] maximum log file size in bytes (only applies when `shift_age` is a positive Integer)
|
56
|
+
# @param shift_period_suffix [String] the log file suffix format for daily, weekly or monthly rotation
|
57
|
+
# @param binmode sets whether the logger writes in binary mode
|
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
|
+
@logdev = Logger::LogDevice.new(log, shift_age:, shift_size:, shift_period_suffix:, binmode:)
|
61
|
+
end
|
62
|
+
|
63
|
+
@formatter = formatter
|
64
|
+
@level = level
|
65
|
+
define_log_methods
|
66
|
+
end
|
67
|
+
|
68
|
+
def level=(level)
|
69
|
+
@level = level
|
70
|
+
define_log_methods
|
71
|
+
end
|
72
|
+
|
73
|
+
def formatter=(formatter)
|
74
|
+
@formatter = formatter
|
75
|
+
define_log_methods
|
76
|
+
end
|
77
|
+
|
78
|
+
# Add custom keys to an entry.
|
79
|
+
#
|
80
|
+
# @param context [Hash] a hash of custom keys
|
81
|
+
# @example
|
82
|
+
# Rage.logger.with_context(key: "mykey") do
|
83
|
+
# Rage.logger.info "cache miss"
|
84
|
+
# end
|
85
|
+
def with_context(context)
|
86
|
+
old_context = Thread.current[:rage_logger][:context]
|
87
|
+
|
88
|
+
if old_context.empty? # there's nothing in the context yet
|
89
|
+
Thread.current[:rage_logger][:context] = context
|
90
|
+
else # it's not the first `with_context` call in the chain
|
91
|
+
Thread.current[:rage_logger][:context] = old_context.merge(context)
|
92
|
+
end
|
93
|
+
|
94
|
+
yield(self)
|
95
|
+
true
|
96
|
+
|
97
|
+
ensure
|
98
|
+
Thread.current[:rage_logger][:context] = old_context
|
99
|
+
end
|
100
|
+
|
101
|
+
# Add a custom tag to an entry.
|
102
|
+
#
|
103
|
+
# @param tag [String] the tag to add to an entry
|
104
|
+
# @example
|
105
|
+
# Rage.logger.tagged("ApiCall") do
|
106
|
+
# Rage.logger.info "success"
|
107
|
+
# end
|
108
|
+
def tagged(tag)
|
109
|
+
Thread.current[:rage_logger][:tags] << tag
|
110
|
+
|
111
|
+
yield(self)
|
112
|
+
true
|
113
|
+
|
114
|
+
ensure
|
115
|
+
Thread.current[:rage_logger][:tags].pop
|
116
|
+
end
|
117
|
+
|
118
|
+
alias_method :with_tag, :tagged
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def define_log_methods
|
123
|
+
methods = METHODS_MAP.map do |level_name, level_val|
|
124
|
+
if @logdev.nil? || level_val < @level
|
125
|
+
# logging is disabled or the log level is higher than the current one
|
126
|
+
<<-RUBY
|
127
|
+
def #{level_name}(msg = nil)
|
128
|
+
false
|
129
|
+
end
|
130
|
+
RUBY
|
131
|
+
elsif defined?(IRB)
|
132
|
+
# the call was made from IRB - don't use the formatter
|
133
|
+
<<-RUBY
|
134
|
+
def #{level_name}(msg = nil)
|
135
|
+
@logdev.write((msg || yield) + "\n")
|
136
|
+
end
|
137
|
+
RUBY
|
138
|
+
elsif @formatter.class.name.start_with?("Rage::")
|
139
|
+
# the call was made from within the application and a built-in formatter is used;
|
140
|
+
# in such case we use the `gen_timestamp` method which is much faster than `Time.now.strftime`;
|
141
|
+
# it's not a standard approach however, so it's used with built-in formatters only
|
142
|
+
<<-RUBY
|
143
|
+
def #{level_name}(msg = nil)
|
144
|
+
@logdev.write(
|
145
|
+
@formatter.call("#{level_name}".freeze, Iodine::Rack::Utils.gen_timestamp, nil, msg || yield)
|
146
|
+
)
|
147
|
+
end
|
148
|
+
RUBY
|
149
|
+
else
|
150
|
+
# the call was made from within the application and a custom formatter is used;
|
151
|
+
# stick to the standard approach of using one of the Log Level constants as sevetiry and `Time.now` as time
|
152
|
+
<<-RUBY
|
153
|
+
def #{level_name}(msg = nil)
|
154
|
+
@logdev.write(
|
155
|
+
@formatter.call(#{level_val}, Time.now, nil, msg || yield)
|
156
|
+
)
|
157
|
+
end
|
158
|
+
RUBY
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
self.class.class_eval(methods.join("\n"))
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class Rage::TextFormatter
|
2
|
+
def initialize
|
3
|
+
@pid = Process.pid
|
4
|
+
Iodine.on_state(:on_start) do
|
5
|
+
@pid = Process.pid
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(severity, timestamp, _, message)
|
10
|
+
logger = Thread.current[:rage_logger]
|
11
|
+
tags = logger[:tags]
|
12
|
+
|
13
|
+
if final = logger[:final]
|
14
|
+
params, env = final[:params], final[:env]
|
15
|
+
if params
|
16
|
+
return "[#{tags[0]}] timestamp=#{timestamp} pid=#{@pid} level=info method=#{env["REQUEST_METHOD"]} path=#{env["PATH_INFO"]} controller=#{params[:controller]} action=#{params[:action]} status=#{final[:response][0]} duration=#{final[:duration]}\n"
|
17
|
+
else
|
18
|
+
# no controller/action keys are written if there are no params
|
19
|
+
return "[#{tags[0]}] timestamp=#{timestamp} pid=#{@pid} level=info method=#{env["REQUEST_METHOD"]} path=#{env["PATH_INFO"]} status=#{final[:response][0]} duration=#{final[:duration]}\n"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
if tags.length == 1
|
24
|
+
tags_msg = "[#{tags[0]}] timestamp=#{timestamp} pid=#{@pid} level=#{severity}"
|
25
|
+
elsif tags.length == 2
|
26
|
+
tags_msg = "[#{tags[0]}][#{tags[1]}] timestamp=#{timestamp} pid=#{@pid} level=#{severity}"
|
27
|
+
else
|
28
|
+
tags_msg = "[#{tags[0]}][#{tags[1]}]"
|
29
|
+
i = 2
|
30
|
+
while i < tags.length
|
31
|
+
tags_msg << "[#{tags[i]}]"
|
32
|
+
i += 1
|
33
|
+
end
|
34
|
+
tags_msg << " timestamp=#{timestamp} pid=#{@pid} level=#{severity}"
|
35
|
+
end
|
36
|
+
|
37
|
+
context = logger[:context]
|
38
|
+
|
39
|
+
if !context.empty?
|
40
|
+
context_msg = ""
|
41
|
+
context.each { |k, v| context_msg << "#{k}=#{v} " }
|
42
|
+
end
|
43
|
+
|
44
|
+
"#{tags_msg} #{context_msg}message=#{message}\n"
|
45
|
+
end
|
46
|
+
end
|
data/lib/rage/router/backend.rb
CHANGED
@@ -14,8 +14,36 @@ class Rage::Router::Backend
|
|
14
14
|
@constrainer = Rage::Router::Constrainer.new({})
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
17
|
+
def mount(path, handler, methods)
|
18
|
+
raise "Mount handler should respond to `call`" unless handler.respond_to?(:call)
|
19
|
+
|
18
20
|
raw_handler = handler
|
21
|
+
is_sidekiq = handler.respond_to?(:name) && handler.name == "Sidekiq::Web"
|
22
|
+
|
23
|
+
handler = ->(env, _params) do
|
24
|
+
env["SCRIPT_NAME"] = path
|
25
|
+
sub_path = env["PATH_INFO"].delete_prefix!(path)
|
26
|
+
env["PATH_INFO"] = "/" if sub_path == ""
|
27
|
+
|
28
|
+
if is_sidekiq
|
29
|
+
Rage::SidekiqSession.with_session(env) do
|
30
|
+
raw_handler.call(env)
|
31
|
+
end
|
32
|
+
else
|
33
|
+
raw_handler.call(env)
|
34
|
+
end
|
35
|
+
|
36
|
+
ensure
|
37
|
+
env["PATH_INFO"] = "#{env["SCRIPT_NAME"]}#{sub_path}"
|
38
|
+
end
|
39
|
+
|
40
|
+
methods.each do |method|
|
41
|
+
__on(method, path, handler, {}, {}, { raw_handler:, mount: true })
|
42
|
+
__on(method, "#{path}/*", handler, {}, {}, { raw_handler:, mount: true })
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def on(method, path, handler, constraints: {}, defaults: nil)
|
19
47
|
raise "Path could not be empty" if path&.empty?
|
20
48
|
|
21
49
|
if match_index = (path =~ OPTIONAL_PARAM_REGEXP)
|
@@ -29,12 +57,17 @@ class Rage::Router::Backend
|
|
29
57
|
return
|
30
58
|
end
|
31
59
|
|
60
|
+
meta = { raw_handler: handler }
|
61
|
+
|
32
62
|
if handler.is_a?(String)
|
33
63
|
raise "Invalid route handler format, expected to match the 'controller#action' pattern" unless handler =~ STRING_HANDLER_REGEXP
|
34
64
|
|
35
65
|
controller, action = to_controller_class($1), $2
|
36
66
|
run_action_method_name = controller.__register_action(action.to_sym)
|
37
67
|
|
68
|
+
meta[:controller] = $1
|
69
|
+
meta[:action] = $2
|
70
|
+
|
38
71
|
handler = eval("->(env, params) { #{controller}.new(env, params).#{run_action_method_name} }")
|
39
72
|
else
|
40
73
|
raise "Non-string route handler should respond to `call`" unless handler.respond_to?(:call)
|
@@ -45,7 +78,7 @@ class Rage::Router::Backend
|
|
45
78
|
handler = ->(env, _params) { orig_handler.call(env) }
|
46
79
|
end
|
47
80
|
|
48
|
-
__on(method, path, handler,
|
81
|
+
__on(method, path, handler, constraints, defaults, meta)
|
49
82
|
end
|
50
83
|
|
51
84
|
def lookup(env)
|
@@ -55,7 +88,7 @@ class Rage::Router::Backend
|
|
55
88
|
|
56
89
|
private
|
57
90
|
|
58
|
-
def __on(method, path, handler,
|
91
|
+
def __on(method, path, handler, constraints, defaults, meta)
|
59
92
|
@constrainer.validate_constraints(constraints)
|
60
93
|
# Let the constrainer know if any constraints are being used now
|
61
94
|
@constrainer.note_usage(constraints)
|
@@ -162,7 +195,7 @@ class Rage::Router::Backend
|
|
162
195
|
end
|
163
196
|
end
|
164
197
|
|
165
|
-
route = { method
|
198
|
+
route = { method:, path:, pattern:, params:, constraints:, handler:, defaults:, meta: }
|
166
199
|
@routes << route
|
167
200
|
current_node.add_route(route, @constrainer)
|
168
201
|
end
|
data/lib/rage/router/dsl.rb
CHANGED
@@ -12,7 +12,7 @@ class Rage::Router::DSL
|
|
12
12
|
##
|
13
13
|
# This class implements routing logic for your application, providing API similar to Rails.
|
14
14
|
#
|
15
|
-
# Compared to Rails router, the most notable difference is that a wildcard segment can only be in the last section of the path and cannot be named.
|
15
|
+
# Compared to the Rails router, the most notable difference is that a wildcard segment can only be in the last section of the path and cannot be named.
|
16
16
|
# Example:
|
17
17
|
# ```ruby
|
18
18
|
# get "/photos/*"
|
@@ -327,8 +327,19 @@ class Rage::Router::DSL
|
|
327
327
|
via = args[1][:via]
|
328
328
|
end
|
329
329
|
|
330
|
-
|
331
|
-
|
330
|
+
at = "/#{at}" unless at.start_with?("/")
|
331
|
+
at = at.delete_suffix("/") if at.end_with?("/")
|
332
|
+
|
333
|
+
http_methods = if via == :all || via.nil?
|
334
|
+
@default_match_methods.map { |method| method.to_s.upcase! }
|
335
|
+
else
|
336
|
+
Array(via).map! do |method|
|
337
|
+
raise ArgumentError, "Invalid HTTP method: #{method}" unless @default_match_methods.include?(method)
|
338
|
+
method.to_s.upcase!
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
@router.mount(at, app, http_methods)
|
332
343
|
end
|
333
344
|
|
334
345
|
private
|
@@ -22,7 +22,7 @@ class Rage::Router::HandlerStorage
|
|
22
22
|
params: params,
|
23
23
|
constraints: constraints,
|
24
24
|
handler: route[:handler],
|
25
|
-
create_params_object: compile_create_params_object(params, route[:defaults])
|
25
|
+
create_params_object: compile_create_params_object(params, route[:defaults], route[:meta])
|
26
26
|
}
|
27
27
|
|
28
28
|
constraints_keys = constraints.keys
|
@@ -47,8 +47,11 @@ class Rage::Router::HandlerStorage
|
|
47
47
|
|
48
48
|
private
|
49
49
|
|
50
|
-
def compile_create_params_object(param_keys, defaults)
|
51
|
-
lines = [
|
50
|
+
def compile_create_params_object(param_keys, defaults, meta)
|
51
|
+
lines = [
|
52
|
+
":controller => '#{meta[:controller]}'.freeze",
|
53
|
+
":action => '#{meta[:action]}'.freeze"
|
54
|
+
]
|
52
55
|
|
53
56
|
param_keys.each_with_index do |key, i|
|
54
57
|
lines << ":#{key} => param_values[#{i}]"
|
@@ -56,7 +59,7 @@ class Rage::Router::HandlerStorage
|
|
56
59
|
|
57
60
|
if defaults
|
58
61
|
defaults.except(*param_keys.map(&:to_sym)).each do |key, value|
|
59
|
-
lines << ":#{key} => '#{value}'"
|
62
|
+
lines << ":#{key} => '#{value}'.freeze"
|
60
63
|
end
|
61
64
|
end
|
62
65
|
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "digest"
|
4
|
+
require "base64"
|
5
|
+
|
6
|
+
##
|
7
|
+
# Used **specifically** for compatibility with Sidekiq's Web interface.
|
8
|
+
# Remove once we have real sessions or once Sidekiq's author decides they
|
9
|
+
# don't need cookie sessions to protect against CSRF.
|
10
|
+
#
|
11
|
+
class Rage::SidekiqSession
|
12
|
+
KEY = Digest::SHA2.hexdigest(ENV["SECRET_KEY_BASE"] || File.read("Gemfile.lock") + File.read("config/routes.rb"))
|
13
|
+
SESSION_KEY = "rage.sidekiq.session"
|
14
|
+
|
15
|
+
def self.with_session(env)
|
16
|
+
env["rack.session"] = session = self.new(env)
|
17
|
+
response = yield
|
18
|
+
|
19
|
+
if session.changed
|
20
|
+
Rack::Utils.set_cookie_header!(
|
21
|
+
response[1],
|
22
|
+
SESSION_KEY,
|
23
|
+
{ path: env["SCRIPT_NAME"], httponly: true, same_site: true, value: session.dump }
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
response
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :changed
|
31
|
+
|
32
|
+
def initialize(env)
|
33
|
+
@env = env
|
34
|
+
session = Rack::Utils.parse_cookies(@env)[SESSION_KEY]
|
35
|
+
@data = decode_session(session)
|
36
|
+
end
|
37
|
+
|
38
|
+
def [](key)
|
39
|
+
@data[key]
|
40
|
+
end
|
41
|
+
|
42
|
+
def[]=(key, value)
|
43
|
+
@changed = true
|
44
|
+
@data[key] = value
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_hash
|
48
|
+
@data
|
49
|
+
end
|
50
|
+
|
51
|
+
def dump
|
52
|
+
encoded_data = Marshal.dump(@data)
|
53
|
+
signature = OpenSSL::HMAC.hexdigest("SHA256", KEY, encoded_data)
|
54
|
+
|
55
|
+
Base64.urlsafe_encode64("#{encoded_data}--#{signature}")
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def decode_session(session)
|
61
|
+
return {} unless session
|
62
|
+
|
63
|
+
encoded_data, signature = Base64.urlsafe_decode64(session).split("--")
|
64
|
+
ref_signature = OpenSSL::HMAC.hexdigest("SHA256", KEY, encoded_data)
|
65
|
+
|
66
|
+
if Rack::Utils.secure_compare(signature, ref_signature)
|
67
|
+
Marshal.load(encoded_data)
|
68
|
+
else
|
69
|
+
{}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "config/application"
|
@@ -1,7 +1,10 @@
|
|
1
|
-
Rage.configure do
|
1
|
+
Rage.configure do
|
2
2
|
# Specify the number of server processes to run. Defaults to number of CPU cores.
|
3
|
-
config.workers_count = 1
|
3
|
+
config.server.workers_count = 1
|
4
4
|
|
5
5
|
# Specify the port the server will listen on.
|
6
|
-
config.port = 3000
|
6
|
+
config.server.port = 3000
|
7
|
+
|
8
|
+
# Specify the logger
|
9
|
+
config.logger = Rage::Logger.new(STDOUT)
|
7
10
|
end
|
@@ -1,7 +1,11 @@
|
|
1
|
-
Rage.configure do
|
1
|
+
Rage.configure do
|
2
2
|
# Specify the number of server processes to run. Defaults to number of CPU cores.
|
3
|
-
# config.workers_count = ENV.fetch("WEB_CONCURRENCY", 1)
|
3
|
+
# config.server.workers_count = ENV.fetch("WEB_CONCURRENCY", 1)
|
4
4
|
|
5
5
|
# Specify the port the server will listen on.
|
6
|
-
config.port = 3000
|
6
|
+
config.server.port = 3000
|
7
|
+
|
8
|
+
# Specify the logger
|
9
|
+
config.logger = Rage::Logger.new("log/production.log")
|
10
|
+
config.log_level = Logger::INFO
|
7
11
|
end
|
@@ -1,7 +1,10 @@
|
|
1
|
-
Rage.configure do
|
1
|
+
Rage.configure do
|
2
2
|
# Specify the number of server processes to run. Defaults to number of CPU cores.
|
3
|
-
config.workers_count = 1
|
3
|
+
config.server.workers_count = 1
|
4
4
|
|
5
5
|
# Specify the port the server will listen on.
|
6
|
-
config.port = 3000
|
6
|
+
config.server.port = 3000
|
7
|
+
|
8
|
+
# Specify the logger
|
9
|
+
config.logger = Rage::Logger.new("log/test.log")
|
7
10
|
end
|
data/lib/rage/version.rb
CHANGED
data/lib/rage-rb.rb
CHANGED
@@ -22,8 +22,9 @@ module Rage
|
|
22
22
|
@config ||= Rage::Configuration.new
|
23
23
|
end
|
24
24
|
|
25
|
-
def self.configure
|
26
|
-
|
25
|
+
def self.configure(&)
|
26
|
+
config.instance_eval(&)
|
27
|
+
config.__finalize
|
27
28
|
end
|
28
29
|
|
29
30
|
def self.env
|
@@ -38,6 +39,10 @@ module Rage
|
|
38
39
|
@root ||= Pathname.new(".").expand_path
|
39
40
|
end
|
40
41
|
|
42
|
+
def self.logger
|
43
|
+
@logger ||= config.logger
|
44
|
+
end
|
45
|
+
|
41
46
|
module Router
|
42
47
|
module Strategies
|
43
48
|
end
|
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.5.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-
|
11
|
+
date: 2023-11-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '2.
|
47
|
+
version: '2.2'
|
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: '2.
|
54
|
+
version: '2.2'
|
55
55
|
description:
|
56
56
|
email:
|
57
57
|
- rsamoi@icloud.com
|
@@ -79,6 +79,8 @@ files:
|
|
79
79
|
- lib/rage/errors.rb
|
80
80
|
- lib/rage/fiber.rb
|
81
81
|
- lib/rage/fiber_scheduler.rb
|
82
|
+
- lib/rage/logger/logger.rb
|
83
|
+
- lib/rage/logger/text_formatter.rb
|
82
84
|
- lib/rage/params_parser.rb
|
83
85
|
- lib/rage/request.rb
|
84
86
|
- lib/rage/router/README.md
|
@@ -89,7 +91,9 @@ files:
|
|
89
91
|
- lib/rage/router/node.rb
|
90
92
|
- lib/rage/router/strategies/host.rb
|
91
93
|
- lib/rage/setup.rb
|
94
|
+
- lib/rage/sidekiq_session.rb
|
92
95
|
- lib/rage/templates/Gemfile
|
96
|
+
- lib/rage/templates/Rakefile
|
93
97
|
- lib/rage/templates/app-controllers-application_controller.rb
|
94
98
|
- lib/rage/templates/config-application.rb
|
95
99
|
- lib/rage/templates/config-environments-development.rb
|