concurrent_rails 0.1.5 → 0.2.1
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/README.md +83 -11
- data/lib/concurrent_rails.rb +3 -5
- data/lib/concurrent_rails/adapters/delay.rb +23 -0
- data/lib/concurrent_rails/adapters/future.rb +23 -0
- data/lib/concurrent_rails/future.rb +16 -13
- data/lib/concurrent_rails/multi.rb +5 -3
- data/lib/concurrent_rails/promises.rb +57 -22
- data/lib/concurrent_rails/testing.rb +37 -0
- data/lib/concurrent_rails/version.rb +1 -1
- metadata +34 -4
- data/lib/tasks/concurrent_rails_tasks.rake +0 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a0352c1516c930c65708383fa734cd7444b02b0a213c0ef1f794e5e31f8e1fdf
|
|
4
|
+
data.tar.gz: 33ec089c95b33ff31a4652a073feac356e8b23a8d7e30bd214ea0a0dbc4aedcb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a486a3ab777574962127795db050fd2615b69594d0bc88adb5114368c7d90492f2b51b237817f97118a31969396d53171bf66371240577051977a052d935148a
|
|
7
|
+
data.tar.gz: db095fe9e33e527a3ade62e8a40aec1866718bfce6b55ef519775cd1761d78b1eb1868d8c8b0ab799dcd05f78980ba16e31f5a7912b87ab3e2dd49d917dab004
|
data/README.md
CHANGED
|
@@ -2,19 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
Multithread is hard. [concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby) did an amazing job
|
|
6
|
-
implementing the concepts of multithread in the Ruby world. The problem is that Rails doesn't play nice with it. Rails have a complex way of managing threads called Executor and concurrent-ruby (most specifically, [Future](https://github.com/ruby-concurrency/concurrent-ruby/blob/master/docs-source/future.md)) does not work seamlessly with it.
|
|
5
|
+
Multithread is hard. [concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby) did an amazing job implementing the concepts of multithread in the Ruby world. The problem is that Rails doesn't play nice with it. Rails have a complex way of managing threads called Executor and concurrent-ruby (most specifically, [Future](https://github.com/ruby-concurrency/concurrent-ruby/blob/master/docs-source/future.md)) does not work seamlessly with it.
|
|
7
6
|
|
|
8
7
|
The goal of this gem is to provide a simple library that allows the developer to work with Futures without having to care about Rails's Executor and the whole pack of problems that come with it: autoload, thread pools, active record connections, etc.
|
|
9
8
|
|
|
10
9
|
## Usage
|
|
11
10
|
|
|
12
|
-
This library provides three classes that will help you run tasks in parallel: `ConcurrentRails::Promises`, `ConcurrentRails::Future` and `ConcurrentRails::Multi`
|
|
11
|
+
This library provides three classes that will help you run tasks in parallel: `ConcurrentRails::Promises`, `ConcurrentRails::Future` ([in process of being deprecated by concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby#deprecated)) and `ConcurrentRails::Multi`
|
|
13
12
|
|
|
14
13
|
### Promises
|
|
15
14
|
|
|
16
|
-
`Promises` is the recommended way from `concurrent-ruby` to create `Future`s as `Concurrent::Future` will be deprecated at some point.
|
|
17
|
-
Similar to other classes, all you have to do is call `.future` helper and pass a block:
|
|
15
|
+
`Promises` is the recommended way from `concurrent-ruby` to create `Future`s as `Concurrent::Future` will be deprecated at some point. All you have to do is call `#future` and pass a block to be executed asynchronously:
|
|
18
16
|
|
|
19
17
|
```ruby
|
|
20
18
|
irb(main):001:0> future = ConcurrentRails::Promises.future(5) { |v| sleep(v); 42 }
|
|
@@ -40,9 +38,52 @@ irb(main):002:0> future.value
|
|
|
40
38
|
=> 84
|
|
41
39
|
```
|
|
42
40
|
|
|
43
|
-
###
|
|
41
|
+
### Delayed futures
|
|
44
42
|
|
|
45
|
-
|
|
43
|
+
Delayed future is a future that is enqueued but not run until `#touch` or any other method that requires a resolution is called.
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
irb(main):002:0> delay = ConcurrentRails::Promises.delay { 42 }
|
|
47
|
+
=> #<ConcurrentRails::Promises:0x00007f8b55333d48 @executor=:io, @instan...
|
|
48
|
+
|
|
49
|
+
irb(main):003:0> delay.state
|
|
50
|
+
=> :pending
|
|
51
|
+
|
|
52
|
+
irb(main):004:0> delay.touch
|
|
53
|
+
=> #<Concurrent::Promises::Future:0x00007f8b553325b0 pending>
|
|
54
|
+
|
|
55
|
+
irb(main):005:0> delay.state
|
|
56
|
+
=> :fulfilled
|
|
57
|
+
|
|
58
|
+
irb(main):006:0> delay.value
|
|
59
|
+
=> 42
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Three methods will trigger a resolution: `#touch`, `#value` and `#wait`: `#touch` will simply trigger the execution but won't block the main thread, while `#wait` and `#value` will block the main thread until a resolution is given.
|
|
63
|
+
|
|
64
|
+
### Callbacks
|
|
65
|
+
|
|
66
|
+
Delayed and regular futures can set a callback to be executed after the resolution of the future. There are three different callbacks:
|
|
67
|
+
|
|
68
|
+
* `on_resolution`: runs after the future is resolved and yields three parameters to the callback in the following order: `true/false` for future's fulfillment, `value` as the result of the future execution, and `reason`, that will be `nil` if the future fulfilled or the error that the future triggered.
|
|
69
|
+
|
|
70
|
+
* `on_fulfillment`: runs after the future is fulfilled and yields `value` to the callback
|
|
71
|
+
|
|
72
|
+
* `on_rejection`: runs after the future is rejected and yields the `error` to the callback
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
delay = ConcurrentRails::Promises.delay { complex_find_user_query }.
|
|
76
|
+
on_fulfillment { |user| user.update!(name: 'John Doe') }.
|
|
77
|
+
on_rejection { |reason| log_error(reason) }
|
|
78
|
+
|
|
79
|
+
delay.touch
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
All of these callbacks have a bang version (e.g. `on_fulfillment!`). The bang version will execute the callback on the same thread pool that was initially set up and the version without bang will run asynchronously on a different executor.
|
|
83
|
+
|
|
84
|
+
### (Deprecated) Future
|
|
85
|
+
|
|
86
|
+
`ConcurrentRails::Future` will execute your code in a separate thread and you can check the progress of it whenever you need it. When the task is ready, you can access the result with `#result` function:
|
|
46
87
|
|
|
47
88
|
```ruby
|
|
48
89
|
irb(main):001:0> future = ConcurrentRails::Future.new do
|
|
@@ -81,7 +122,7 @@ irb(main):005:0> future.reason
|
|
|
81
122
|
=> #<ZeroDivisionError: divided by 0>
|
|
82
123
|
```
|
|
83
124
|
|
|
84
|
-
### Multi
|
|
125
|
+
### (Deprecated) Multi
|
|
85
126
|
|
|
86
127
|
`ConcurrentRails::Multi` will let you execute multiple tasks in parallel and aggregate the results of each task when they are done. `Multi` accepts an undefined number of `Proc`s.
|
|
87
128
|
|
|
@@ -132,6 +173,37 @@ irb(main):007:0> multi.errors
|
|
|
132
173
|
|
|
133
174
|
It is worth mention that a failed proc will return `nil`.
|
|
134
175
|
|
|
176
|
+
## Testing
|
|
177
|
+
If you are using RSpec, you will notice that it might not play well with threads. ActiveRecord opens a database connection for every thread and since RSpec tests are wrapped in a transaction, by the time you promise tries to access something on the database, for example, a user, gems like Database Cleaner probably already triggered and deleted the user, resulting in `ActiveRecord::RecordNotFound` errors. You have a couple of solutions like disable transactional fixtures if you are using it or update the Database Cleaner strategy (that will result in much slower tests).
|
|
178
|
+
Since none of these solutions were satisfactory to me, I created `ConcurrentRails::Testing` with two strategies: `immediate!` and `fake!`. When you wrap a Promise's `future` with `immediate!`, the executor gets replaced from `:io` to `:immediate`. It still returns a promise anyway. This is not the case with `fake!` strategy: it executes the task outside the `ConcurrentRails` engine and returns whatever `.value` would return:
|
|
179
|
+
|
|
180
|
+
`immediate!` strategy:
|
|
181
|
+
```ruby
|
|
182
|
+
irb(main):001:1* result = ConcurrentRails::Testing.immediate! do
|
|
183
|
+
irb(main):002:1* ConcurrentRails::Promises.future { 42 }
|
|
184
|
+
irb(main):003:0> end
|
|
185
|
+
=>
|
|
186
|
+
#<ConcurrentRails::Promises:0x000000013e5fc870
|
|
187
|
+
...
|
|
188
|
+
irb(main):004:0> result.class
|
|
189
|
+
=> ConcurrentRails::Promises # <-- Still a `ConcurrentRails::Promises` class
|
|
190
|
+
irb(main):005:0> result.executor
|
|
191
|
+
=> :immediate # <-- default executor (:io) gets replaced
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
`fake!` strategy:
|
|
195
|
+
|
|
196
|
+
```ruby
|
|
197
|
+
irb(main):001:1* result = ConcurrentRails::Testing.fake! do
|
|
198
|
+
irb(main):002:1* ConcurrentRails::Promises.future { 42 }
|
|
199
|
+
irb(main):003:0> end
|
|
200
|
+
=> 42 # <-- yields the task but does not return a Promise
|
|
201
|
+
irb(main):004:0> result.class
|
|
202
|
+
=> Integer
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Further reading
|
|
206
|
+
|
|
135
207
|
For more information on how Futures work and how Rails handle multithread check these links:
|
|
136
208
|
|
|
137
209
|
[Future documentation](https://github.com/ruby-concurrency/concurrent-ruby/blob/master/docs-source/future.md)
|
|
@@ -143,19 +215,19 @@ For more information on how Futures work and how Rails handle multithread check
|
|
|
143
215
|
Add this line to your application's Gemfile:
|
|
144
216
|
|
|
145
217
|
```ruby
|
|
146
|
-
gem 'concurrent_rails', '~> 0.1
|
|
218
|
+
gem 'concurrent_rails', '~> 0.2.1'
|
|
147
219
|
```
|
|
148
220
|
|
|
149
221
|
And then execute:
|
|
150
222
|
|
|
151
223
|
```bash
|
|
152
|
-
|
|
224
|
+
bundle
|
|
153
225
|
```
|
|
154
226
|
|
|
155
227
|
Or install it yourself as:
|
|
156
228
|
|
|
157
229
|
```bash
|
|
158
|
-
|
|
230
|
+
gem install concurrent_rails
|
|
159
231
|
```
|
|
160
232
|
|
|
161
233
|
## Contributing
|
data/lib/concurrent_rails.rb
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'concurrent_rails/version'
|
|
4
|
-
require 'concurrent_rails/railtie'
|
|
5
3
|
require 'concurrent_rails/future'
|
|
6
4
|
require 'concurrent_rails/multi'
|
|
7
5
|
require 'concurrent_rails/promises'
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
require 'concurrent_rails/railtie'
|
|
7
|
+
require 'concurrent_rails/testing'
|
|
8
|
+
require 'concurrent_rails/version'
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ConcurrentRails::Adapters
|
|
4
|
+
module Delay
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
class_methods do
|
|
8
|
+
def delay(*args, &task)
|
|
9
|
+
delay_on(:io, *args, &task)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def delay_on(executor, *args, &task)
|
|
13
|
+
new(executor).delay_on_rails(*args, &task)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def delay_on_rails(*args, &task)
|
|
18
|
+
@instance = rails_wrapped { delay_on(executor, *args, &task) }
|
|
19
|
+
|
|
20
|
+
self
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ConcurrentRails::Adapters
|
|
4
|
+
module Future
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
class_methods do
|
|
8
|
+
def future(*args, &task)
|
|
9
|
+
future_on(:io, *args, &task)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def future_on(executor, *args, &task)
|
|
13
|
+
new(executor).future_on_rails(*args, &task)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def future_on_rails(*args, &task)
|
|
18
|
+
@instance = rails_wrapped { future_on(executor, *args, &task) }
|
|
19
|
+
|
|
20
|
+
self
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module ConcurrentRails
|
|
4
4
|
class Future
|
|
5
|
-
extend Forwardable
|
|
6
|
-
|
|
7
5
|
def initialize(executor: :io, &block)
|
|
8
6
|
@executor = executor
|
|
9
7
|
@future = run_on_rails(block)
|
|
8
|
+
ActiveSupport::Deprecation.warn('ConcurrentRails::Future is deprecated. See README for details')
|
|
10
9
|
end
|
|
11
10
|
|
|
12
11
|
def execute
|
|
@@ -17,30 +16,34 @@ module ConcurrentRails
|
|
|
17
16
|
|
|
18
17
|
%i[value value!].each do |method_name|
|
|
19
18
|
define_method method_name do
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
|
24
|
-
result = future.__send__(method_name)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
result
|
|
19
|
+
permit_concurrent_loads do
|
|
20
|
+
future.__send__(method_name)
|
|
28
21
|
end
|
|
29
22
|
end
|
|
30
23
|
end
|
|
31
24
|
|
|
32
|
-
|
|
25
|
+
delegate :state, :reason, :rejected?, :complete?, :add_observer, to: :future
|
|
33
26
|
|
|
34
27
|
private
|
|
35
28
|
|
|
36
29
|
def run_on_rails(block)
|
|
37
|
-
@future =
|
|
30
|
+
@future = rails_wrapped do
|
|
38
31
|
Concurrent::Future.new(executor: executor) do
|
|
39
|
-
|
|
32
|
+
rails_wrapped(&block)
|
|
40
33
|
end
|
|
41
34
|
end
|
|
42
35
|
end
|
|
43
36
|
|
|
37
|
+
def rails_wrapped(&block)
|
|
38
|
+
Rails.application.executor.wrap(&block)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def permit_concurrent_loads(&block)
|
|
42
|
+
rails_wrapped do
|
|
43
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads(&block)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
44
47
|
attr_reader :executor, :future
|
|
45
48
|
end
|
|
46
49
|
end
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
module ConcurrentRails
|
|
4
4
|
class Multi
|
|
5
5
|
def self.enqueue(*actions, executor: :io)
|
|
6
|
-
unless actions.all?
|
|
7
|
-
raise ArgumentError, '#enqueue accepts `Proc`s only'
|
|
8
|
-
end
|
|
6
|
+
raise ArgumentError, '#enqueue accepts `Proc`s only' unless actions.all?(Proc)
|
|
9
7
|
|
|
10
8
|
new(actions, executor).enqueue
|
|
11
9
|
end
|
|
@@ -30,6 +28,10 @@ module ConcurrentRails
|
|
|
30
28
|
futures.map(&:value)
|
|
31
29
|
end
|
|
32
30
|
|
|
31
|
+
def compute!
|
|
32
|
+
futures.map(&:value!)
|
|
33
|
+
end
|
|
34
|
+
|
|
33
35
|
def complete?
|
|
34
36
|
futures.all?(&:complete?)
|
|
35
37
|
end
|
|
@@ -1,48 +1,83 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'concurrent_rails/adapters/future'
|
|
4
|
+
require 'concurrent_rails/adapters/delay'
|
|
5
|
+
|
|
3
6
|
module ConcurrentRails
|
|
4
7
|
class Promises
|
|
5
8
|
include Concurrent::Promises::FactoryMethods
|
|
6
|
-
|
|
9
|
+
include ConcurrentRails::Adapters::Delay
|
|
10
|
+
include ConcurrentRails::Adapters::Future
|
|
11
|
+
|
|
12
|
+
def initialize(executor)
|
|
13
|
+
@executor = executor
|
|
14
|
+
end
|
|
7
15
|
|
|
8
|
-
|
|
9
|
-
|
|
16
|
+
%i[value value!].each do |method_name|
|
|
17
|
+
define_method(method_name) do |timeout = nil, timeout_value = nil|
|
|
18
|
+
permit_concurrent_loads do
|
|
19
|
+
instance.__send__(method_name, timeout, timeout_value)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
10
22
|
end
|
|
11
23
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
24
|
+
%i[then chain].each do |chainable|
|
|
25
|
+
define_method(chainable) do |*args, &task|
|
|
26
|
+
method = "#{chainable}_on"
|
|
27
|
+
@instance = rails_wrapped do
|
|
28
|
+
instance.__send__(method, executor, *args, &task)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
self
|
|
15
32
|
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def touch
|
|
36
|
+
@instance = rails_wrapped { instance.touch }
|
|
16
37
|
|
|
17
38
|
self
|
|
18
39
|
end
|
|
19
40
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
Rails.application.executor.wrap do
|
|
23
|
-
result = nil
|
|
41
|
+
def wait(timeout = nil)
|
|
42
|
+
result = permit_concurrent_loads { instance.__send__(:wait_until_resolved, timeout) }
|
|
24
43
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
end
|
|
44
|
+
timeout ? result : self
|
|
45
|
+
end
|
|
28
46
|
|
|
29
|
-
|
|
47
|
+
%i[on_fulfillment on_rejection on_resolution].each do |method|
|
|
48
|
+
define_method(method) do |*args, &callback_task|
|
|
49
|
+
rails_wrapped do
|
|
50
|
+
@instance = instance.__send__("#{method}_using", executor, *args, &callback_task)
|
|
30
51
|
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
52
|
|
|
34
|
-
|
|
35
|
-
@future_instance = Rails.application.executor.wrap do
|
|
36
|
-
future_on(default_executor, *args, &task)
|
|
53
|
+
self
|
|
37
54
|
end
|
|
38
55
|
|
|
39
|
-
|
|
56
|
+
define_method("#{method}!") do |*args, &callback_task|
|
|
57
|
+
rails_wrapped do
|
|
58
|
+
@instance = instance.__send__(:add_callback, "callback_#{method}", args, callback_task)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
self
|
|
62
|
+
end
|
|
40
63
|
end
|
|
41
64
|
|
|
42
|
-
|
|
65
|
+
delegate :state, :reason, :rejected?, :resolved?, :fulfilled?, to: :instance
|
|
66
|
+
|
|
67
|
+
attr_reader :executor
|
|
43
68
|
|
|
44
69
|
private
|
|
45
70
|
|
|
46
|
-
|
|
71
|
+
def rails_wrapped(&block)
|
|
72
|
+
Rails.application.executor.wrap(&block)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def permit_concurrent_loads(&block)
|
|
76
|
+
rails_wrapped do
|
|
77
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads(&block)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
attr_reader :instance
|
|
47
82
|
end
|
|
48
83
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ConcurrentRails
|
|
4
|
+
class Testing
|
|
5
|
+
class << self
|
|
6
|
+
attr_reader :execution_mode
|
|
7
|
+
|
|
8
|
+
%i[immediate fake].each do |exec_method|
|
|
9
|
+
define_method("#{exec_method}!") do |&task|
|
|
10
|
+
@execution_mode = exec_method
|
|
11
|
+
result = task.call
|
|
12
|
+
@execution_mode = :real
|
|
13
|
+
|
|
14
|
+
result
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
define_method("#{exec_method}?") do
|
|
18
|
+
execution_mode == exec_method
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
module TestingFuture
|
|
23
|
+
def future(*args, &task)
|
|
24
|
+
if ConcurrentRails::Testing.immediate?
|
|
25
|
+
future_on(:immediate, *args, &task)
|
|
26
|
+
elsif ConcurrentRails::Testing.fake?
|
|
27
|
+
yield
|
|
28
|
+
else
|
|
29
|
+
super
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
ConcurrentRails::Promises.extend(TestingFuture)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: concurrent_rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Luiz Eduardo Kowalski
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-
|
|
11
|
+
date: 2021-06-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -38,6 +38,20 @@ dependencies:
|
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '1.12'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rubocop-minitest
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0.12'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0.12'
|
|
41
55
|
- !ruby/object:Gem::Dependency
|
|
42
56
|
name: rubocop-performance
|
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -52,6 +66,20 @@ dependencies:
|
|
|
52
66
|
- - ">="
|
|
53
67
|
- !ruby/object:Gem::Version
|
|
54
68
|
version: '1.10'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: sqlite3
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '1.4'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '1.4'
|
|
55
83
|
description: Small library to make concurrent-ruby and Rails play nice together
|
|
56
84
|
email:
|
|
57
85
|
- luizeduardokowalski@gmail.com
|
|
@@ -63,12 +91,14 @@ files:
|
|
|
63
91
|
- README.md
|
|
64
92
|
- Rakefile
|
|
65
93
|
- lib/concurrent_rails.rb
|
|
94
|
+
- lib/concurrent_rails/adapters/delay.rb
|
|
95
|
+
- lib/concurrent_rails/adapters/future.rb
|
|
66
96
|
- lib/concurrent_rails/future.rb
|
|
67
97
|
- lib/concurrent_rails/multi.rb
|
|
68
98
|
- lib/concurrent_rails/promises.rb
|
|
69
99
|
- lib/concurrent_rails/railtie.rb
|
|
100
|
+
- lib/concurrent_rails/testing.rb
|
|
70
101
|
- lib/concurrent_rails/version.rb
|
|
71
|
-
- lib/tasks/concurrent_rails_tasks.rake
|
|
72
102
|
homepage: https://github.com/luizkowalski/concurrent_rails
|
|
73
103
|
licenses:
|
|
74
104
|
- MIT
|
|
@@ -91,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
91
121
|
- !ruby/object:Gem::Version
|
|
92
122
|
version: '0'
|
|
93
123
|
requirements: []
|
|
94
|
-
rubygems_version: 3.
|
|
124
|
+
rubygems_version: 3.2.20
|
|
95
125
|
signing_key:
|
|
96
126
|
specification_version: 4
|
|
97
127
|
summary: Multithread is hard
|