asynchronize 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|