concurrent_rails 0.2.1 → 0.4.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/README.md +9 -7
- data/lib/concurrent_rails/{adapters/delay.rb → delay_adapter.rb} +2 -2
- data/lib/concurrent_rails/{adapters/future.rb → future_adapter.rb} +2 -2
- data/lib/concurrent_rails/promises.rb +6 -6
- data/lib/concurrent_rails/testing.rb +21 -17
- data/lib/concurrent_rails/version.rb +1 -1
- metadata +32 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f546ccc88f5ebbe088d526b38964a490ccbd7ad2e71a540efd8967c0ff608170
|
4
|
+
data.tar.gz: 50cb65d1f7600f44aa900179614c143d5e402e296f80f926eda2233b9c3dc289
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b33613492e179d066fbc5882140693fee391862241b5f76d6cd18d3fafb9c35079c8ba9eefbca11ede757f491fca51c4433ae207dc584d9ac3f39ebe498eabe
|
7
|
+
data.tar.gz: '0329ed54f4e87a12910dc345993193feff12537ea8fecf9d57125966696cd314deb86fbe3774c306b7a60d23802e2e8e3ac98ef79e56e2a1062d70f406d4dbba'
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|

|
4
4
|
|
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
|
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 has 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.
|
6
6
|
|
7
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.
|
8
8
|
|
@@ -174,12 +174,12 @@ irb(main):007:0> multi.errors
|
|
174
174
|
It is worth mention that a failed proc will return `nil`.
|
175
175
|
|
176
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
|
178
|
-
Since none of these solutions were satisfactory to me, I created `ConcurrentRails::Testing` with two strategies: `immediate
|
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 your 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
179
|
|
180
|
-
`immediate
|
180
|
+
`immediate` strategy:
|
181
181
|
```ruby
|
182
|
-
irb(main):001:1* result = ConcurrentRails::Testing.immediate
|
182
|
+
irb(main):001:1* result = ConcurrentRails::Testing.immediate do
|
183
183
|
irb(main):002:1* ConcurrentRails::Promises.future { 42 }
|
184
184
|
irb(main):003:0> end
|
185
185
|
=>
|
@@ -191,10 +191,10 @@ irb(main):005:0> result.executor
|
|
191
191
|
=> :immediate # <-- default executor (:io) gets replaced
|
192
192
|
```
|
193
193
|
|
194
|
-
`fake
|
194
|
+
`fake` strategy:
|
195
195
|
|
196
196
|
```ruby
|
197
|
-
irb(main):001:1* result = ConcurrentRails::Testing.fake
|
197
|
+
irb(main):001:1* result = ConcurrentRails::Testing.fake do
|
198
198
|
irb(main):002:1* ConcurrentRails::Promises.future { 42 }
|
199
199
|
irb(main):003:0> end
|
200
200
|
=> 42 # <-- yields the task but does not return a Promise
|
@@ -202,6 +202,8 @@ irb(main):004:0> result.class
|
|
202
202
|
=> Integer
|
203
203
|
```
|
204
204
|
|
205
|
+
You can also set the stragegy globally using `ConcurrentRails::Testing.fake!` or `ConcurrentRails::Testing.immediate!`
|
206
|
+
|
205
207
|
## Further reading
|
206
208
|
|
207
209
|
For more information on how Futures work and how Rails handle multithread check these links:
|
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'concurrent_rails/
|
4
|
-
require 'concurrent_rails/
|
3
|
+
require 'concurrent_rails/future_adapter'
|
4
|
+
require 'concurrent_rails/delay_adapter'
|
5
5
|
|
6
6
|
module ConcurrentRails
|
7
7
|
class Promises
|
8
8
|
include Concurrent::Promises::FactoryMethods
|
9
|
-
include ConcurrentRails::
|
10
|
-
include ConcurrentRails::
|
9
|
+
include ConcurrentRails::DelayAdapter
|
10
|
+
include ConcurrentRails::FutureAdapter
|
11
11
|
|
12
12
|
def initialize(executor)
|
13
13
|
@executor = executor
|
@@ -16,7 +16,7 @@ module ConcurrentRails
|
|
16
16
|
%i[value value!].each do |method_name|
|
17
17
|
define_method(method_name) do |timeout = nil, timeout_value = nil|
|
18
18
|
permit_concurrent_loads do
|
19
|
-
instance.
|
19
|
+
instance.public_send(method_name, timeout, timeout_value)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -25,7 +25,7 @@ module ConcurrentRails
|
|
25
25
|
define_method(chainable) do |*args, &task|
|
26
26
|
method = "#{chainable}_on"
|
27
27
|
@instance = rails_wrapped do
|
28
|
-
instance.
|
28
|
+
instance.public_send(method, executor, *args, &task)
|
29
29
|
end
|
30
30
|
|
31
31
|
self
|
@@ -5,33 +5,37 @@ module ConcurrentRails
|
|
5
5
|
class << self
|
6
6
|
attr_reader :execution_mode
|
7
7
|
|
8
|
-
%
|
9
|
-
define_method(
|
10
|
-
@execution_mode =
|
11
|
-
result
|
8
|
+
%w[immediate fake real].each do |test_mode|
|
9
|
+
define_method(test_mode) do |&task|
|
10
|
+
@execution_mode = test_mode
|
11
|
+
result = task.call
|
12
12
|
@execution_mode = :real
|
13
13
|
|
14
14
|
result
|
15
15
|
end
|
16
16
|
|
17
|
-
define_method("#{
|
18
|
-
execution_mode
|
17
|
+
define_method("#{test_mode}!") do
|
18
|
+
@execution_mode = test_mode
|
19
19
|
end
|
20
|
-
end
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
if ConcurrentRails::Testing.immediate?
|
25
|
-
future_on(:immediate, *args, &task)
|
26
|
-
elsif ConcurrentRails::Testing.fake?
|
27
|
-
yield
|
28
|
-
else
|
29
|
-
super
|
30
|
-
end
|
21
|
+
define_method("#{test_mode}?") do
|
22
|
+
execution_mode == test_mode
|
31
23
|
end
|
32
24
|
end
|
25
|
+
end
|
33
26
|
|
34
|
-
|
27
|
+
module TestingFuture
|
28
|
+
def future(*args, &task)
|
29
|
+
if ConcurrentRails::Testing.immediate?
|
30
|
+
future_on(:immediate, *args, &task)
|
31
|
+
elsif ConcurrentRails::Testing.fake?
|
32
|
+
yield
|
33
|
+
else
|
34
|
+
super
|
35
|
+
end
|
36
|
+
end
|
35
37
|
end
|
38
|
+
|
39
|
+
ConcurrentRails::Promises.extend(TestingFuture)
|
36
40
|
end
|
37
41
|
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.
|
4
|
+
version: 0.4.0
|
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:
|
11
|
+
date: 2022-09-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -24,62 +24,76 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '5.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest-reporters
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.5'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.5'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rubocop
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
45
|
- - ">="
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
47
|
+
version: '1.33'
|
34
48
|
type: :development
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
52
|
- - ">="
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1.
|
54
|
+
version: '1.33'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rubocop-minitest
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - ">="
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: '0.
|
61
|
+
version: '0.21'
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - ">="
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0.
|
68
|
+
version: '0.21'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: rubocop-performance
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - ">="
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: '1.
|
75
|
+
version: '1.14'
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - ">="
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version: '1.
|
82
|
+
version: '1.14'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
84
|
+
name: ruby-lsp
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
|
-
- - "
|
87
|
+
- - ">="
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
89
|
+
version: '0.3'
|
76
90
|
type: :development
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
|
-
- - "
|
94
|
+
- - ">="
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
96
|
+
version: '0.3'
|
83
97
|
description: Small library to make concurrent-ruby and Rails play nice together
|
84
98
|
email:
|
85
99
|
- luizeduardokowalski@gmail.com
|
@@ -91,9 +105,9 @@ files:
|
|
91
105
|
- README.md
|
92
106
|
- Rakefile
|
93
107
|
- lib/concurrent_rails.rb
|
94
|
-
- lib/concurrent_rails/
|
95
|
-
- lib/concurrent_rails/adapters/future.rb
|
108
|
+
- lib/concurrent_rails/delay_adapter.rb
|
96
109
|
- lib/concurrent_rails/future.rb
|
110
|
+
- lib/concurrent_rails/future_adapter.rb
|
97
111
|
- lib/concurrent_rails/multi.rb
|
98
112
|
- lib/concurrent_rails/promises.rb
|
99
113
|
- lib/concurrent_rails/railtie.rb
|
@@ -103,9 +117,7 @@ homepage: https://github.com/luizkowalski/concurrent_rails
|
|
103
117
|
licenses:
|
104
118
|
- MIT
|
105
119
|
metadata:
|
106
|
-
|
107
|
-
source_code_uri: https://github.com/luizkowalski/concurrent_rails
|
108
|
-
changelog_uri: https://github.com/luizkowalski/concurrent_rails/blob/master/CHANGELOG.md
|
120
|
+
rubygems_mfa_required: 'true'
|
109
121
|
post_install_message:
|
110
122
|
rdoc_options: []
|
111
123
|
require_paths:
|
@@ -114,14 +126,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
114
126
|
requirements:
|
115
127
|
- - ">="
|
116
128
|
- !ruby/object:Gem::Version
|
117
|
-
version: '2.
|
129
|
+
version: '2.7'
|
118
130
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
131
|
requirements:
|
120
132
|
- - ">="
|
121
133
|
- !ruby/object:Gem::Version
|
122
134
|
version: '0'
|
123
135
|
requirements: []
|
124
|
-
rubygems_version: 3.
|
136
|
+
rubygems_version: 3.3.22
|
125
137
|
signing_key:
|
126
138
|
specification_version: 4
|
127
139
|
summary: Multithread is hard
|