standard-procedure-async 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b92c0e8440b6f2ad4b0c95cc83ac562ece4124490f15982f1d6531efec3ca2b1
4
+ data.tar.gz: ba0100a22ccbf8c572489111b841bdee403347e2f3a65172b8584594ebd026e5
5
+ SHA512:
6
+ metadata.gz: ec6d3846f51149189ae18dffbf38af10cbb9c317ac9b546b1e336b726219d040b7b0d5fab65dd4a33c1be079ad093703947236c46ce1345af0f33cc5474bb50d
7
+ data.tar.gz: fb99264092314bad0f9c4c1e7d1061e2530456bf59b409eadbbe6f5f4a216ad69d71a5abd2fd3a74c4d0c1de0d9fa4f50b7f2053259de9435bfd3e9f7eb34fc8
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.standard.yml ADDED
@@ -0,0 +1,3 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/testdouble/standard
3
+ ruby_version: 2.6
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2023-05-30
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in standard-procedure-async.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "rspec", "~> 3.0"
11
+
12
+ gem "standard", "~> 1.3"
data/Gemfile.lock ADDED
@@ -0,0 +1,134 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ standard-procedure-async (0.1.0)
5
+ concurrent-ruby (>= 1.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ actionpack (7.0.5)
11
+ actionview (= 7.0.5)
12
+ activesupport (= 7.0.5)
13
+ rack (~> 2.0, >= 2.2.4)
14
+ rack-test (>= 0.6.3)
15
+ rails-dom-testing (~> 2.0)
16
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
17
+ actionview (7.0.5)
18
+ activesupport (= 7.0.5)
19
+ builder (~> 3.1)
20
+ erubi (~> 1.4)
21
+ rails-dom-testing (~> 2.0)
22
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
23
+ activesupport (7.0.5)
24
+ concurrent-ruby (~> 1.0, >= 1.0.2)
25
+ i18n (>= 1.6, < 2)
26
+ minitest (>= 5.1)
27
+ tzinfo (~> 2.0)
28
+ ast (2.4.2)
29
+ builder (3.2.4)
30
+ concurrent-ruby (1.2.2)
31
+ concurrent_rails (0.4.1)
32
+ railties (>= 5.2)
33
+ crass (1.0.6)
34
+ diff-lcs (1.5.0)
35
+ erubi (1.12.0)
36
+ i18n (1.13.0)
37
+ concurrent-ruby (~> 1.0)
38
+ json (2.6.3)
39
+ language_server-protocol (3.17.0.3)
40
+ lint_roller (1.0.0)
41
+ loofah (2.21.3)
42
+ crass (~> 1.0.2)
43
+ nokogiri (>= 1.12.0)
44
+ method_source (1.0.0)
45
+ minitest (5.18.0)
46
+ nokogiri (1.15.2-arm64-darwin)
47
+ racc (~> 1.4)
48
+ nokogiri (1.15.2-x86_64-linux)
49
+ racc (~> 1.4)
50
+ parallel (1.23.0)
51
+ parser (3.2.2.1)
52
+ ast (~> 2.4.1)
53
+ racc (1.6.2)
54
+ rack (2.2.7)
55
+ rack-test (2.1.0)
56
+ rack (>= 1.3)
57
+ rails-dom-testing (2.0.3)
58
+ activesupport (>= 4.2.0)
59
+ nokogiri (>= 1.6)
60
+ rails-html-sanitizer (1.6.0)
61
+ loofah (~> 2.21)
62
+ nokogiri (~> 1.14)
63
+ railties (7.0.5)
64
+ actionpack (= 7.0.5)
65
+ activesupport (= 7.0.5)
66
+ method_source
67
+ rake (>= 12.2)
68
+ thor (~> 1.0)
69
+ zeitwerk (~> 2.5)
70
+ rainbow (3.1.1)
71
+ rake (13.0.6)
72
+ regexp_parser (2.8.0)
73
+ rexml (3.2.5)
74
+ rspec (3.12.0)
75
+ rspec-core (~> 3.12.0)
76
+ rspec-expectations (~> 3.12.0)
77
+ rspec-mocks (~> 3.12.0)
78
+ rspec-core (3.12.2)
79
+ rspec-support (~> 3.12.0)
80
+ rspec-expectations (3.12.3)
81
+ diff-lcs (>= 1.2.0, < 2.0)
82
+ rspec-support (~> 3.12.0)
83
+ rspec-mocks (3.12.5)
84
+ diff-lcs (>= 1.2.0, < 2.0)
85
+ rspec-support (~> 3.12.0)
86
+ rspec-support (3.12.0)
87
+ rubocop (1.50.2)
88
+ json (~> 2.3)
89
+ parallel (~> 1.10)
90
+ parser (>= 3.2.0.0)
91
+ rainbow (>= 2.2.2, < 4.0)
92
+ regexp_parser (>= 1.8, < 3.0)
93
+ rexml (>= 3.2.5, < 4.0)
94
+ rubocop-ast (>= 1.28.0, < 2.0)
95
+ ruby-progressbar (~> 1.7)
96
+ unicode-display_width (>= 2.4.0, < 3.0)
97
+ rubocop-ast (1.28.1)
98
+ parser (>= 3.2.1.0)
99
+ rubocop-performance (1.16.0)
100
+ rubocop (>= 1.7.0, < 2.0)
101
+ rubocop-ast (>= 0.4.0)
102
+ ruby-progressbar (1.13.0)
103
+ standard (1.28.2)
104
+ language_server-protocol (~> 3.17.0.2)
105
+ lint_roller (~> 1.0)
106
+ rubocop (~> 1.50.2)
107
+ standard-custom (~> 1.0.0)
108
+ standard-performance (~> 1.0.1)
109
+ standard-custom (1.0.0)
110
+ lint_roller (~> 1.0)
111
+ standard-performance (1.0.1)
112
+ lint_roller (~> 1.0)
113
+ rubocop-performance (~> 1.16.0)
114
+ thor (1.2.2)
115
+ tzinfo (2.0.6)
116
+ concurrent-ruby (~> 1.0)
117
+ unicode-display_width (2.4.2)
118
+ zeitwerk (2.6.8)
119
+
120
+ PLATFORMS
121
+ arm64-darwin-22
122
+ x86_64-linux
123
+
124
+ DEPENDENCIES
125
+ activesupport
126
+ concurrent_rails
127
+ railties
128
+ rake (~> 13.0)
129
+ rspec (~> 3.0)
130
+ standard (~> 1.3)
131
+ standard-procedure-async!
132
+
133
+ BUNDLED WITH
134
+ 2.3.7
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Rahoul Baruah
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # Standard::Procedure::Async
2
+
3
+ ## The Actor model
4
+
5
+ The [Actor Model](https://en.wikipedia.org/wiki/Actor_model) is widely regarded as one of the safest ways to write thread-safe code. Each "actor" maintains a single internal thread and as messages are received by the actor (via method calls), the thread responds to those messages sequentially. This means that no matter which thread sends a message to the actor, the actor's internal behaviour is always thread-safe.
6
+
7
+ The actual implementation does not use a thread-per-object as that could get very costly. Instead, each actor maintains a queue of incoming messages and then uses a [Future](https://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html) to actually perform those messages. The future uses a thread which is allocated from Concurrent Ruby's internal thread pool, freeing up resources when the system is quiet and increasing the number of workers when the system is busy.
8
+
9
+ ## Why does this gem exist?
10
+
11
+ Two reasons.
12
+
13
+ Concurrent-ruby has a simple implementation of this model, using the [Async](https://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Async.html) mixin. However, Concurrent::Async uses [IVar](https://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/IVar.html)s which are now deprecated.
14
+
15
+ In addition, while concurrent-ruby is an excellent library, it does not work well with Ruby on Rails. The [Rails Executor](https://guides.rubyonrails.org/threading_and_code_execution.html) ensures that any threads interact safely with the framework. But concurrent-ruby knows nothing about the executor.
16
+
17
+ Finally, using the actor model is infectious. If you mark a public method as asynchronous, in order to be safe, you have to mark them all as asynchronous. Concurrent::Async's syntax relies on the caller using `@my_object.async.my_method` which means that it is easy to forget and miss an asynchronous call. Which in turn will result in inconsistent behaviour and hard to trace bugs.
18
+
19
+ So [Standard::Procedure::Async::Actor](https://github.com/standard-procedure/async/blob/main/lib/standard/procedure/async/actor.rb) replaces the IVars with a mix of Futures and MVars. The MVar is used to transfer the return values of any methods back to the calling thread and the Future is used to do the work in a separate thread.
20
+
21
+ In addition, if you are in a Ruby on Rails project, Standard::Procedure::Async uses [Luiz Kowalski](https://github.com/luizkowalski)'s [concurrent_rails](https://github.com/luizkowalski/concurrent_rails) gem. This is a layer above concurrent-ruby's Futures that ensure any future code is run within the Rails Executor.
22
+
23
+ Finally, instead of relying on the caller to call `async` on asynchronous methods, we define the asynchronous methods on the class itself with the `async` definition. The caller simply uses `@my_object.my_method` and it will always be run safely in an alternate thread. There is also an implementation of `await`, making it easy to resolve the results of your method calls.
24
+
25
+ ## Adding `async` and `await` capabilities to ruby objects
26
+
27
+ Instead of defining methods on your class with ruby's `def` keyword, include the [Standard::Procedure::Async::Actor](https://github.com/standard-procedure/async/blob/main/lib/standard/procedure/async/actor.rb) module and use the `async` class method.
28
+
29
+ When you call an asynchronous method, it immediately returns an internal message object. If you don't care about the return value from the method, you can discard this object immediately. But if you do need the return value, you can call `value` on this message object - and your thread will then wait until the return value is ready. Alternatively, you can use `await`, effectively turning your asynchronous method into a synchronous one.
30
+
31
+ An added bonus (or negative, depending upon your point of view) is that this syntax is very similar to Javascript's async/await pairing, which similarly marks out asychronous function calls and waits until any Promises are ready to return their values.
32
+
33
+ Example usage:
34
+ ```ruby
35
+ class MyObject
36
+ include Standard::Procedure::Async::Actor
37
+
38
+ def initialize name
39
+ @name = name
40
+ @status = :idle
41
+ end
42
+
43
+ async :report_status do
44
+ @status
45
+ end
46
+
47
+ async :greet do
48
+ "Hello #{@name}".freeze
49
+ end
50
+
51
+ async :rename do |new_name|
52
+ @name = new_name.freeze
53
+ end
54
+
55
+ async :do_some_long_running_task do
56
+ @status = :in_progress
57
+ do_part_two_of_the_long_running_task
58
+ @status
59
+ end
60
+
61
+ private
62
+
63
+ async :do_part_two_of_the_long_running_task do
64
+ sleep 10
65
+ _rename "John"
66
+ @status = :done
67
+ end
68
+ end
69
+
70
+ @my_object = MyObject.new "George"
71
+
72
+ puts await { @my_object.greet } # => "Hello George"
73
+ await { @my_object.rename "Ringo" }
74
+ puts await { @my_object.greet } # => "Hello Ringo"
75
+ @my_object.rename "Paul" # Note: we didn't use await here - we'll talk about that later
76
+ puts await { @my_object.greet } # => "Hello Paul"
77
+
78
+ @initial_status = @my_object.do_some_long_running_task
79
+ puts @initial_status # => a internal message object
80
+ puts @initial_status.value # => :in_progress
81
+ sleep 11
82
+ @final_status = await { @my_object.report_status }
83
+ puts @final_status # => :done
84
+ puts await { @my_object.greet } # => Hello John
85
+ ```
86
+
87
+ ### Defining asynchronous methods
88
+
89
+ When defining `MyObject`, we use `async` instead of `def` for each method. For example, we use `async :greet` instead of `def greet`. This creates two methods - `greet` and `_greet`. `_greet` is the actual implementation of the method and `greet` is the asynchronous wrapper around it.
90
+
91
+ ### Awaiting the results from those methods
92
+
93
+ The asynchronous wrapper always returns an object containing a [Concurrent::MVar](https://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/MVar.html) which is empty until the actor has completed its work.
94
+
95
+ If you need the return value from the method, there are two ways to access it.
96
+
97
+ You can call `value` on the returned object, or you can use the `await` method (which is just a fancy wrapper around `value`). In both cases the calling thread will block until the value is returned. In the example above, you can see what happens if you use neither of these methods - `puts @initial_status` returns the internal message object itself, not any meaningful information. The next line then calls `@initial_status.value` to wait until the return value is generated.
98
+
99
+ ### The sequence of asynchronous calls
100
+
101
+ In the example above there is the following code:
102
+
103
+ ```ruby
104
+ @my_object.rename "Paul"
105
+ puts await { @my_object.greet } # => "Hello Paul"
106
+ ```
107
+ The call to `rename` is asynchronous, so you may expect that sometimes the following call to `greet` would return "Hello Paul" and other times it would return "Hello Ringo" (the previous value) - depending on the timing of the two calls.
108
+
109
+ However, it will _always_ return "Hello Paul".
110
+
111
+ This is because internally, the actor queues all method calls. So even if the call to `rename` takes a long time to complete, the subsequent call to `greet` will not start until `rename` has finished.
112
+
113
+ Another example of the same behaviour is in `do_some_long_running_task`:
114
+
115
+ ```ruby
116
+ async :do_some_long_running_task do
117
+ @status = :in_progress
118
+ do_part_two_of_the_long_running_task
119
+ @status
120
+ end
121
+ ```
122
+
123
+ When `@my_object.do_some_long_running_task` is called, the message `do_some_long_running_task` is added to the queue.
124
+
125
+ When the queue starts processing that message, `_do_some_long_running_task` (the implementation of the method) changes the status to :in_progress, then adds `do_part_two_of_the_long_running_task` to the message queue. This second message will _not_ start processing immediately as it is behind the unfinished `do_some_long_running_task`. `_do_some_long_running_task` returns the value of status (which is still :in_progress) and completes, which allows the next message on the queue to start. And that next message is probably `do_part_two_of_the_long_running_task` - but it might not be, as other threads may have got there first.
126
+
127
+ ### The rules of using actors
128
+
129
+ - If you make a public method asynchronous, you need to make _all_ public methods asynchronous. You cannot mix and match asynchronous and synchronous usage. The simplest way to comply is to make all your public methods as `async` and your protected and private methods as synchronous. If you have to call an async method on `self` use the internal implementation (which starts with `_`).
130
+ - Never make internal instance variables directly accessible without an asynchronous method call. Do not use `attr_reader` or `attr_accessor` - these will bypass the internal queue and you may get inconsistent results from the actor. For the same reason, never update internal variables outside of an asynchronous call.
131
+ - When an object is calling its own internal methods, never use `await` or `value` as this will cause your actor to block indefinitely. If you don't care about the return value or if it does not matter when the method starts and finishes, call the asynchronous method. If you need the return value or need to be sure that the method runs immediately then use the internal implementation - `_rename` instead of `rename`.
132
+ - Avoid class variables. These are effectively global variables that are accessible without any locking around them, so you could get inconsistent results. If you must have class variables, intialize them on startup and if they are mutable, use concurrent-ruby's thread-safe objects.
133
+
134
+ ## Making Rails work with Concurrent-Ruby
135
+
136
+ [Concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby) is amazing, providing an extensive suite of tools to make multi-threaded programming as safe as it can possibly be in ruby (which isn't perfect, as ruby's dynamic semantics make it impossible to be completely safe).
137
+
138
+ However, concurrent-ruby doesn't play well with [Ruby on Rails](https://rubyonrails.org). Rails is a big complex framework that auto-loads code and has lots of data stored in class variables (which are effectively globals that can be written to and read from any thread at any time). Therefore it includes the [Rails Executor](https://guides.rubyonrails.org/threading_and_code_execution.html) which ensures that the framework is aware when other threads may be touching Rails code.
139
+
140
+ This gem checks to see if `Rails` is defined, and if so, it attempts to load [Luiz Kowalski](https://github.com/luizkowalski)'s [concurrent_rails](https://github.com/luizkowalski/concurrent_rails) gem. This provides a wrapper around concurrent-ruby's [Promises](https://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html) factory that ensures the Rails Executor is loaded before the threaded code is run. This gem then uses the concurrent-rails Future instead of concurrent-ruby's Future, so it can be sure that your code is both thread-safe and rails-safe.
141
+
142
+ ## Installation
143
+
144
+ Add this line to your application's Gemfile:
145
+
146
+ ```ruby
147
+ gem 'standard-procedure-async'
148
+ ```
149
+
150
+ If you are using Ruby on Rails, you must also add in the `concurrent_rails` gem, or this gem will raise an error when you try to use it.
151
+
152
+ ```ruby
153
+ gem 'concurrent_rails'
154
+ ```
155
+
156
+ And then execute:
157
+
158
+ $ bundle install
159
+
160
+ Or install it yourself as:
161
+
162
+ $ gem install standard-procedure-async
163
+
164
+ ## Development
165
+
166
+ Coming soon
167
+ ## Contributing
168
+
169
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/standard-procedure-async.
170
+
171
+ ## License
172
+
173
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "standard/rake"
9
+
10
+ task default: %i[spec standard]
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/array"
4
+ require "concurrent/mvar"
5
+ require "concurrent/immutable_struct"
6
+
7
+ module Standard::Procedure::Async
8
+ module Actor
9
+ def self.included base
10
+ base.class_eval do
11
+ extend ClassMethods
12
+
13
+ def initialize *args
14
+ super
15
+ @_messages = Concurrent::Array.new
16
+ end
17
+ end
18
+ end
19
+
20
+ module ClassMethods
21
+ def async name, &implementation
22
+ name = name.to_sym
23
+ implementation_name = :"_#{name}"
24
+
25
+ define_method name.to_sym do |*args, &block|
26
+ _add_message_to_queue(implementation_name, *args, &block)
27
+ end
28
+
29
+ define_method implementation_name do |*args, &block|
30
+ implementation.call(*args, &block)
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :_messages
38
+
39
+ def _add_message_to_queue name, *args, &block
40
+ message = Message.new(self, name, args, block, Concurrent::MVar.new)
41
+ _messages << message
42
+ _perform_messages if _messages.count == 1
43
+ message
44
+ end
45
+
46
+ def _perform_messages
47
+ Standard::Procedure::Async.promises.future do
48
+ while (message = _messages.shift)
49
+ message.call
50
+ end
51
+ end
52
+ end
53
+
54
+ class Message < Concurrent::ImmutableStruct.new(:target, :name, :args, :block, :result)
55
+ def value
56
+ result.take
57
+ end
58
+
59
+ def call
60
+ result.put target.send(name, *args, &block)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,5 @@
1
+ module Kernel
2
+ def await &block
3
+ block.call.value
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Standard::Procedure::Async
4
+ class Error < StandardError
5
+ end
6
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rails_not_loaded_error"
4
+ require "concurrent/promises"
5
+
6
+ module Standard::Procedure::Async
7
+ class Promises
8
+ def initialize
9
+ @promises = rails_is_loaded? ? ConcurrentRails::Promises : Concurrent::Promises
10
+ end
11
+
12
+ attr_reader :promises
13
+
14
+ def future &block
15
+ @promises.future(&block)
16
+ end
17
+
18
+ private
19
+
20
+ def rails_is_loaded?
21
+ return false if !defined?(Rails::Railtie)
22
+ raise RailsNotLoadedError if !defined?(ConcurrentRails) || !defined?(Rails::Railtie)
23
+ true
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "error"
4
+
5
+ module Standard::Procedure::Async
6
+ class RailsNotLoadedError < Error
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Standard
4
+ module Procedure
5
+ module Async
6
+ VERSION = "0.1.0"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "async/version"
4
+ require_relative "async/error"
5
+ require_relative "async/promises"
6
+ require_relative "async/actor"
7
+ require_relative "async/await"
8
+ module Standard
9
+ module Procedure
10
+ module Async
11
+ def self.promises
12
+ @promises ||= Promises.new
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,8 @@
1
+ module Standard
2
+ module Procedure
3
+ module Async
4
+ VERSION: String
5
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/standard/procedure/async/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "standard-procedure-async"
7
+ spec.version = Standard::Procedure::Async::VERSION
8
+ spec.authors = ["Rahoul Baruah"]
9
+ spec.email = ["rahoulb@standardprocedure.app"]
10
+
11
+ spec.summary = "A simple wrapper around Concurrent::Future to make concurrent-ruby Rails-friendly."
12
+ spec.description = "Provides a wrapper around concurrent-ruby's Concurrent::Future to automatically wrap it in a Rails-friendly executor."
13
+ spec.homepage = "https://github.com/standard-procedure/async"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.7.0"
16
+
17
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = "https://github.com/standard-procedure/async"
21
+ spec.metadata["changelog_uri"] = "https://github.com/standard-procedure/async/blob/main/CHANGELOG.md"
22
+
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
26
+ end
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_dependency "concurrent-ruby", ">= 1.0"
33
+ spec.add_development_dependency "activesupport"
34
+ spec.add_development_dependency "railties"
35
+ spec.add_development_dependency "concurrent_rails"
36
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: standard-procedure-async
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Rahoul Baruah
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-05-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: concurrent-ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: railties
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: concurrent_rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Provides a wrapper around concurrent-ruby's Concurrent::Future to automatically
70
+ wrap it in a Rails-friendly executor.
71
+ email:
72
+ - rahoulb@standardprocedure.app
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".rspec"
78
+ - ".standard.yml"
79
+ - CHANGELOG.md
80
+ - Gemfile
81
+ - Gemfile.lock
82
+ - LICENSE.txt
83
+ - README.md
84
+ - Rakefile
85
+ - lib/standard/procedure/async.rb
86
+ - lib/standard/procedure/async/actor.rb
87
+ - lib/standard/procedure/async/await.rb
88
+ - lib/standard/procedure/async/error.rb
89
+ - lib/standard/procedure/async/promises.rb
90
+ - lib/standard/procedure/async/rails_not_loaded_error.rb
91
+ - lib/standard/procedure/async/version.rb
92
+ - sig/standard/procedure/async.rbs
93
+ - standard-procedure-async.gemspec
94
+ homepage: https://github.com/standard-procedure/async
95
+ licenses:
96
+ - MIT
97
+ metadata:
98
+ allowed_push_host: https://rubygems.org
99
+ homepage_uri: https://github.com/standard-procedure/async
100
+ source_code_uri: https://github.com/standard-procedure/async
101
+ changelog_uri: https://github.com/standard-procedure/async/blob/main/CHANGELOG.md
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: 2.7.0
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubygems_version: 3.4.12
118
+ signing_key:
119
+ specification_version: 4
120
+ summary: A simple wrapper around Concurrent::Future to make concurrent-ruby Rails-friendly.
121
+ test_files: []