concurrent_rails 0.1.8 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 257bcdcbd99076d7b73c193297ce5bc410c1fd5962bb823b965b5abbdf21197c
4
- data.tar.gz: 5ae2230ba4416193fac70bcd9500673e7d96081491786717eb8d6ce95c938f92
3
+ metadata.gz: 14fd5aa53c8cb75c74999acd7e1c43410a9564ba4122d83142f0e3ed8565d429
4
+ data.tar.gz: 71e60b4e355052fcc328ada6b881b9e41330360aaaa582eff9afb90cab19866f
5
5
  SHA512:
6
- metadata.gz: df4405c6f8e8efe155dea24db8a3799955b59c4d3a93118153b557711941c66428027caea64d245df4437be49d3d0482ffb2461bad863b38877e152f57eb6970
7
- data.tar.gz: 8c11ffcd0afbeedf533a8da19ca19f79a1e2566e326946f55e699337f45c179a2f245f5b4fd46f62b694a9803ac317cf0bfb6dec95c38285cf3487c5631a4457
6
+ metadata.gz: 020b3dee7781090195212145f6faf7f19c311cdee6b619e0601dde476fd3dd2dbe811269da5362794d5903f56ca95eaf56e615dcadb55db454d2a705b6599837
7
+ data.tar.gz: 9abb0931cb4283b4c9947d7498784c3d0ca0c32e70fb0fbaff9aa29ad28cde0c97ba48bde5f2d07837902cfcb912f987f449b9825658f47f93a7ef1f48bacf32
data/README.md CHANGED
@@ -13,8 +13,7 @@ This library provides three classes that will help you run tasks in parallel: `C
13
13
 
14
14
  ### Promises
15
15
 
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:
16
+ `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
17
 
19
18
  ```ruby
20
19
  irb(main):001:0> future = ConcurrentRails::Promises.future(5) { |v| sleep(v); 42 }
@@ -40,7 +39,50 @@ irb(main):002:0> future.value
40
39
  => 84
41
40
  ```
42
41
 
43
- ### Future
42
+ ### Delayed futures
43
+
44
+ Delayed future is a future that is enqueued but not run until `#touch` or any other method that requires a resolution is called.
45
+
46
+ ```ruby
47
+ irb(main):002:0> delay = ConcurrentRails::Promises.delay { 42 }
48
+ => #<ConcurrentRails::Promises:0x00007f8b55333d48 @executor=:io, @instan...
49
+
50
+ irb(main):003:0> delay.state
51
+ => :pending
52
+
53
+ irb(main):004:0> delay.touch
54
+ => #<Concurrent::Promises::Future:0x00007f8b553325b0 pending>
55
+
56
+ irb(main):005:0> delay.state
57
+ => :fulfilled
58
+
59
+ irb(main):006:0> delay.value
60
+ => 42
61
+ ```
62
+
63
+ 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.
64
+
65
+ ### Callbacks
66
+
67
+ Delayed and regular futures can set a callback to be executed after the resolution of the future. There are three different callbacks:
68
+
69
+ * `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.
70
+
71
+ * `on_fulfillment`: runs after the future is fulfilled and yields `value` to the callback
72
+
73
+ * `on_rejection`: runs after the future is rejected and yields the `error` to the callback
74
+
75
+ ```ruby
76
+ delay = ConcurrentRails::Promises.delay { complex_find_user_query }.
77
+ on_fulfillment { |user| user.update!(name: 'John Doe') }.
78
+ on_rejection { |reason| log_error(reason) }
79
+
80
+ delay.touch
81
+ ```
82
+
83
+ 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.
84
+
85
+ ### (Deprecated) Future
44
86
 
45
87
  `ConcurrentRails::Future` will execute your code in a separated 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
88
 
@@ -81,7 +123,7 @@ irb(main):005:0> future.reason
81
123
  => #<ZeroDivisionError: divided by 0>
82
124
  ```
83
125
 
84
- ### Multi
126
+ ### (Deprecated) Multi
85
127
 
86
128
  `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
129
 
@@ -143,7 +185,7 @@ For more information on how Futures work and how Rails handle multithread check
143
185
  Add this line to your application's Gemfile:
144
186
 
145
187
  ```ruby
146
- gem 'concurrent_rails', '~> 0.1.8'
188
+ gem 'concurrent_rails', '~> 0.2.0'
147
189
  ```
148
190
 
149
191
  And then execute:
@@ -5,6 +5,3 @@ require 'concurrent_rails/multi'
5
5
  require 'concurrent_rails/promises'
6
6
  require 'concurrent_rails/railtie'
7
7
  require 'concurrent_rails/version'
8
-
9
- module ConcurrentRails
10
- end
@@ -0,0 +1,25 @@
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
+
23
+ delegate :touch, to: :instance
24
+ end
25
+ 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
- def initialize(executor: :fast, &block)
5
+ def initialize(executor: :io, &block)
8
6
  @executor = executor
9
7
  @future = run_on_rails(block)
8
+ # ActiveSupport::Deprecation.warn('Concurrent::Future is deprecated')
10
9
  end
11
10
 
12
11
  def execute
@@ -25,7 +24,7 @@ module ConcurrentRails
25
24
  end
26
25
  end
27
26
 
28
- def_delegators :@future, :state, :reason, :rejected?, :complete?, :add_observer
27
+ delegate :state, :reason, :rejected?, :complete?, :add_observer, to: :future
29
28
 
30
29
  private
31
30
 
@@ -1,61 +1,70 @@
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
- extend Forwardable
6
8
  include Concurrent::Promises::FactoryMethods
7
-
8
- class << self
9
- def future(*args, &task)
10
- future_on(:fast, *args, &task)
11
- end
12
-
13
- def future_on(executor, *args, &task)
14
- new(executor).run_on_rails(*args, &task)
15
- end
16
- end
9
+ include ConcurrentRails::Adapters::Delay
10
+ include ConcurrentRails::Adapters::Future
17
11
 
18
12
  def initialize(executor)
19
13
  @executor = executor
20
14
  end
21
15
 
22
- def run_on_rails(*args, &task)
23
- @future_instance = rails_wrapped { future_on(executor, *args, &task) }
24
-
25
- self
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
26
22
  end
27
23
 
28
24
  %i[then chain].each do |chainable|
29
25
  define_method(chainable) do |*args, &task|
30
26
  method = "#{chainable}_on"
31
- @future_instance = rails_wrapped do
32
- future_instance.__send__(method, executor, *args, &task)
27
+ @instance = rails_wrapped do
28
+ instance.__send__(method, executor, *args, &task)
33
29
  end
34
30
 
35
31
  self
36
32
  end
37
33
  end
38
34
 
39
- %i[value value!].each do |method_name|
40
- define_method(method_name) do |timeout = nil, timeout_value = nil|
35
+ %i[on_fulfillment on_rejection on_resolution].each do |method|
36
+ define_method(method) do |*args, &callback_task|
41
37
  rails_wrapped do
42
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
43
- future_instance.__send__(method_name, timeout, timeout_value)
44
- end
38
+ @instance = instance.__send__("#{method}_using", executor, *args, &callback_task)
45
39
  end
40
+
41
+ self
46
42
  end
47
- end
48
43
 
49
- %i[state reason rejected? resolved? fulfilled?].each do |delegatable|
50
- def_delegator :@future_instance, delegatable
44
+ define_method("#{method}!") do |*args, &callback_task|
45
+ rails_wrapped do
46
+ @instance = instance.__send__(:add_callback, "callback_#{method}", args, callback_task)
47
+ end
48
+
49
+ self
50
+ end
51
51
  end
52
52
 
53
+ delegate :state, :reason, :rejected?, :resolved?, :fulfilled?, :wait,
54
+ to: :instance
55
+
53
56
  private
54
57
 
55
58
  def rails_wrapped(&block)
56
59
  Rails.application.executor.wrap(&block)
57
60
  end
58
61
 
59
- attr_reader :future_instance, :executor
62
+ def permit_concurrent_loads(&block)
63
+ rails_wrapped do
64
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads(&block)
65
+ end
66
+ end
67
+
68
+ attr_reader :executor, :instance
60
69
  end
61
70
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ConcurrentRails
4
- VERSION = '0.1.8'
4
+ VERSION = '0.2.0'
5
5
  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.8
4
+ version: 0.2.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: 2021-05-05 00:00:00.000000000 Z
11
+ date: 2021-05-13 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,6 +91,8 @@ 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
@@ -90,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
120
  - !ruby/object:Gem::Version
91
121
  version: '0'
92
122
  requirements: []
93
- rubygems_version: 3.1.6
123
+ rubygems_version: 3.2.17
94
124
  signing_key:
95
125
  specification_version: 4
96
126
  summary: Multithread is hard