rage-rb 0.4.0 → 0.5.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 +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
|