asynchronize 0.1.2 → 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 +4 -4
- data/asynchronize.gemspec +1 -1
- data/lib/asynchronize.rb +22 -25
- data/readme.md +30 -14
- data/spec/spec.rb +51 -12
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eac19fda33067270bc4464149d1f02a9561cd6a994e004f9e73c0b288ae38f5c
|
4
|
+
data.tar.gz: 55301cee6db401579e3d8836c6485b4ae33e76f6319f98e6853f151076c77a83
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c06a933de8ec8aab5f2f97bf0943b5405a02ff3ff74f3612506cab6eb74117ac090f1810b5e3aa1a52b98babae51499fe8319c773e74cf41f6dbfa106d62c6b
|
7
|
+
data.tar.gz: 19661f53978d370e1cf80b34c1ee5a19bb7575a7f5600752ddd67960a4da6dc9148f3ac6c52e1cb06f249f99281cc6bc247e14642728fa4e6080552b8235ab9d
|
data/asynchronize.gemspec
CHANGED
@@ -2,7 +2,7 @@ require 'date'
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'asynchronize'
|
5
|
-
s.version = '0.
|
5
|
+
s.version = '0.2.0'
|
6
6
|
s.date = Date.today.to_s
|
7
7
|
s.summary = 'Easily make multiple methods asynchronous with one line of code.'
|
8
8
|
s.description = 'Take any synchronous method, and run it asynchronously, ' +
|
data/lib/asynchronize.rb
CHANGED
@@ -3,13 +3,13 @@ module Asynchronize
|
|
3
3
|
def self.included(base)
|
4
4
|
base.class_eval do
|
5
5
|
# The methods we have already asynchronized
|
6
|
-
|
6
|
+
@asynced_methods = Set.new
|
7
7
|
# The methods that should be asynchronized.
|
8
|
-
|
8
|
+
@methods_to_async = Set.new
|
9
9
|
# Originally used a single value here, but that's not thread safe.
|
10
10
|
# ...Though you probably have other problems if you have multiple
|
11
11
|
# threads adding methods to your class.
|
12
|
-
|
12
|
+
@methods_asyncing = Set.new
|
13
13
|
|
14
14
|
##
|
15
15
|
# Call to asynchronize a method.
|
@@ -23,19 +23,18 @@ module Asynchronize
|
|
23
23
|
# @example To add any number of methods to be asynchronized.
|
24
24
|
# asynchronize :method1, :method2, :methodn
|
25
25
|
def self.asynchronize(*methods)
|
26
|
-
|
26
|
+
@methods_to_async.merge(methods)
|
27
27
|
methods.each do |method|
|
28
28
|
# If it's not defined yet, we'll get it with method_added
|
29
|
-
next unless method_defined?
|
29
|
+
next unless method_defined?(method)
|
30
30
|
Asynchronize.create_new_method(method, self)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
# require 'pry'; binding.pry
|
35
34
|
# Save the old method_added so we don't overwrite it.
|
36
|
-
if self.methods.include?
|
35
|
+
if self.methods.include?(:method_added)
|
37
36
|
singleton_class.send(:alias_method, :old_method_added, :method_added)
|
38
|
-
singleton_class.
|
37
|
+
singleton_class.send(:undef_method, :method_added)
|
39
38
|
end
|
40
39
|
|
41
40
|
##
|
@@ -45,12 +44,14 @@ module Asynchronize
|
|
45
44
|
# anything else Ruby calls this automatically when defining a method; it
|
46
45
|
# should not be called directly.
|
47
46
|
def self.method_added(method)
|
48
|
-
#
|
49
|
-
return if
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
47
|
+
# Return if this is an inherited class that hasn't included asynchronize
|
48
|
+
return if @methods_asyncing.nil?
|
49
|
+
# Return if we're already processing this method
|
50
|
+
return if @methods_asyncing.include?(method)
|
51
|
+
@methods_asyncing.add(method)
|
52
|
+
self.old_method_added(method) if self.methods.include?(:old_method_added)
|
53
|
+
return unless @methods_to_async.include?(method)
|
54
|
+
# This will delete from @methods_asyncing
|
54
55
|
Asynchronize.create_new_method(method, self)
|
55
56
|
end
|
56
57
|
end
|
@@ -63,13 +64,13 @@ module Asynchronize
|
|
63
64
|
old_method = instance_method(method)
|
64
65
|
# Can't just store the method name, since it would break if the method
|
65
66
|
# was redefined.
|
66
|
-
return if
|
67
|
-
undef_method
|
67
|
+
return if @asynced_methods.include?(old_method)
|
68
|
+
undef_method(method)
|
68
69
|
|
69
|
-
|
70
|
+
@methods_asyncing.add(method)
|
70
71
|
define_method(method, Asynchronize._build_new_method(old_method))
|
71
|
-
|
72
|
-
|
72
|
+
@methods_asyncing.delete(method)
|
73
|
+
@asynced_methods.add(instance_method(method))
|
73
74
|
end
|
74
75
|
end
|
75
76
|
|
@@ -77,12 +78,8 @@ module Asynchronize
|
|
77
78
|
def self._build_new_method(old_method)
|
78
79
|
return Proc.new do |*args, &block|
|
79
80
|
return Thread.new(old_method, args, block) do |told_method, targs, tblock|
|
80
|
-
|
81
|
-
|
82
|
-
Thread.current[:return_value] = result
|
83
|
-
else
|
84
|
-
tblock.call(result)
|
85
|
-
end
|
81
|
+
Thread.current[:return_value] = told_method.bind(self).call(*targs)
|
82
|
+
tblock.call(Thread.current[:return_value]) unless tblock.nil?
|
86
83
|
end
|
87
84
|
end
|
88
85
|
end
|
data/readme.md
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
### The easiest way to make multiple methods asynchronous.
|
6
6
|
|
7
7
|
Find yourself writing the same boilerplate for all your asynchronous methods?
|
8
|
-
Get
|
8
|
+
Get dry with asynchronize.
|
9
9
|
|
10
10
|
Just install with `gem install asynchronize` or add to your Gemfile and `bundle`
|
11
11
|
|
@@ -27,21 +27,20 @@ end
|
|
27
27
|
```
|
28
28
|
|
29
29
|
Now, to call those methods.
|
30
|
-
You can
|
31
|
-
```Ruby
|
32
|
-
Test.new.my_test do |return_value|
|
33
|
-
puts return_value
|
34
|
-
end
|
35
|
-
# > test
|
36
|
-
```
|
37
|
-
|
38
|
-
Or, you can manage the thread yourself; the returned value will be in the thread
|
30
|
+
You can manage the thread yourself; the returned value will be in the thread
|
39
31
|
variable `:return_value` once it returns.
|
40
32
|
```Ruby
|
41
33
|
thread = Test.new.my_test
|
42
34
|
thread.join
|
43
|
-
puts thread[:return_value]
|
44
|
-
|
35
|
+
puts thread[:return_value] # > test
|
36
|
+
```
|
37
|
+
|
38
|
+
Or for convenience, you can just pass it a block.
|
39
|
+
The return value, will still be in the thread variable `:return_value`
|
40
|
+
```Ruby
|
41
|
+
Test.new.my_test do |return_value|
|
42
|
+
puts return_value # > test
|
43
|
+
end
|
45
44
|
```
|
46
45
|
|
47
46
|
## Inspiration
|
@@ -54,10 +53,21 @@ def method_name(args)
|
|
54
53
|
end
|
55
54
|
```
|
56
55
|
It's extra typing, adds an unneeded extra layer of nesting, and just feels
|
57
|
-
dirty.
|
56
|
+
dirty. I couldn't find an existing library that wasn't trying to solve other
|
57
|
+
problems I didn't have. Now, just call asynchronize to make any method
|
58
|
+
asynchronous.
|
59
|
+
|
60
|
+
## Versioning Policy
|
61
|
+
Once I feel like this is ready for production code - version 1.0.0, this project
|
62
|
+
will follow [Semantic Versioning](https://semver.org) until then, the patch
|
63
|
+
number (0.0.x) will be updated for any changes that do not affect the public
|
64
|
+
interface. Versions that increment the minor number will have at least one of
|
65
|
+
the following. A new feature will be added, some feature will be deprecated, or
|
66
|
+
some previously deprecated feature will be removed. Deprecated features will be
|
67
|
+
removed on the very next version that increments the minor version number.
|
58
68
|
|
59
69
|
## FAQ
|
60
|
-
###
|
70
|
+
### Doesn't metaprogramming hurt performance?
|
61
71
|
Not at all! We're actually totally redefining the methods, so the method itself
|
62
72
|
is exactly as efficient as it would have been had you wrote it that way
|
63
73
|
originally.
|
@@ -87,6 +97,12 @@ tests as the source. You should read it, I'd love feedback!
|
|
87
97
|
### Do you accept contributions?
|
88
98
|
Absolutely! If your use case isn't compatible with the project, you find a
|
89
99
|
bug, or just want to donate some tests; make an issue or send a PR please.
|
100
|
+
To run the test suite, just run `bundle` then `rake` from the project directory.
|
101
|
+
|
102
|
+
### What's the difference between this and promises?
|
103
|
+
This attempts to be a very lightweight wrapper around threads. There's no new
|
104
|
+
interface to use, just define a regular method, and interact with it like a
|
105
|
+
regular thread.
|
90
106
|
|
91
107
|
## License
|
92
108
|
MIT
|
data/spec/spec.rb
CHANGED
@@ -4,9 +4,6 @@ require './lib/asynchronize.rb'
|
|
4
4
|
class BasicSpec < Minitest::Test
|
5
5
|
describe Asynchronize do
|
6
6
|
before do
|
7
|
-
if defined? Test
|
8
|
-
BasicSpec.send(:remove_const, :Test)
|
9
|
-
end
|
10
7
|
class Test
|
11
8
|
include Asynchronize
|
12
9
|
def test(val=5)
|
@@ -14,6 +11,9 @@ class BasicSpec < Minitest::Test
|
|
14
11
|
end
|
15
12
|
end
|
16
13
|
end
|
14
|
+
after do
|
15
|
+
BasicSpec.send(:remove_const, :Test)
|
16
|
+
end
|
17
17
|
|
18
18
|
describe "when we asynchronize a method" do
|
19
19
|
it "should not be the same method" do
|
@@ -42,21 +42,21 @@ class BasicSpec < Minitest::Test
|
|
42
42
|
end
|
43
43
|
it "should not affect methods on other classes when called before" do
|
44
44
|
Test.asynchronize :test
|
45
|
-
class
|
45
|
+
class OtherBeforeTest
|
46
46
|
def test
|
47
47
|
end
|
48
48
|
end
|
49
49
|
Test.new.test.class.must_equal(Thread)
|
50
|
-
|
50
|
+
OtherBeforeTest.new.test.class.wont_equal(Thread)
|
51
51
|
end
|
52
52
|
it "should not affect methods on other classes when called after" do
|
53
|
-
class
|
53
|
+
class OtherAfterTest
|
54
54
|
def test
|
55
55
|
end
|
56
56
|
end
|
57
57
|
Test.asynchronize :test
|
58
58
|
Test.new.test.class.must_equal(Thread)
|
59
|
-
|
59
|
+
OtherAfterTest.new.test.class.wont_equal(Thread)
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
@@ -107,10 +107,7 @@ class BasicSpec < Minitest::Test
|
|
107
107
|
|
108
108
|
describe "when there is an existing method_added" do
|
109
109
|
before do
|
110
|
-
|
111
|
-
BasicSpec.send(:remove_const, :AnotherTest)
|
112
|
-
end
|
113
|
-
class AnotherTest
|
110
|
+
class MethodAddedTest
|
114
111
|
@running = false
|
115
112
|
def self.method_added(method)
|
116
113
|
return if @running
|
@@ -129,9 +126,51 @@ class BasicSpec < Minitest::Test
|
|
129
126
|
end
|
130
127
|
end
|
131
128
|
end
|
129
|
+
after do
|
130
|
+
BasicSpec.send(:remove_const, :MethodAddedTest)
|
131
|
+
end
|
132
132
|
it "should call that method_added before, and only once." do
|
133
|
-
|
133
|
+
MethodAddedTest.new.test.join[:return_value].must_equal 5
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe "when inheriting from another class" do
|
138
|
+
before do
|
139
|
+
class ChildClassTest < Test
|
140
|
+
include Asynchronize
|
141
|
+
def test
|
142
|
+
return super + 1
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
after do
|
147
|
+
BasicSpec.send(:remove_const, :ChildClassTest)
|
148
|
+
end
|
149
|
+
it "should be able to call super when it's been asynchronized" do
|
150
|
+
class ChildClassTest
|
151
|
+
asynchronize :test
|
152
|
+
end
|
153
|
+
ChildClassTest.new.test.join[:return_value].must_equal 6
|
154
|
+
end
|
155
|
+
it "should be able to call super when super has been asynchronized" do
|
156
|
+
class Test
|
157
|
+
asynchronize :test
|
158
|
+
end
|
159
|
+
class ChildClassTest
|
160
|
+
undef_method :test
|
161
|
+
def test
|
162
|
+
return super.join[:return_value] + 1
|
163
|
+
end
|
164
|
+
end
|
165
|
+
ChildClassTest.new.test.must_equal 6
|
134
166
|
end
|
135
167
|
end
|
136
168
|
end
|
169
|
+
|
170
|
+
# describe "when being inherited from another class" do
|
171
|
+
# before do
|
172
|
+
#
|
173
|
+
# end
|
174
|
+
# after do
|
175
|
+
# end
|
137
176
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: asynchronize
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kenneth Cochran
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Take any synchronous method, and run it asynchronously, without cluttering
|
14
14
|
your code with repetetive boilerplate.
|