rage-rb 0.7.0 → 1.0.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/.yardopts +1 -1
- data/CHANGELOG.md +15 -0
- data/README.md +1 -1
- data/lib/rage/fiber.rb +14 -6
- data/lib/rage/fiber_scheduler.rb +20 -10
- data/lib/rage/logger/json_formatter.rb +3 -1
- data/lib/rage/logger/logger.rb +8 -7
- data/lib/rage/logger/text_formatter.rb +3 -1
- data/lib/rage/middleware/fiber_wrapper.rb +1 -1
- data/lib/rage/rails.rb +15 -0
- data/lib/rage/rspec.rb +178 -0
- data/lib/rage/version.rb +1 -1
- data/rage.gemspec +1 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9bb19c01aee898bea43a41450feeb655f94bde8277a4f9c18cab6b9d1accbb47
|
4
|
+
data.tar.gz: fe4806d8a2bfbb71496a720371cc8416b5283c9b8284c1a72ec46384ee234348
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 43d06881f297512724588c06637a29eefcb8308fc1c086a09b013c4291d4ca58cf7b2ab482b1c1276a3e4a88544a71e95289268cc896d479cb92135f31555bf8
|
7
|
+
data.tar.gz: 9af4f8816208451c18ff3b31d7d5a7e230561af93753d1ee0f8177f515b92f3dd2c47007b7f65be823ceeb69b6338683a05c57eb1a13e17cfe769b87e4ef2540
|
data/.yardopts
CHANGED
@@ -1 +1 @@
|
|
1
|
-
--exclude lib/rage/templates --markup markdown --no-private -o doc
|
1
|
+
--exclude lib/rage/templates --exclude lib/rage/rspec --exclude lib/rage/rails --markup markdown --no-private -o doc
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.0.0] - 2024-03-13
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- RSpec integration (#60).
|
8
|
+
- Add DNS cache (#65).
|
9
|
+
- Allow to disable the `FiberScheduler#io_write` hook (#63).
|
10
|
+
|
11
|
+
### Fixed
|
12
|
+
|
13
|
+
- Preload fiber ID (#62).
|
14
|
+
- Release ActiveRecord connections on yield (#66).
|
15
|
+
- Logger fixes (#64).
|
16
|
+
- Fix publish calls in cluster mode (#67).
|
17
|
+
|
3
18
|
## [0.7.0] - 2024-01-09
|
4
19
|
|
5
20
|
- Add conditional GET using `stale?` by [@tonekk](https://github.com/tonekk) (#55).
|
data/README.md
CHANGED
@@ -150,8 +150,8 @@ Status | Changes
|
|
150
150
|
: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;~~
|
151
151
|
:white_check_mark: | ~~Add request logging.~~
|
152
152
|
:white_check_mark: | ~~Automatic code reloading in development with Zeitwerk.~~
|
153
|
+
:white_check_mark: | ~~Support conditional get with `etag` and `last_modified`.~~
|
153
154
|
⏳ | Expose the `send_data` and `send_file` methods.
|
154
|
-
⏳ | Support conditional get with `etag` and `last_modified`.
|
155
155
|
⏳ | Expose the `cookies` and `session` objects.
|
156
156
|
⏳ | Implement Iodine-based equivalent of Action Cable.
|
157
157
|
|
data/lib/rage/fiber.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Fiber
|
4
|
+
# @private
|
4
5
|
AWAIT_ERROR_MESSAGE = "err"
|
5
6
|
|
6
7
|
# @private
|
@@ -23,14 +24,14 @@ class Fiber
|
|
23
24
|
@__err
|
24
25
|
end
|
25
26
|
|
26
|
-
|
27
|
-
def
|
28
|
-
@__rage_id
|
27
|
+
# @private
|
28
|
+
def __set_id
|
29
|
+
@__rage_id = object_id.to_s
|
29
30
|
end
|
30
31
|
|
31
|
-
|
32
|
-
def
|
33
|
-
|
32
|
+
# @private
|
33
|
+
def __get_id
|
34
|
+
@__rage_id
|
34
35
|
end
|
35
36
|
|
36
37
|
# @private
|
@@ -49,6 +50,13 @@ class Fiber
|
|
49
50
|
Fiber.yield
|
50
51
|
end
|
51
52
|
|
53
|
+
# @private
|
54
|
+
# under normal circumstances, the method is a copy of `yield`, but it can be overriden to perform
|
55
|
+
# additional steps on yielding, e.g. releasing AR connections; see "lib/rage/rails.rb"
|
56
|
+
class << self
|
57
|
+
alias_method :defer, :yield
|
58
|
+
end
|
59
|
+
|
52
60
|
# Wait on several fibers at the same time. Calling this method will automatically pause the current fiber, allowing the
|
53
61
|
# server to process other requests. Once all fibers have completed, the current fiber will be automatically resumed.
|
54
62
|
#
|
data/lib/rage/fiber_scheduler.rb
CHANGED
@@ -7,13 +7,14 @@ class Rage::FiberScheduler
|
|
7
7
|
|
8
8
|
def initialize
|
9
9
|
@root_fiber = Fiber.current
|
10
|
+
@dns_cache = {}
|
10
11
|
end
|
11
12
|
|
12
13
|
def io_wait(io, events, timeout = nil)
|
13
14
|
f = Fiber.current
|
14
15
|
::Iodine::Scheduler.attach(io.fileno, events, timeout&.ceil || 0) { |err| f.resume(err) }
|
15
16
|
|
16
|
-
err = Fiber.
|
17
|
+
err = Fiber.defer
|
17
18
|
if err == Errno::ETIMEDOUT::Errno
|
18
19
|
0
|
19
20
|
else
|
@@ -49,13 +50,15 @@ class Rage::FiberScheduler
|
|
49
50
|
end
|
50
51
|
end
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
|
53
|
+
unless ENV["RAGE_DISABLE_IO_WRITE"]
|
54
|
+
def io_write(io, buffer, length, offset = 0)
|
55
|
+
bytes_to_write = length
|
56
|
+
bytes_to_write = buffer.size if length == 0
|
55
57
|
|
56
|
-
|
58
|
+
::Iodine::Scheduler.write(io.fileno, buffer.get_string, bytes_to_write, offset)
|
57
59
|
|
58
|
-
|
60
|
+
bytes_to_write - offset
|
61
|
+
end
|
59
62
|
end
|
60
63
|
|
61
64
|
def kernel_sleep(duration = nil)
|
@@ -77,7 +80,13 @@ class Rage::FiberScheduler
|
|
77
80
|
# end
|
78
81
|
|
79
82
|
def address_resolve(hostname)
|
80
|
-
|
83
|
+
@dns_cache[hostname] ||= begin
|
84
|
+
::Iodine.run_after(60_000) do
|
85
|
+
@dns_cache[hostname] = nil
|
86
|
+
end
|
87
|
+
|
88
|
+
Resolv.getaddresses(hostname)
|
89
|
+
end
|
81
90
|
end
|
82
91
|
|
83
92
|
def block(_blocker, timeout = nil)
|
@@ -100,7 +109,7 @@ class Rage::FiberScheduler
|
|
100
109
|
end
|
101
110
|
|
102
111
|
def unblock(_blocker, fiber)
|
103
|
-
::Iodine.publish(fiber.__block_channel, "")
|
112
|
+
::Iodine.publish(fiber.__block_channel, "", Iodine::PubSub::PROCESS)
|
104
113
|
end
|
105
114
|
|
106
115
|
def fiber(&block)
|
@@ -109,6 +118,7 @@ class Rage::FiberScheduler
|
|
109
118
|
fiber = if parent == @root_fiber
|
110
119
|
# the fiber to wrap a request in
|
111
120
|
Fiber.new(blocking: false) do
|
121
|
+
Fiber.current.__set_id
|
112
122
|
Fiber.current.__set_result(block.call)
|
113
123
|
end
|
114
124
|
else
|
@@ -119,10 +129,10 @@ class Rage::FiberScheduler
|
|
119
129
|
Thread.current[:rage_logger] = logger
|
120
130
|
Fiber.current.__set_result(block.call)
|
121
131
|
# send a message for `Fiber.await` to work
|
122
|
-
Iodine.publish("await:#{parent.object_id}", "") if parent.alive?
|
132
|
+
Iodine.publish("await:#{parent.object_id}", "", Iodine::PubSub::PROCESS) if parent.alive?
|
123
133
|
rescue Exception => e
|
124
134
|
Fiber.current.__set_err(e)
|
125
|
-
Iodine.publish("await:#{parent.object_id}", Fiber::AWAIT_ERROR_MESSAGE) if parent.alive?
|
135
|
+
Iodine.publish("await:#{parent.object_id}", Fiber::AWAIT_ERROR_MESSAGE, Iodine::PubSub::PROCESS) if parent.alive?
|
126
136
|
end
|
127
137
|
end
|
128
138
|
|
@@ -7,7 +7,7 @@ class Rage::JSONFormatter
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def call(severity, timestamp, _, message)
|
10
|
-
logger = Thread.current[:rage_logger]
|
10
|
+
logger = Thread.current[:rage_logger] || { tags: [], context: {} }
|
11
11
|
tags, context = logger[:tags], logger[:context]
|
12
12
|
|
13
13
|
if !context.empty?
|
@@ -29,6 +29,8 @@ class Rage::JSONFormatter
|
|
29
29
|
tags_msg = "{\"tags\":[\"#{tags[0]}\"],\"timestamp\":\"#{timestamp}\",\"pid\":\"#{@pid}\",\"level\":\"#{severity}\""
|
30
30
|
elsif tags.length == 2
|
31
31
|
tags_msg = "{\"tags\":[\"#{tags[0]}\",\"#{tags[1]}\"],\"timestamp\":\"#{timestamp}\",\"pid\":\"#{@pid}\",\"level\":\"#{severity}\""
|
32
|
+
elsif tags.length == 0
|
33
|
+
tags_msg = "{\"timestamp\":\"#{timestamp}\",\"pid\":\"#{@pid}\",\"level\":\"#{severity}\""
|
32
34
|
else
|
33
35
|
tags_msg = "{\"tags\":[\"#{tags[0]}\",\"#{tags[1]}\""
|
34
36
|
i = 2
|
data/lib/rage/logger/logger.rb
CHANGED
@@ -83,7 +83,7 @@ class Rage::Logger
|
|
83
83
|
# Rage.logger.info "cache miss"
|
84
84
|
# end
|
85
85
|
def with_context(context)
|
86
|
-
old_context = Thread.current[:rage_logger][:context]
|
86
|
+
old_context = (Thread.current[:rage_logger] ||= { tags: [], context: {} })[:context]
|
87
87
|
|
88
88
|
if old_context.empty? # there's nothing in the context yet
|
89
89
|
Thread.current[:rage_logger][:context] = context
|
@@ -92,8 +92,6 @@ class Rage::Logger
|
|
92
92
|
end
|
93
93
|
|
94
94
|
yield(self)
|
95
|
-
true
|
96
|
-
|
97
95
|
ensure
|
98
96
|
Thread.current[:rage_logger][:context] = old_context
|
99
97
|
end
|
@@ -106,17 +104,20 @@ class Rage::Logger
|
|
106
104
|
# Rage.logger.info "success"
|
107
105
|
# end
|
108
106
|
def tagged(tag)
|
109
|
-
Thread.current[:rage_logger][:tags] << tag
|
110
|
-
|
107
|
+
(Thread.current[:rage_logger] ||= { tags: [], context: {} })[:tags] << tag
|
111
108
|
yield(self)
|
112
|
-
true
|
113
|
-
|
114
109
|
ensure
|
115
110
|
Thread.current[:rage_logger][:tags].pop
|
116
111
|
end
|
117
112
|
|
118
113
|
alias_method :with_tag, :tagged
|
119
114
|
|
115
|
+
def debug? = @level <= Logger::DEBUG
|
116
|
+
def error? = @level <= Logger::ERROR
|
117
|
+
def fatal? = @level <= Logger::FATAL
|
118
|
+
def info? = @level <= Logger::INFO
|
119
|
+
def warn? = @level <= Logger::WARN
|
120
|
+
|
120
121
|
private
|
121
122
|
|
122
123
|
def define_log_methods
|
@@ -7,7 +7,7 @@ class Rage::TextFormatter
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def call(severity, timestamp, _, message)
|
10
|
-
logger = Thread.current[:rage_logger]
|
10
|
+
logger = Thread.current[:rage_logger] || { tags: [], context: {} }
|
11
11
|
tags, context = logger[:tags], logger[:context]
|
12
12
|
|
13
13
|
if !context.empty?
|
@@ -29,6 +29,8 @@ class Rage::TextFormatter
|
|
29
29
|
tags_msg = "[#{tags[0]}] timestamp=#{timestamp} pid=#{@pid} level=#{severity}"
|
30
30
|
elsif tags.length == 2
|
31
31
|
tags_msg = "[#{tags[0]}][#{tags[1]}] timestamp=#{timestamp} pid=#{@pid} level=#{severity}"
|
32
|
+
elsif tags.length == 0
|
33
|
+
tags_msg = "timestamp=#{timestamp} pid=#{@pid} level=#{severity}"
|
32
34
|
else
|
33
35
|
tags_msg = "[#{tags[0]}][#{tags[1]}]"
|
34
36
|
i = 2
|
@@ -17,7 +17,7 @@ class Rage::FiberWrapper
|
|
17
17
|
@app.call(env)
|
18
18
|
ensure
|
19
19
|
# notify Iodine the request can now be resumed
|
20
|
-
Iodine.publish(Fiber.current.__get_id, "", Iodine::PubSub::PROCESS)
|
20
|
+
Iodine.publish(Fiber.current.__get_id, "", Iodine::PubSub::PROCESS)
|
21
21
|
end
|
22
22
|
|
23
23
|
# the fiber encountered blocking IO and yielded; instruct Iodine to pause the request
|
data/lib/rage/rails.rb
CHANGED
@@ -30,6 +30,21 @@ if defined?(ActiveRecord)
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
+
# release ActiveRecord connections on yield
|
34
|
+
if defined?(ActiveRecord)
|
35
|
+
class Fiber
|
36
|
+
def self.defer
|
37
|
+
res = Fiber.yield
|
38
|
+
|
39
|
+
if ActiveRecord::Base.connection_pool.active_connection?
|
40
|
+
ActiveRecord::Base.connection_handler.clear_active_connections!
|
41
|
+
end
|
42
|
+
|
43
|
+
res
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
33
48
|
# plug into Rails' Zeitwerk instance to reload the code
|
34
49
|
Rails.autoloaders.main.on_setup do
|
35
50
|
if Iodine.running?
|
data/lib/rage/rspec.rb
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rack/test"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
# set up environment
|
7
|
+
ENV["RAGE_ENV"] ||= ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "test"
|
8
|
+
|
9
|
+
# load the app
|
10
|
+
require "bundler/setup"
|
11
|
+
require "rage"
|
12
|
+
require_relative "#{Rage.root}/config/application"
|
13
|
+
|
14
|
+
# verify the environment
|
15
|
+
abort("The test suite is running in #{Rage.env} mode instead of 'test'!") unless Rage.env.test?
|
16
|
+
|
17
|
+
# mock fiber methods as RSpec tests don't run concurrently
|
18
|
+
class Fiber
|
19
|
+
def self.schedule(&block)
|
20
|
+
fiber = Fiber.new(blocking: true) do
|
21
|
+
Fiber.current.__set_id
|
22
|
+
Fiber.current.__set_result(block.call)
|
23
|
+
end
|
24
|
+
fiber.resume
|
25
|
+
|
26
|
+
fiber
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.await(_)
|
30
|
+
# no-op
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# define request helpers
|
35
|
+
module RageRequestHelpers
|
36
|
+
include Rack::Test::Methods
|
37
|
+
|
38
|
+
alias_method :response, :last_response
|
39
|
+
|
40
|
+
APP = Rack::Builder.parse_file("#{Rage.root}/config.ru").yield_self do |app|
|
41
|
+
app.is_a?(Array) ? app[0] : app
|
42
|
+
end
|
43
|
+
|
44
|
+
def app
|
45
|
+
APP
|
46
|
+
end
|
47
|
+
|
48
|
+
%w(get options head).each do |method_name|
|
49
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
50
|
+
def #{method_name}(path, params: {}, headers: {})
|
51
|
+
request("#{method_name.upcase}", path, params: params, headers: headers)
|
52
|
+
end
|
53
|
+
RUBY
|
54
|
+
end
|
55
|
+
|
56
|
+
%w(post put patch delete).each do |method_name|
|
57
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
58
|
+
def #{method_name}(path, params: {}, headers: {}, as: nil)
|
59
|
+
if as == :json
|
60
|
+
params = params.to_json
|
61
|
+
headers["content-type"] = "application/json"
|
62
|
+
end
|
63
|
+
|
64
|
+
request("#{method_name.upcase}", path, params: params, headers: headers.merge("IODINE_HAS_BODY" => !params.empty?))
|
65
|
+
end
|
66
|
+
RUBY
|
67
|
+
end
|
68
|
+
|
69
|
+
def request(method, path, params: {}, headers: {})
|
70
|
+
if headers.any?
|
71
|
+
headers = headers.transform_keys do |k|
|
72
|
+
if k.downcase == "content-type"
|
73
|
+
"CONTENT_TYPE"
|
74
|
+
elsif k.downcase == "content-length"
|
75
|
+
"CONTENT_LENGTH"
|
76
|
+
elsif k.upcase == k
|
77
|
+
k
|
78
|
+
else
|
79
|
+
"HTTP_#{k.tr("-", "_").upcase! || k}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
custom_request(method, path, params, headers)
|
85
|
+
end
|
86
|
+
|
87
|
+
def host!(host)
|
88
|
+
@__host = host
|
89
|
+
end
|
90
|
+
|
91
|
+
def default_host
|
92
|
+
@__host || "example.org"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# include request helpers
|
97
|
+
RSpec.configure do |config|
|
98
|
+
config.include(RageRequestHelpers, type: :request)
|
99
|
+
end
|
100
|
+
|
101
|
+
# patch MockResponse class
|
102
|
+
class Rack::MockResponse
|
103
|
+
def parsed_body
|
104
|
+
if headers["content-type"].start_with?("application/json")
|
105
|
+
JSON.parse(body)
|
106
|
+
else
|
107
|
+
body
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def code
|
112
|
+
status.to_s
|
113
|
+
end
|
114
|
+
|
115
|
+
alias_method :response_code, :status
|
116
|
+
end
|
117
|
+
|
118
|
+
# define http status matcher
|
119
|
+
RSpec::Matchers.matcher :have_http_status do |expected|
|
120
|
+
codes = Rack::Utils::SYMBOL_TO_STATUS_CODE
|
121
|
+
|
122
|
+
failure_message do |response|
|
123
|
+
actual = response.status
|
124
|
+
|
125
|
+
if expected.is_a?(Integer)
|
126
|
+
"expected the response to have status code #{expected} but it was #{actual}"
|
127
|
+
elsif expected == :success
|
128
|
+
"expected the response to have a success status code (2xx) but it was #{actual}"
|
129
|
+
elsif expected == :error
|
130
|
+
"expected the response to have an error status code (5xx) but it was #{actual}"
|
131
|
+
elsif expected == :missing
|
132
|
+
"expected the response to have a missing status code (404) but it was #{actual}"
|
133
|
+
else
|
134
|
+
"expected the response to have status code :#{expected} (#{codes[expected]}) but it was :#{codes.key(actual)} (#{actual})"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
failure_message_when_negated do |response|
|
139
|
+
actual = response.status
|
140
|
+
|
141
|
+
if expected.is_a?(Integer)
|
142
|
+
"expected the response not to have status code #{expected} but it was #{actual}"
|
143
|
+
elsif expected == :success
|
144
|
+
"expected the response not to have a success status code (2xx) but it was #{actual}"
|
145
|
+
elsif expected == :error
|
146
|
+
"expected the response not to have an error status code (5xx) but it was #{actual}"
|
147
|
+
elsif expected == :missing
|
148
|
+
"expected the response not to have a missing status code (404) but it was #{actual}"
|
149
|
+
else
|
150
|
+
"expected the response not to have status code :#{expected} (#{codes[expected]}) but it was :#{codes.key(actual)} (#{actual})"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
match do |response|
|
155
|
+
actual = response.status
|
156
|
+
|
157
|
+
case expected
|
158
|
+
when :success
|
159
|
+
actual >= 200 && actual < 300
|
160
|
+
when :error
|
161
|
+
actual >= 500
|
162
|
+
when :missing
|
163
|
+
actual == 404
|
164
|
+
when Symbol
|
165
|
+
actual == codes.fetch(expected)
|
166
|
+
else
|
167
|
+
actual == expected
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
if defined? RSpec::Rails::Matchers
|
173
|
+
module RSpec::Rails::Matchers
|
174
|
+
def have_http_status(_)
|
175
|
+
super
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
data/lib/rage/version.rb
CHANGED
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: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roman Samoilov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-03-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '2.6'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rack-test
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.1'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.1'
|
69
83
|
description:
|
70
84
|
email:
|
71
85
|
- rsamoi@icloud.com
|
@@ -112,6 +126,7 @@ files:
|
|
112
126
|
- lib/rage/router/handler_storage.rb
|
113
127
|
- lib/rage/router/node.rb
|
114
128
|
- lib/rage/router/strategies/host.rb
|
129
|
+
- lib/rage/rspec.rb
|
115
130
|
- lib/rage/setup.rb
|
116
131
|
- lib/rage/sidekiq_session.rb
|
117
132
|
- lib/rage/templates/Gemfile
|