concurrent_rails 0.1.7 → 0.3.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: fe2277deed1f15f3c47a332667db0b85bbfb0d39d58bcb6539fa0d15d843c55d
4
- data.tar.gz: 0e347f69da65b60fcbdb63a04cdcb3bac782ba01b6e543db93428ff4c2bdbdc0
3
+ metadata.gz: fd75cf15aa7d7d0354bea478936bde3967d20a8e38daabff9e3fddf5c11bd5b6
4
+ data.tar.gz: 8c5faee065ed6de8bb7fedf2b29c3bc371792f579c7a00e6195d4b4e18ee0a29
5
5
  SHA512:
6
- metadata.gz: '079c165494e1eea8ae9c98fe89031c87408808527a3f90eda3c5c5d87598bfb5932b0285acaa99d4e5e714a1012af3676bc16fc9b5929268ff26640b90f4b191'
7
- data.tar.gz: 97944d3f3db134d08eb894f6f3211a705cb080ec4d4ec331657c94940fd08a08c4f1f46859d430f3c2e5097a1cc54d7de322e9f39a0fcc48de78d49982cd26e8
6
+ metadata.gz: 649cf84ed43e2b6c7e70f89840a3091fefea3ade9df62af7d50ee7e7ef309af24729912bd9dbc5adc4112da953d3964e8b0ac4d41b36d3eb256edd73813c979f
7
+ data.tar.gz: 818ffd9287980e87473f9445b0424a5a006271b3ce4a070a4bd4b6fb08c4405576eff466e4def1c5ddce032614600f3d9a830951d20cfbb29823b7667545780a
data/README.md CHANGED
@@ -2,8 +2,7 @@
2
2
 
3
3
  ![status](https://github.com/luizkowalski/concurrent_rails/actions/workflows/ruby.yml/badge.svg?branch=master)
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
 
@@ -13,8 +12,7 @@ This library provides three classes that will help you run tasks in parallel: `C
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
- ### Future
41
+ ### Delayed futures
44
42
 
45
- `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:
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,39 @@ 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 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
+
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
+ You can also set the stragegy globally using `ConcurrentRails::Testing.fake!` or `ConcurrentRails::Testing.immediate!`
206
+
207
+ ## Further reading
208
+
135
209
  For more information on how Futures work and how Rails handle multithread check these links:
136
210
 
137
211
  [Future documentation](https://github.com/ruby-concurrency/concurrent-ruby/blob/master/docs-source/future.md)
@@ -143,19 +217,19 @@ For more information on how Futures work and how Rails handle multithread check
143
217
  Add this line to your application's Gemfile:
144
218
 
145
219
  ```ruby
146
- gem 'concurrent_rails', '~> 0.1.7'
220
+ gem 'concurrent_rails', '~> 0.2.1'
147
221
  ```
148
222
 
149
223
  And then execute:
150
224
 
151
225
  ```bash
152
- $ bundle
226
+ bundle
153
227
  ```
154
228
 
155
229
  Or install it yourself as:
156
230
 
157
231
  ```bash
158
- $ gem install concurrent_rails
232
+ gem install concurrent_rails
159
233
  ```
160
234
 
161
235
  ## Contributing
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConcurrentRails::DelayAdapter
4
+ extend ActiveSupport::Concern
5
+
6
+ class_methods do
7
+ def delay(*args, &task)
8
+ delay_on(:io, *args, &task)
9
+ end
10
+
11
+ def delay_on(executor, *args, &task)
12
+ new(executor).delay_on_rails(*args, &task)
13
+ end
14
+ end
15
+
16
+ def delay_on_rails(*args, &task)
17
+ @instance = rails_wrapped { delay_on(executor, *args, &task) }
18
+
19
+ self
20
+ end
21
+ end
@@ -1,42 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ConcurrentRails
4
- class Future
5
- extend Forwardable
6
-
7
- def initialize(executor: :fast, &block)
8
- @executor = executor
9
- @future = run_on_rails(block)
10
- end
3
+ class ConcurrentRails::Future
4
+ def initialize(executor: :io, &block)
5
+ @executor = executor
6
+ @future = run_on_rails(block)
7
+ ActiveSupport::Deprecation.warn('ConcurrentRails::Future is deprecated. See README for details')
8
+ end
11
9
 
12
- def execute
13
- future.execute
10
+ def execute
11
+ future.execute
14
12
 
15
- self
16
- end
13
+ self
14
+ end
17
15
 
18
- %i[value value!].each do |method_name|
19
- define_method method_name do
20
- Rails.application.executor.wrap do
21
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
22
- future.__send__(method_name)
23
- end
24
- end
16
+ %i[value value!].each do |method_name|
17
+ define_method method_name do
18
+ permit_concurrent_loads do
19
+ future.__send__(method_name)
25
20
  end
26
21
  end
22
+ end
27
23
 
28
- def_delegators :@future, :state, :reason, :rejected?, :complete?, :add_observer
24
+ delegate :state, :reason, :rejected?, :complete?, :add_observer, to: :future
29
25
 
30
- private
26
+ private
31
27
 
32
- def run_on_rails(block)
33
- @future = Rails.application.executor.wrap do
34
- Concurrent::Future.new(executor: executor) do
35
- Rails.application.executor.wrap(&block)
36
- end
28
+ def run_on_rails(block)
29
+ @future = rails_wrapped do
30
+ Concurrent::Future.new(executor: executor) do
31
+ rails_wrapped(&block)
37
32
  end
38
33
  end
34
+ end
39
35
 
40
- attr_reader :executor, :future
36
+ def rails_wrapped(&block)
37
+ Rails.application.executor.wrap(&block)
41
38
  end
39
+
40
+ def permit_concurrent_loads(&block)
41
+ rails_wrapped do
42
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads(&block)
43
+ end
44
+ end
45
+
46
+ attr_reader :executor, :future
42
47
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConcurrentRails::FutureAdapter
4
+ extend ActiveSupport::Concern
5
+
6
+ class_methods do
7
+ def future(*args, &task)
8
+ future_on(:io, *args, &task)
9
+ end
10
+
11
+ def future_on(executor, *args, &task)
12
+ new(executor).future_on_rails(*args, &task)
13
+ end
14
+ end
15
+
16
+ def future_on_rails(*args, &task)
17
+ @instance = rails_wrapped { future_on(executor, *args, &task) }
18
+
19
+ self
20
+ end
21
+ end
@@ -1,53 +1,49 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ConcurrentRails
4
- class Multi
5
- def self.enqueue(*actions, executor: :io)
6
- unless actions.all? { |action| action.is_a?(Proc) }
7
- raise ArgumentError, '#enqueue accepts `Proc`s only'
8
- end
9
-
10
- new(actions, executor).enqueue
11
- end
3
+ class ConcurrentRails::Multi
4
+ def self.enqueue(*actions, executor: :io)
5
+ raise ArgumentError, '#enqueue accepts `Proc`s only' unless actions.all?(Proc)
12
6
 
13
- def initialize(actions, executor)
14
- @actions = actions
15
- @executor = executor
16
- @exceptions = Concurrent::Array.new
17
- end
7
+ new(actions, executor).enqueue
8
+ end
18
9
 
19
- def enqueue
20
- @futures = actions.map do |action|
21
- f = ConcurrentRails::Future.new(executor: executor, &action)
22
- f.add_observer(self)
23
- f.execute
24
- end
10
+ def initialize(actions, executor)
11
+ @actions = actions
12
+ @executor = executor
13
+ @exceptions = Concurrent::Array.new
14
+ end
25
15
 
26
- self
16
+ def enqueue
17
+ @futures = actions.map do |action|
18
+ f = ConcurrentRails::Future.new(executor: executor, &action)
19
+ f.add_observer(self)
20
+ f.execute
27
21
  end
28
22
 
29
- def compute
30
- futures.map(&:value)
31
- end
23
+ self
24
+ end
32
25
 
33
- def compute!
34
- futures.map(&:value!)
35
- end
26
+ def compute
27
+ futures.map(&:value)
28
+ end
36
29
 
37
- def complete?
38
- futures.all?(&:complete?)
39
- end
30
+ def compute!
31
+ futures.map(&:value!)
32
+ end
40
33
 
41
- def errors
42
- @exceptions
43
- end
34
+ def complete?
35
+ futures.all?(&:complete?)
36
+ end
44
37
 
45
- private
38
+ def errors
39
+ @exceptions
40
+ end
46
41
 
47
- def update(_time, _value, reason)
48
- @exceptions << reason if reason
49
- end
42
+ private
50
43
 
51
- attr_reader :actions, :futures, :exceptions, :executor
44
+ def update(_time, _value, reason)
45
+ @exceptions << reason if reason
52
46
  end
47
+
48
+ attr_reader :actions, :futures, :exceptions, :executor
53
49
  end
@@ -1,58 +1,81 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ConcurrentRails
4
- class Promises
5
- extend Forwardable
6
- include Concurrent::Promises::FactoryMethods
7
-
8
- class << self
9
- def future(*args, &task)
10
- future_on(:fast, *args, &task)
11
- end
3
+ require 'concurrent_rails/future_adapter'
4
+ require 'concurrent_rails/delay_adapter'
5
+
6
+ class ConcurrentRails::Promises
7
+ include Concurrent::Promises::FactoryMethods
8
+ include ConcurrentRails::DelayAdapter
9
+ include ConcurrentRails::FutureAdapter
10
+
11
+ def initialize(executor)
12
+ @executor = executor
13
+ end
12
14
 
13
- def future_on(executor, *args, &task)
14
- new.with_rails(executor, *args, &task)
15
+ %i[value value!].each do |method_name|
16
+ define_method(method_name) do |timeout = nil, timeout_value = nil|
17
+ permit_concurrent_loads do
18
+ instance.public_send(method_name, timeout, timeout_value)
15
19
  end
16
20
  end
21
+ end
17
22
 
18
- def then(*args, &task)
19
- @future_instance = Rails.application.executor.wrap do
20
- future_instance.then(*args, &task)
23
+ %i[then chain].each do |chainable|
24
+ define_method(chainable) do |*args, &task|
25
+ method = "#{chainable}_on"
26
+ @instance = rails_wrapped do
27
+ instance.public_send(method, executor, *args, &task)
21
28
  end
22
29
 
23
30
  self
24
31
  end
32
+ end
25
33
 
26
- def chain(*args, &task)
27
- Rails.application.executor.wrap do
28
- future_instance.chain(*args, &task)
29
- end
34
+ def touch
35
+ @instance = rails_wrapped { instance.touch }
30
36
 
31
- self
32
- end
37
+ self
38
+ end
39
+
40
+ def wait(timeout = nil)
41
+ result = permit_concurrent_loads { instance.__send__(:wait_until_resolved, timeout) }
33
42
 
34
- %i[value value!].each do |method_name|
35
- define_method method_name do |timeout = nil, timeout_value = nil|
36
- Rails.application.executor.wrap do
37
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
38
- future_instance.__send__(method_name, timeout, timeout_value)
39
- end
40
- end
43
+ timeout ? result : self
44
+ end
45
+
46
+ %i[on_fulfillment on_rejection on_resolution].each do |method|
47
+ define_method(method) do |*args, &callback_task|
48
+ rails_wrapped do
49
+ @instance = instance.__send__("#{method}_using", executor, *args, &callback_task)
41
50
  end
51
+
52
+ self
42
53
  end
43
54
 
44
- def with_rails(executor, *args, &task)
45
- @future_instance = Rails.application.executor.wrap do
46
- future_on(executor, *args, &task)
55
+ define_method("#{method}!") do |*args, &callback_task|
56
+ rails_wrapped do
57
+ @instance = instance.__send__(:add_callback, "callback_#{method}", args, callback_task)
47
58
  end
48
59
 
49
60
  self
50
61
  end
62
+ end
51
63
 
52
- def_delegators :@future_instance, :state, :reason, :rejected?, :resolved?
64
+ delegate :state, :reason, :rejected?, :resolved?, :fulfilled?, to: :instance
53
65
 
54
- private
66
+ attr_reader :executor
55
67
 
56
- attr_reader :future_instance
68
+ private
69
+
70
+ def rails_wrapped(&block)
71
+ Rails.application.executor.wrap(&block)
57
72
  end
73
+
74
+ def permit_concurrent_loads(&block)
75
+ rails_wrapped do
76
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads(&block)
77
+ end
78
+ end
79
+
80
+ attr_reader :instance
58
81
  end
@@ -1,6 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ConcurrentRails
4
- class Railtie < ::Rails::Railtie
5
- end
3
+ class ConcurrentRails::Railtie < ::Rails::Railtie
6
4
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ConcurrentRails::Testing
4
+ class << self
5
+ attr_reader :execution_mode
6
+
7
+ %w[immediate fake real].each do |test_mode|
8
+ define_method(test_mode) do |&task|
9
+ @execution_mode = test_mode
10
+ result = task.call
11
+ @execution_mode = :real
12
+
13
+ result
14
+ end
15
+
16
+ define_method("#{test_mode}!") do
17
+ @execution_mode = test_mode
18
+ end
19
+
20
+ define_method("#{test_mode}?") do
21
+ execution_mode == test_mode
22
+ end
23
+ end
24
+ end
25
+
26
+ module TestingFuture
27
+ def future(*args, &task)
28
+ if ConcurrentRails::Testing.immediate?
29
+ future_on(:immediate, *args, &task)
30
+ elsif ConcurrentRails::Testing.fake?
31
+ yield
32
+ else
33
+ super
34
+ end
35
+ end
36
+ end
37
+
38
+ ConcurrentRails::Promises.extend(TestingFuture)
39
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ConcurrentRails
4
- VERSION = '0.1.7'
4
+ VERSION = '0.3.0'
5
5
  end
@@ -4,7 +4,5 @@ require 'concurrent_rails/future'
4
4
  require 'concurrent_rails/multi'
5
5
  require 'concurrent_rails/promises'
6
6
  require 'concurrent_rails/railtie'
7
+ require 'concurrent_rails/testing'
7
8
  require 'concurrent_rails/version'
8
-
9
- module ConcurrentRails
10
- 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.7
4
+ version: 0.3.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-04 00:00:00.000000000 Z
11
+ date: 2021-11-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -24,6 +24,20 @@ 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.4'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.4'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rubocop
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +52,20 @@ dependencies:
38
52
  - - ">="
39
53
  - !ruby/object:Gem::Version
40
54
  version: '1.12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop-minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0.12'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0.12'
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: rubocop-performance
43
71
  requirement: !ruby/object:Gem::Requirement
@@ -63,12 +91,14 @@ files:
63
91
  - README.md
64
92
  - Rakefile
65
93
  - lib/concurrent_rails.rb
94
+ - lib/concurrent_rails/delay_adapter.rb
66
95
  - lib/concurrent_rails/future.rb
96
+ - lib/concurrent_rails/future_adapter.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.1.6
124
+ rubygems_version: 3.2.31
95
125
  signing_key:
96
126
  specification_version: 4
97
127
  summary: Multithread is hard
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
- # desc "Explaining what the task does"
3
- # task :concurrent_rails do
4
- # # Task goes here
5
- # end