concurrent_rails 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ff1bb57ccfc21ffd6e539661e4f939497bdd46329bb77cbe580a9ec0d6374267
4
- data.tar.gz: b1c66b86282b56e676d751d846584fbf3047c9532c4380fb2b19b69f8f3cb009
3
+ metadata.gz: 5667dde5992af86d3d798a7a2f87b9ec6f84ebaa3eedcd4f37fa76ba5caa460e
4
+ data.tar.gz: 2f8d9f11f5fb55acbb4f9a6277312f754cc9798d3cb3c48c52873d15d6c21416
5
5
  SHA512:
6
- metadata.gz: da0805e477d67b12438149cc97a331ea3c770198a22529cb7d7d298216e1cb7a216f328b49405bf646c7326a76b29beb722edf088ed0a62fad1a7b3d3f8ca791
7
- data.tar.gz: 7c99edc66b92b084375b4b2be896a4a19c6b7e7d04b1a501f92ce7839a8234a08c9822166e5355be3a6d758eee1ffb0c147cd54d511bb337810773cf10b89ea5
6
+ metadata.gz: efe856cba6102e157f0286451ddfa618e9a56195456f16d4dc090a16aefb0846a5c227793fdc92686021b243a2603ccbb27833edbeb15d94ea974cbcf54ac37e
7
+ data.tar.gz: 595c6b3011bd8094407efcc1746d8f0c949a2fb4b37d4678923a84b8ef7d0a2f1ca1eb7a4bc61165cbdd7f9256802932a4711635552cfd60978a353b80fa8cfd
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ConcurrentRails
2
2
 
3
- ![status](https://github.com/luizkowalski/concurrent_rails/actions/workflows/ruby.yml/badge.svg?branch=master)
3
+ ![status](https://github.com/luizkowalski/concurrent_rails/actions/workflows/ruby.yml/badge.svg?branch=main)
4
4
 
5
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
 
@@ -8,7 +8,7 @@ The goal of this gem is to provide a simple library that allows the developer to
8
8
 
9
9
  ## Usage
10
10
 
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`
11
+ This library provides three classes that will help you run tasks in parallel: `ConcurrentRails::Promises`, `ConcurrentRails::Future` ([in the process of being deprecated by concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby#deprecated)) and `ConcurrentRails::Multi`
12
12
 
13
13
  ### Promises
14
14
 
@@ -174,16 +174,18 @@ 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
+
177
178
  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
179
  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
 
180
181
  `immediate` strategy:
182
+
181
183
  ```ruby
182
184
  irb(main):001:1* result = ConcurrentRails::Testing.immediate do
183
185
  irb(main):002:1* ConcurrentRails::Promises.future { 42 }
184
186
  irb(main):003:0> end
185
187
  =>
186
- #<ConcurrentRails::Promises:0x000000013e5fc870
188
+ #<ConcurrentRails::Promises:0x000000013e5fc870
187
189
  ...
188
190
  irb(main):004:0> result.class
189
191
  => ConcurrentRails::Promises # <-- Still a `ConcurrentRails::Promises` class
@@ -217,7 +219,7 @@ For more information on how Futures work and how Rails handle multithread check
217
219
  Add this line to your application's Gemfile:
218
220
 
219
221
  ```ruby
220
- gem 'concurrent_rails', '~> 0.2.1'
222
+ gem 'concurrent_rails', '~> 0.5.1'
221
223
  ```
222
224
 
223
225
  And then execute:
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "concurrent_rails/future_adapter"
4
- require "concurrent_rails/delay_adapter"
5
-
6
3
  module ConcurrentRails
7
4
  class Promises
8
5
  include Concurrent::Promises::FactoryMethods
@@ -47,13 +44,13 @@ module ConcurrentRails
47
44
  %i[on_fulfillment on_rejection on_resolution].each do |method|
48
45
  define_method(method) do |*args, &callback_task|
49
46
  rails_wrapped do
50
- @instance = instance.__send__("#{method}_using", executor, *args, &callback_task)
47
+ @instance = instance.__send__(:"#{method}_using", executor, *args, &callback_task)
51
48
  end
52
49
 
53
50
  self
54
51
  end
55
52
 
56
- define_method("#{method}!") do |*args, &callback_task|
53
+ define_method(:"#{method}!") do |*args, &callback_task|
57
54
  rails_wrapped do
58
55
  @instance = instance.__send__(:add_callback, "callback_#{method}", args, callback_task)
59
56
  end
@@ -68,8 +65,8 @@ module ConcurrentRails
68
65
 
69
66
  private
70
67
 
71
- def rails_wrapped(&block)
72
- Rails.application.executor.wrap(&block)
68
+ def rails_wrapped(&)
69
+ Rails.application.executor.wrap(&)
73
70
  end
74
71
 
75
72
  def permit_concurrent_loads(&block)
@@ -14,20 +14,20 @@ module ConcurrentRails
14
14
  result
15
15
  end
16
16
 
17
- define_method("#{test_mode}!") do
17
+ define_method(:"#{test_mode}!") do
18
18
  @execution_mode = test_mode
19
19
  end
20
20
 
21
- define_method("#{test_mode}?") do
21
+ define_method(:"#{test_mode}?") do
22
22
  execution_mode == test_mode
23
23
  end
24
24
  end
25
25
  end
26
26
 
27
27
  module TestingFuture
28
- def future(*args, &task)
28
+ def future(*args, &)
29
29
  if ConcurrentRails::Testing.immediate?
30
- future_on(:immediate, *args, &task)
30
+ future_on(:immediate, *args, &)
31
31
  elsif ConcurrentRails::Testing.fake?
32
32
  yield
33
33
  else
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ConcurrentRails
4
- VERSION = "0.5.1"
4
+ VERSION = "0.6.0"
5
5
  end
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "concurrent_rails/future"
4
- require "concurrent_rails/multi"
5
- require "concurrent_rails/promises"
6
- require "concurrent_rails/railtie"
7
- require "concurrent_rails/testing"
8
- require "concurrent_rails/version"
3
+ require "zeitwerk"
4
+ loader = Zeitwerk::Loader.for_gem
5
+ loader.setup
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.5.1
4
+ version: 0.6.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: 2023-08-05 00:00:00.000000000 Z
11
+ date: 2024-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -16,14 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '6.0'
19
+ version: '6.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '6.0'
26
+ version: '6.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: zeitwerk
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  description: Small library to make concurrent-ruby and Rails play nice together
28
42
  email:
29
43
  - luizeduardokowalski@gmail.com
@@ -36,9 +50,7 @@ files:
36
50
  - Rakefile
37
51
  - lib/concurrent_rails.rb
38
52
  - lib/concurrent_rails/delay_adapter.rb
39
- - lib/concurrent_rails/future.rb
40
53
  - lib/concurrent_rails/future_adapter.rb
41
- - lib/concurrent_rails/multi.rb
42
54
  - lib/concurrent_rails/promises.rb
43
55
  - lib/concurrent_rails/railtie.rb
44
56
  - lib/concurrent_rails/testing.rb
@@ -56,14 +68,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
56
68
  requirements:
57
69
  - - ">="
58
70
  - !ruby/object:Gem::Version
59
- version: '3.0'
71
+ version: '3.1'
60
72
  required_rubygems_version: !ruby/object:Gem::Requirement
61
73
  requirements:
62
74
  - - ">="
63
75
  - !ruby/object:Gem::Version
64
76
  version: '0'
65
77
  requirements: []
66
- rubygems_version: 3.4.18
78
+ rubygems_version: 3.5.17
67
79
  signing_key:
68
80
  specification_version: 4
69
81
  summary: Multithread is hard
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ConcurrentRails
4
- class Future
5
- def initialize(executor: :io, &block)
6
- @executor = executor
7
- @future = run_on_rails(block)
8
- ActiveSupport::Deprecation.warn("ConcurrentRails::Future is deprecated. See README for details")
9
- end
10
-
11
- def execute
12
- future.execute
13
-
14
- self
15
- end
16
-
17
- %i[value value!].each do |method_name|
18
- define_method method_name do
19
- permit_concurrent_loads do
20
- future.__send__(method_name)
21
- end
22
- end
23
- end
24
-
25
- delegate :state, :reason, :rejected?, :complete?, :add_observer, to: :future
26
-
27
- private
28
-
29
- def run_on_rails(block)
30
- @future = rails_wrapped do
31
- Concurrent::Future.new(executor: executor) do
32
- rails_wrapped(&block)
33
- end
34
- end
35
- end
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
-
47
- attr_reader :executor, :future
48
- end
49
- end
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ConcurrentRails
4
- class Multi
5
- def self.enqueue(*actions, executor: :io)
6
- raise ArgumentError, "#enqueue accepts `Proc`s only" unless actions.all?(Proc)
7
-
8
- new(actions, executor).enqueue
9
- end
10
-
11
- def initialize(actions, executor)
12
- @actions = actions
13
- @executor = executor
14
- @exceptions = Concurrent::Array.new
15
- end
16
-
17
- def enqueue
18
- @futures = actions.map do |action|
19
- f = ConcurrentRails::Future.new(executor: executor, &action)
20
- f.add_observer(self)
21
- f.execute
22
- end
23
-
24
- self
25
- end
26
-
27
- def compute
28
- futures.map(&:value)
29
- end
30
-
31
- def compute!
32
- futures.map(&:value!)
33
- end
34
-
35
- def complete?
36
- futures.all?(&:complete?)
37
- end
38
-
39
- def errors
40
- @exceptions
41
- end
42
-
43
- private
44
-
45
- def update(_time, _value, reason)
46
- @exceptions << reason if reason
47
- end
48
-
49
- attr_reader :actions, :futures, :exceptions, :executor
50
- end
51
- end