asynchronize 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: 3469c3b1731ec0b85a8014439e676153947b17adf250567a15779fc039cb39a4
4
+ data.tar.gz: 7145e39f5206053ab44c1f896b3b26c242891919b48e1c4afae26ce61e2b8199
5
+ SHA512:
6
+ metadata.gz: f970416bd025a1e3ad544fcda9d4babb6fbf7eb75c9e764c88ed987c720f2c3122893d83aacd142ecfbbc80f1a169e6554144efef33a37d5fd96140659366dd8
7
+ data.tar.gz: 3b4829c2ecccdad396c960966febf28d6176621cede70d1243aae3abd6a968c3df4a0e5fa25ff6dcd7ee0831d479c6e95bc6fec2e21a3416d48a5afa84a78ca1
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.pattern = "spec/*spec.rb"
5
+ end
6
+
7
+ desc "Run tests"
8
+ task :default => :test
@@ -0,0 +1,24 @@
1
+ require 'date'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'asynchronize'
5
+ s.version = '0.1.0'
6
+ s.date = Date.today.to_s
7
+ s.summary = 'Easily make multiple methods asynchronous at once.'
8
+ s.description = 'Take any synchronous method, and run it asynchronously, ' +
9
+ 'without cluttering your code with repetetive boilerplate.'
10
+ s.author = 'Kenneth Cochran'
11
+ s.email = 'kenneth.cochran101@gmail.com'
12
+ s.files = [
13
+ 'lib/asynchronize.rb',
14
+ 'spec/spec.rb',
15
+ 'asynchronize.gemspec',
16
+ 'Rakefile',
17
+ 'readme.md'
18
+ ]
19
+ s.test_files = [
20
+ 'spec/spec.rb'
21
+ ]
22
+ s.homepage = 'https://github.com/kennycoc/asynchronize'
23
+ s.license = 'MIT'
24
+ end
@@ -0,0 +1,59 @@
1
+ module Asynchronize
2
+ require 'set'
3
+ def self.included base
4
+ base.class_eval do
5
+ # The methods we have already asynchronized
6
+ @@asynced_methods = Set.new
7
+ # The methods that should be asynchronized.
8
+ @@methods_to_async = Set.new
9
+ # Originally used a single value here, but that's not thread safe.
10
+ # ...Though you probably have other problems if you have multiple
11
+ # threads adding methods to your class.
12
+ @@methods_asyncing = Set.new
13
+
14
+ def self.asynchronize(*methods)
15
+ @@methods_to_async.merge(methods)
16
+ methods.each do |method|
17
+ # If it's not defined yet, we'll get it with method_added
18
+ next unless method_defined? method
19
+ Asynchronize.create_new_method(method, self)
20
+ end
21
+ end
22
+
23
+ # Save the old method_added so we don't overwrite it.
24
+ if method_defined? :method_added
25
+ alias_method :old_method_added, :method_added
26
+ undef_method(:method_added)
27
+ end
28
+
29
+ def self.method_added(method)
30
+ old_method_added(method) if method_defined? :old_method_added
31
+ return unless @@methods_to_async.include? method
32
+ return if @@methods_asyncing.include? method
33
+ Asynchronize.create_new_method(method, self)
34
+ end
35
+ end
36
+ end
37
+
38
+ def self.create_new_method(method, klass)
39
+ klass.instance_eval do
40
+ old_method = instance_method(method)
41
+ return if @@asynced_methods.include?(old_method)
42
+ undef_method method
43
+ @@methods_asyncing.add(method)
44
+
45
+ define_method(method) do |*args, &block|
46
+ return Thread.new(args, block) do |targs, tblock|
47
+ result = old_method.bind(self).(*targs)
48
+ unless tblock.nil?
49
+ tblock.call(result);
50
+ else
51
+ Thread.current[:return_value] = result
52
+ end
53
+ end
54
+ end # define_method
55
+ @@methods_asyncing.delete(method)
56
+ @@asynced_methods.add(instance_method method)
57
+ end
58
+ end
59
+ end
data/readme.md ADDED
@@ -0,0 +1,90 @@
1
+ # Asynchronize
2
+ ### The easiest way to make a method asynchronous.
3
+
4
+ Find yourself writing the same boilerplate for all your asynchronous methods?
5
+ Get dryyy with asynchronize.
6
+
7
+ There are no dependencies other than Ruby.
8
+ Just install with `gem install asynchronize` or add to your Gemfile and `bundle`
9
+
10
+ ## Usage
11
+ Create a class with asynchronized methods
12
+ ```Ruby
13
+ require 'asynchronize'
14
+ class Test
15
+ include Asynchronize
16
+ # This can be called anywhere.
17
+ asynchronize :my_test, :my_other_test
18
+ def my_test
19
+ return 'test'
20
+ end
21
+ def my_other_test
22
+ #do stuff here too
23
+ end
24
+ end
25
+ ```
26
+
27
+ Now, to call those methods.
28
+ You can just pass it a block.
29
+ ```Ruby
30
+ Test.new.my_test do |return_value|
31
+ puts return_value
32
+ end
33
+ # > test
34
+ ```
35
+
36
+ Or, you can manage the thread yourself; the returned value will be in the thread
37
+ variable `:return_value` once it returns.
38
+ ```Ruby
39
+ thread = Test.new.my_test
40
+ thread.join
41
+ puts thread[:return_value]
42
+ # > test
43
+ ```
44
+
45
+ ## Inspiration
46
+ I got tired of writing this over and over.
47
+ ```Ruby
48
+ def method_name(args)
49
+ Thread.new(args) do |targs|
50
+ # Actual code.
51
+ end
52
+ end
53
+ ```
54
+ It's extra typing, adds an unneeded extra layer of nesting, and just feels
55
+ dirty. Now, I can just call asynchronize to make any method asynchronous.
56
+
57
+ ## FAQ
58
+ ### Metaprogramming?? won't this hurt performance?
59
+ Not at all! We're actually totally redefining the methods, so the method itself
60
+ is exactly as efficient as it would have been had you wrote it that way
61
+ originally.
62
+
63
+ ### So, how does it work?
64
+ When you `include Asynchronize` it does two things.
65
+ 1. It defines the asynchronize method for your class
66
+ 2. It defines method_added on your class.
67
+
68
+ When you call asynchronize, it creates a set containing all of the methods you
69
+ want asynchronized. If they already exist, they are modified; otherwise,
70
+ method_added checks for them with every new method you add to the class. This
71
+ way, you can call asynchronize any time, and know that the methods will be
72
+ asynchronized when you use them.
73
+
74
+ ### So, does that mean I can't use asynchronize if I already use method_added?
75
+ We check for and alias your old method_added. It will be called before
76
+ anything else. Of course, if you define method_added after including
77
+ Asynchronize, you have to do the same and be careful to not overwrite ours!
78
+
79
+ ### Why do I need another framework? My code's bloated enough as it is?
80
+ It's super tiny. Just a light wrapper around the existing language features.
81
+ Seriously, it's just around fifty lines of code. Actually, according to
82
+ [cloc](https://www.npmjs.com/package/cloc) there's twice as many lines in the
83
+ tests as the source. You should read it, I'd love feedback!
84
+
85
+ ### Do you accept contributions?
86
+ Absolutely! If your use case isn't compatible with the project, you find a
87
+ bug, or just want to donate some tests; make an issue or send a PR please.
88
+
89
+ ## License
90
+ MIT
data/spec/spec.rb ADDED
@@ -0,0 +1,108 @@
1
+ require 'minitest/autorun'
2
+ require './lib/asynchronize.rb'
3
+
4
+ class BasicSpec < Minitest::Test
5
+ describe Asynchronize do
6
+ before do
7
+ if defined? Test
8
+ BasicSpec.send(:remove_const, :Test)
9
+ end
10
+ class Test
11
+ include Asynchronize
12
+ def test(val=5)
13
+ return val
14
+ end
15
+ end
16
+ end
17
+
18
+ describe "when we asynchronize a method" do
19
+ it "should not be the same method" do
20
+ original_method = Test.instance_method :test
21
+ Test.asynchronize :test
22
+ new_method = Test.instance_method(:test)
23
+ original_method.wont_equal(new_method, "The method was not overwritten")
24
+ end
25
+ it "should not return a thread unless we asynchronize it" do
26
+ Test.new.test.class.wont_equal(Thread, "The method was not overwritten")
27
+ end
28
+ it "should be the same method if we call a second time" do
29
+ Test.asynchronize :test
30
+ original_method = Test.instance_method :test
31
+ Test.asynchronize :test
32
+ new_method = Test.instance_method :test
33
+ original_method.must_equal(new_method,
34
+ "Asynchronized Inception has occurred")
35
+ end
36
+ it "should not throw an error if the specified method does not exist" do
37
+ Test.asynchronize :notamethod
38
+ end
39
+ it "should not create a method if the specified method does not exist" do
40
+ Test.asynchronize :notamethod
41
+ Test.method_defined?(:notamethod).must_equal false
42
+ end
43
+ it "should not affect methods on other classes when called before" do
44
+ Test.asynchronize :test
45
+ class MyTest
46
+ def test
47
+ end
48
+ end
49
+ Test.new.test.class.must_equal(Thread)
50
+ MyTest.new.test.class.wont_equal(Thread)
51
+ end
52
+ it "should not affect methods on other classes when called after" do
53
+ class AnotherTest
54
+ def test
55
+ end
56
+ end
57
+ Test.asynchronize :test
58
+ Test.new.test.class.must_equal(Thread)
59
+ AnotherTest.new.test.class.wont_equal(Thread)
60
+ end
61
+ end
62
+
63
+ describe "when we call asynchronized after defining the method" do
64
+ before do
65
+ Test.asynchronize :test
66
+ end
67
+ it "should return a thread" do
68
+ Test.new.test.class.must_equal(Thread,
69
+ "The asynchronized method without a block did not return a thread.")
70
+ end
71
+ it "should provide the result in return_value" do
72
+ Test.new.test.join[:return_value].must_equal 5
73
+ end
74
+ it "should provide the result as a block parameter" do
75
+ temp = 0
76
+ Test.new.test do |res|
77
+ temp = res
78
+ end.join
79
+ temp.must_equal 5, "temp is equal to #{temp}."
80
+ end
81
+ end
82
+
83
+ describe "when we call asynchronized before defining the method" do
84
+ before do
85
+ Test.asynchronize :othertest
86
+ class Test
87
+ def othertest
88
+ return 5
89
+ end
90
+ end
91
+ end
92
+ it "should return a thread" do
93
+ Test.new.othertest.class.must_equal(Thread,
94
+ "The asynchronized method without a block did not return a thread.")
95
+ end
96
+ it "should provide the result in return_value" do
97
+ Test.new.othertest.join[:return_value].must_equal 5
98
+ end
99
+ it "should provide the result as a block parameter" do
100
+ temp = 0
101
+ Test.new.othertest do |res|
102
+ temp = res
103
+ end.join
104
+ temp.must_equal 5, "temp is equal to #{temp}."
105
+ end
106
+ end
107
+ end
108
+ end
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: asynchronize
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kenneth Cochran
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-05-31 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Take any synchronous method, and run it asynchronously, without cluttering
14
+ your code with repetetive boilerplate.
15
+ email: kenneth.cochran101@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - Rakefile
21
+ - asynchronize.gemspec
22
+ - lib/asynchronize.rb
23
+ - readme.md
24
+ - spec/spec.rb
25
+ homepage: https://github.com/kennycoc/asynchronize
26
+ licenses:
27
+ - MIT
28
+ metadata: {}
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 2.7.6
46
+ signing_key:
47
+ specification_version: 4
48
+ summary: Easily make multiple methods asynchronous at once.
49
+ test_files:
50
+ - spec/spec.rb