in_threads 0.0.4 → 1.0.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.
- data/.gitignore +11 -0
- data/LICENSE.txt +1 -1
- data/README.markdown +13 -20
- data/in_threads.gemspec +13 -51
- data/lib/in_threads.rb +118 -18
- data/spec/in_threads_spec.rb +329 -31
- metadata +13 -47
- data/.tmignore +0 -1
- data/Rakefile +0 -25
- data/VERSION +0 -1
data/.gitignore
ADDED
data/LICENSE.txt
CHANGED
data/README.markdown
CHANGED
@@ -1,42 +1,35 @@
|
|
1
|
-
#
|
1
|
+
# in_threads
|
2
2
|
|
3
|
-
|
3
|
+
Easily execute ruby code in parallel.
|
4
4
|
|
5
|
-
##
|
5
|
+
## Installation
|
6
6
|
|
7
|
-
|
7
|
+
gem install in_threads
|
8
8
|
|
9
|
-
##
|
9
|
+
## Usage
|
10
10
|
|
11
11
|
By default there is maximum of 10 simultaneous threads
|
12
12
|
|
13
|
-
urls.in_threads.each do |url|
|
14
|
-
url.save_to_disk
|
15
|
-
end
|
16
|
-
|
17
13
|
urls.in_threads.map do |url|
|
18
14
|
url.fetch
|
19
15
|
end
|
20
16
|
|
17
|
+
urls.in_threads.each do |url|
|
18
|
+
url.save_to_disk
|
19
|
+
end
|
20
|
+
|
21
21
|
numbers.in_threads(2).map do |number|
|
22
|
-
…
|
23
22
|
# whery long and complicated formula
|
24
23
|
# using only 2 threads
|
25
24
|
end
|
26
25
|
|
27
|
-
You can use any Enumerable method but
|
26
|
+
You can use any Enumerable method, but some of them can not use threads (`inject`, `reduce`) or don't use blocks (`to_a`, `entries`, `drop`, `take`, `first`, `include?`, `member?`) or have both problems depending on usage type (`min`, `max`, `minmax`, `sort`)
|
28
27
|
|
29
28
|
urls.in_threads.any?(&:ok?)
|
30
29
|
urls.in_threads.all?(&:ok?)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
ruby )))
|
35
|
-
|
36
|
-
## INSTALL:
|
37
|
-
|
38
|
-
sudo gem install in_threads
|
30
|
+
urls.in_threads.none?(&:error?)
|
31
|
+
urls.in_threads.grep(/example\.com/, &:fetch)
|
39
32
|
|
40
33
|
## Copyright
|
41
34
|
|
42
|
-
Copyright (c) 2010 Ivan Kuchin. See LICENSE.txt for details.
|
35
|
+
Copyright (c) 2010-2011 Ivan Kuchin. See LICENSE.txt for details.
|
data/in_threads.gemspec
CHANGED
@@ -1,57 +1,19 @@
|
|
1
|
-
#
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
-
# -*- encoding: utf-8 -*-
|
1
|
+
# encoding: UTF-8
|
5
2
|
|
6
3
|
Gem::Specification.new do |s|
|
7
|
-
s.name
|
8
|
-
s.version
|
4
|
+
s.name = 'in_threads'
|
5
|
+
s.version = '1.0.0'
|
6
|
+
s.summary = %q{Execute ruby code in parallel}
|
7
|
+
s.homepage = "http://github.com/toy/#{s.name}"
|
8
|
+
s.authors = ['Ivan Kuchin']
|
9
|
+
s.license = 'MIT'
|
9
10
|
|
10
|
-
s.
|
11
|
-
s.authors = ["Ivan Kuchin"]
|
12
|
-
s.date = %q{2010-12-15}
|
13
|
-
s.extra_rdoc_files = [
|
14
|
-
"LICENSE.txt",
|
15
|
-
"README.markdown"
|
16
|
-
]
|
17
|
-
s.files = [
|
18
|
-
".tmignore",
|
19
|
-
"LICENSE.txt",
|
20
|
-
"README.markdown",
|
21
|
-
"Rakefile",
|
22
|
-
"VERSION",
|
23
|
-
"in_threads.gemspec",
|
24
|
-
"lib/in_threads.rb",
|
25
|
-
"spec/in_threads_spec.rb",
|
26
|
-
"spec/spec_helper.rb"
|
27
|
-
]
|
28
|
-
s.homepage = %q{http://github.com/toy/in_threads}
|
29
|
-
s.licenses = ["MIT"]
|
30
|
-
s.require_paths = ["lib"]
|
31
|
-
s.rubygems_version = %q{1.3.7}
|
32
|
-
s.summary = %q{Execute ruby blocks in parallel}
|
33
|
-
s.test_files = [
|
34
|
-
"spec/in_threads_spec.rb",
|
35
|
-
"spec/spec_helper.rb"
|
36
|
-
]
|
11
|
+
s.rubyforge_project = s.name
|
37
12
|
|
38
|
-
|
39
|
-
|
40
|
-
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
+
s.require_paths = %w[lib]
|
41
17
|
|
42
|
-
|
43
|
-
s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
|
44
|
-
s.add_development_dependency(%q<rake-gem-ghost>, [">= 0"])
|
45
|
-
s.add_development_dependency(%q<rspec>, [">= 0"])
|
46
|
-
else
|
47
|
-
s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
|
48
|
-
s.add_dependency(%q<rake-gem-ghost>, [">= 0"])
|
49
|
-
s.add_dependency(%q<rspec>, [">= 0"])
|
50
|
-
end
|
51
|
-
else
|
52
|
-
s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
|
53
|
-
s.add_dependency(%q<rake-gem-ghost>, [">= 0"])
|
54
|
-
s.add_dependency(%q<rspec>, [">= 0"])
|
55
|
-
end
|
18
|
+
s.add_development_dependency 'rspec'
|
56
19
|
end
|
57
|
-
|
data/lib/in_threads.rb
CHANGED
@@ -2,37 +2,137 @@ require 'thread'
|
|
2
2
|
require 'thwait'
|
3
3
|
|
4
4
|
module Enumerable
|
5
|
-
|
6
|
-
|
5
|
+
# Run enumerable method blocks in threads
|
6
|
+
def in_threads(thread_count = 10, &block)
|
7
|
+
InThreads.new(self, thread_count, &block)
|
7
8
|
end
|
8
9
|
end
|
9
10
|
|
11
|
+
# TODO: create my own ThreadsWait with blackjack and hookers?
|
12
|
+
# TODO: create class methods for connecting Enumerable method to runner
|
13
|
+
# TODO: run_in_threads_inconsecutive for `all?`, `any?`, `none?` and `one?`
|
14
|
+
# TODO: all ruby1.9.3 methods
|
15
|
+
# TODO: better way of handling grep?
|
16
|
+
# TODO: check method presence if Enumerable before connectin to runner
|
17
|
+
|
10
18
|
class InThreads
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
19
|
+
(
|
20
|
+
instance_methods.map(&:to_s) -
|
21
|
+
%w[__id__ __send__ class inspect instance_of? is_a? kind_of? nil? object_id respond_to? send]
|
22
|
+
).each{ |name| undef_method name }
|
23
|
+
(private_instance_methods.map(&:to_s) - %w[initialize]).each{ |name| undef_method name }
|
24
|
+
|
25
|
+
attr_reader :enumerable, :thread_count
|
26
|
+
def initialize(enumerable, thread_count = 10, &block)
|
27
|
+
@enumerable, @thread_count = enumerable, thread_count
|
28
|
+
each(&block) if block
|
29
|
+
end
|
30
|
+
|
31
|
+
def in_threads(thread_count = 10, &block)
|
32
|
+
self.class.new(enumerable, thread_count, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
%w[
|
36
|
+
each
|
37
|
+
all? any? none? one?
|
38
|
+
detect find find_index drop_while take_while
|
39
|
+
partition find_all select reject count
|
40
|
+
collect map group_by max_by min_by minmax_by sort_by
|
41
|
+
].each do |name|
|
42
|
+
class_eval <<-RUBY
|
43
|
+
def #{name}(*args, &block)
|
44
|
+
run_in_threads_consecutive(enumerable, :#{name}, *args, &block)
|
45
|
+
end
|
46
|
+
RUBY
|
47
|
+
end
|
48
|
+
|
49
|
+
%w[
|
50
|
+
reverse_each
|
51
|
+
each_with_index enum_with_index
|
52
|
+
each_cons each_slice enum_cons enum_slice
|
53
|
+
zip
|
54
|
+
cycle
|
55
|
+
].each do |name|
|
56
|
+
class_eval <<-RUBY
|
57
|
+
def #{name}(*args, &block)
|
58
|
+
run_in_threads_block_result_irrelevant(enumerable, :#{name}, *args, &block)
|
59
|
+
end
|
60
|
+
RUBY
|
15
61
|
end
|
16
62
|
|
17
|
-
def
|
18
|
-
|
19
|
-
|
63
|
+
def grep(*args, &block)
|
64
|
+
if block
|
65
|
+
run_in_threads_consecutive(enumerable.grep(*args), :map, &block)
|
66
|
+
else
|
67
|
+
enumerable.grep(*args)
|
68
|
+
end
|
20
69
|
end
|
21
70
|
|
22
|
-
|
23
|
-
|
71
|
+
%w[
|
72
|
+
inject reduce
|
73
|
+
max min minmax sort
|
74
|
+
entries to_a
|
75
|
+
drop take
|
76
|
+
first
|
77
|
+
include? member?
|
78
|
+
].each do |name|
|
79
|
+
class_eval <<-RUBY
|
80
|
+
def #{name}(*args, &block)
|
81
|
+
enumerable.#{name}(*args, &block)
|
82
|
+
end
|
83
|
+
RUBY
|
24
84
|
end
|
25
85
|
|
26
86
|
private
|
27
87
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
|
88
|
+
def run_in_threads_block_result_irrelevant(enumerable, method, *args, &block)
|
89
|
+
if block
|
90
|
+
waiter = ThreadsWait.new
|
91
|
+
begin
|
92
|
+
enumerable.send(method, *args) do |*block_args|
|
93
|
+
waiter.next_wait if waiter.threads.length >= thread_count
|
94
|
+
waiter.join_nowait([Thread.new(*block_args, &block)])
|
95
|
+
end
|
96
|
+
ensure
|
97
|
+
waiter.all_waits
|
98
|
+
end
|
99
|
+
else
|
100
|
+
enumerable.send(method, *args)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def run_in_threads_consecutive(enumerable, method, *args, &block)
|
105
|
+
if block
|
106
|
+
begin
|
107
|
+
queue = Queue.new
|
108
|
+
runner = Thread.new(enumerable) do |enumerable|
|
109
|
+
threads = []
|
110
|
+
begin
|
111
|
+
enumerable.each do |object|
|
112
|
+
if threads.length >= thread_count
|
113
|
+
threads = threads.select(&:alive?)
|
114
|
+
if threads.length >= thread_count
|
115
|
+
ThreadsWait.new(*threads).next_wait
|
116
|
+
end
|
117
|
+
end
|
118
|
+
break if Thread.current[:stop]
|
119
|
+
thread = Thread.new(object, &block)
|
120
|
+
threads << thread
|
121
|
+
queue << thread
|
122
|
+
end
|
123
|
+
ensure
|
124
|
+
threads.map(&:join)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
enumerable.send(method, *args) do |*block_args|
|
128
|
+
queue.pop.value
|
129
|
+
end
|
130
|
+
ensure
|
131
|
+
runner[:stop] = true
|
132
|
+
runner.join
|
32
133
|
end
|
33
|
-
|
134
|
+
else
|
135
|
+
enumerable.send(method, *args)
|
34
136
|
end
|
35
|
-
ensure
|
36
|
-
@threads.map(&:join)
|
37
137
|
end
|
38
138
|
end
|
data/spec/in_threads_spec.rb
CHANGED
@@ -1,57 +1,355 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
@a = (1..30).map{ |i| mock(:"e#{i}", :hello => nil, :value => i, :rand => rand * 0.01) }
|
7
|
-
@sleepy_prock = proc{ |e| sleep(e.rand); e.hello; e.value }
|
8
|
-
@sleepy_prock_a = proc{ |e| sleep(e.inject{ |sum, e| e.rand }); e.hash }
|
3
|
+
class Item
|
4
|
+
def initialize(i)
|
5
|
+
@i, @rand = i, rand
|
9
6
|
end
|
10
7
|
|
8
|
+
class MiddleMatcher
|
9
|
+
def ===(item)
|
10
|
+
raise "#{item.inspect} is not an Item" unless item.is_a?(Item)
|
11
|
+
(0.25..0.75) === item.instance_variable_get(:@rand)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def work
|
16
|
+
sleep @rand * 0.008
|
17
|
+
end
|
18
|
+
|
19
|
+
def value
|
20
|
+
work; @rand
|
21
|
+
end
|
22
|
+
|
23
|
+
def check?
|
24
|
+
value < 0.5
|
25
|
+
end
|
26
|
+
|
27
|
+
def touch_n_value(*args)
|
28
|
+
touch(*args); value
|
29
|
+
end
|
30
|
+
|
31
|
+
def touch_n_check?(*args)
|
32
|
+
touch(*args); check?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class ValueItem < Item
|
37
|
+
def initialize(i, value)
|
38
|
+
super(i)
|
39
|
+
@value = value
|
40
|
+
end
|
41
|
+
|
42
|
+
def value
|
43
|
+
work; @value
|
44
|
+
end
|
45
|
+
|
46
|
+
def check?
|
47
|
+
!!value
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def enum_methods(methods)
|
52
|
+
(Enumerable.instance_methods.map(&:to_s) & methods)
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "in_threads" do
|
56
|
+
let(:enum){ 30.times.map{ |i| Item.new(i) } }
|
57
|
+
let(:speed_coef){ 0.666 } # small coefficient, should be more if sleep time coefficient is bigger
|
58
|
+
|
11
59
|
def measure
|
12
60
|
start = Time.now
|
13
61
|
yield
|
14
62
|
Time.now - start
|
15
63
|
end
|
16
64
|
|
17
|
-
|
18
|
-
|
19
|
-
|
65
|
+
describe "in_threads" do
|
66
|
+
it "should not change existing instance" do
|
67
|
+
threaded = enum.in_threads(10)
|
68
|
+
proc{ threaded.in_threads(20) }.should_not change(threaded, :thread_count)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should create new instance with different title when called on WithProgress" do
|
72
|
+
threaded = enum.in_threads(10)
|
73
|
+
tthreaded = threaded.in_threads(20)
|
74
|
+
threaded.thread_count.should == 10
|
75
|
+
tthreaded.thread_count.should == 20
|
76
|
+
tthreaded.class.should == threaded.class
|
77
|
+
tthreaded.object_id.should_not == threaded.object_id
|
78
|
+
tthreaded.enumerable.should == threaded.enumerable
|
79
|
+
end
|
20
80
|
end
|
21
81
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
82
|
+
describe "each" do
|
83
|
+
it "should return same enum after running" do
|
84
|
+
enum.in_threads.each(&:value).should == enum
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should execute block for each element" do
|
88
|
+
enum.each{ |o| o.should_receive(:touch).once }
|
89
|
+
enum.in_threads.each(&:touch)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should run faster with threads" do
|
93
|
+
measure{ enum.in_threads.each(&:work) }.should < measure{ enum.each(&:work) } * speed_coef
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should run faster with more threads" do
|
97
|
+
measure{ enum.in_threads(20).each(&:work) }.should < measure{ enum.in_threads(2).each(&:work) } * speed_coef
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should return same enum without block" do
|
101
|
+
enum.in_threads.each.to_a.should == enum.each.to_a
|
33
102
|
end
|
34
103
|
end
|
35
104
|
|
36
|
-
|
37
|
-
|
105
|
+
enum_methods(%w[each_with_index enum_with_index]).each do |method|
|
106
|
+
describe method do
|
107
|
+
let(:runner){ proc{ |o, i| o.value } }
|
108
|
+
|
109
|
+
it "should return same result with threads" do
|
110
|
+
enum.in_threads.send(method, &runner).should == enum.send(method, &runner)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should fire same objects" do
|
114
|
+
enum.send(method){ |o, i| o.should_receive(:touch).with(i).once }
|
115
|
+
enum.in_threads.send(method){ |o, i| o.touch_n_value(i) }
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should run faster with threads" do
|
119
|
+
measure{ enum.in_threads.send(method, &runner) }.should < measure{ enum.send(method, &runner) } * speed_coef
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should return same enum without block" do
|
123
|
+
enum.in_threads.send(method).to_a.should == enum.send(method).to_a
|
124
|
+
end
|
125
|
+
end
|
38
126
|
end
|
39
127
|
|
40
|
-
|
41
|
-
|
128
|
+
describe "reverse_each" do
|
129
|
+
it "should return same result with threads" do
|
130
|
+
enum.in_threads.reverse_each(&:value).should == enum.reverse_each(&:value)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should fire same objects in reverse order" do
|
134
|
+
@order = mock('order', :touch => nil)
|
135
|
+
@order.should_receive(:touch).with(enum.last).ordered
|
136
|
+
@order.should_receive(:touch).with(enum[enum.length / 2]).ordered
|
137
|
+
@order.should_receive(:touch).with(enum.first).ordered
|
138
|
+
enum.reverse_each{ |o| o.should_receive(:touch).once }
|
139
|
+
@mutex = Mutex.new
|
140
|
+
enum.in_threads.reverse_each do |o|
|
141
|
+
@mutex.synchronize{ @order.touch(o) }
|
142
|
+
o.touch_n_value
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should run faster with threads" do
|
147
|
+
measure{ enum.in_threads.reverse_each(&:value) }.should < measure{ enum.reverse_each(&:value) } * speed_coef
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should return same enum without block" do
|
151
|
+
enum.in_threads.reverse_each.to_a.should == enum.reverse_each.to_a
|
152
|
+
end
|
42
153
|
end
|
43
154
|
|
44
|
-
|
45
|
-
|
155
|
+
%w[
|
156
|
+
all? any? none? one?
|
157
|
+
detect find find_index drop_while take_while
|
158
|
+
].each do |method|
|
159
|
+
describe method do
|
160
|
+
let(:enum){ 100.times.map{ |i| ValueItem.new(i, i % 2 == 1) } }
|
161
|
+
|
162
|
+
it "should return same result with threads" do
|
163
|
+
enum.in_threads.send(method, &:check?).should == enum.send(method, &:check?)
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should fire same objects but not all" do
|
167
|
+
a = []
|
168
|
+
enum.send(method) do |o|
|
169
|
+
a << o
|
170
|
+
o.check?
|
171
|
+
end
|
172
|
+
|
173
|
+
@a = []
|
174
|
+
@mutex = Mutex.new
|
175
|
+
enum.in_threads.send(method){ |o| @mutex.synchronize{ @a << o }; o.check? }
|
176
|
+
|
177
|
+
@a.length.should >= a.length
|
178
|
+
@a.length.should <= enum.length / 2
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should run faster with threads" do
|
182
|
+
value = %w[all? drop_while take_while].include?(method)
|
183
|
+
enum = 30.times.map{ |i| ValueItem.new(i, value) }
|
184
|
+
measure{ enum.in_threads.send(method, &:check?) }.should < measure{ enum.send(method, &:check?) } * speed_coef
|
185
|
+
end
|
186
|
+
end
|
46
187
|
end
|
47
188
|
|
48
|
-
%w
|
49
|
-
|
50
|
-
|
189
|
+
%w[partition find_all select reject count].each do |method|
|
190
|
+
describe method do
|
191
|
+
it "should return same result with threads" do
|
192
|
+
enum.in_threads.send(method, &:check?).should == enum.send(method, &:check?)
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should fire same objects" do
|
196
|
+
enum.send(method){ |o| o.should_receive(:touch).once }
|
197
|
+
enum.in_threads.send(method, &:touch_n_check?)
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should run faster with threads" do
|
201
|
+
measure{ enum.in_threads.send(method, &:check?) }.should < measure{ enum.send(method, &:check?) } * speed_coef
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
%w[collect map group_by max_by min_by minmax_by sort_by].each do |method|
|
207
|
+
describe method do
|
208
|
+
it "should return same result with threads" do
|
209
|
+
enum.in_threads.send(method, &:value).should == enum.send(method, &:value)
|
210
|
+
end
|
211
|
+
|
212
|
+
it "should fire same objects" do
|
213
|
+
enum.send(method){ |o| o.should_receive(:touch).once; 0 }
|
214
|
+
enum.in_threads.send(method, &:touch_n_value)
|
215
|
+
end
|
216
|
+
|
217
|
+
it "should run faster with threads" do
|
218
|
+
measure{ enum.in_threads.send(method, &:value) }.should < measure{ enum.send(method, &:value) } * speed_coef
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
enum_methods(%w[each_cons each_slice enum_slice enum_cons]).each do |method|
|
224
|
+
describe method do
|
225
|
+
let(:runner){ proc{ |a| a.each(&:value) } }
|
226
|
+
|
227
|
+
it "should fire same objects" do
|
228
|
+
enum.send(method, 3){ |a| a.first.should_receive(:touch).with(a).once }
|
229
|
+
enum.in_threads.send(method, 3){ |a| a.first.touch_n_value(a) }
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should return same with block" do
|
233
|
+
enum.in_threads.send(method, 3, &runner).should == enum.send(method, 3, &runner)
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should run faster with threads" do
|
237
|
+
measure{ enum.in_threads.send(method, 3, &runner) }.should < measure{ enum.send(method, 3, &runner) } * speed_coef
|
238
|
+
end
|
239
|
+
|
240
|
+
it "should return same without block" do
|
241
|
+
enum.in_threads.send(method, 3).to_a.should == enum.send(method, 3).to_a
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
describe "zip" do
|
247
|
+
let(:runner){ proc{ |a| a.each(&:value) } }
|
248
|
+
|
249
|
+
it "should fire same objects" do
|
250
|
+
enum.zip(enum, enum){ |a| a.first.should_receive(:touch).with(a).once }
|
251
|
+
enum.in_threads.zip(enum, enum){ |a| a.first.touch_n_value(a) }
|
252
|
+
end
|
253
|
+
|
254
|
+
it "should return same with block" do
|
255
|
+
enum.in_threads.zip(enum, enum, &runner).should == enum.zip(enum, enum, &runner)
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should run faster with threads" do
|
259
|
+
measure{ enum.in_threads.zip(enum, enum, &runner) }.should < measure{ enum.zip(enum, enum, &runner) } * speed_coef
|
260
|
+
end
|
261
|
+
|
262
|
+
it "should return same without block" do
|
263
|
+
enum.in_threads.zip(enum, enum).should == enum.zip(enum, enum)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
describe "cycle" do
|
268
|
+
it "should fire same objects" do
|
269
|
+
enum.cycle(1){ |o| o.should_receive(:touch).exactly(3).times }
|
270
|
+
enum.in_threads.cycle(3, &:touch_n_value)
|
271
|
+
end
|
272
|
+
|
273
|
+
it "should run faster with threads" do
|
274
|
+
measure{ enum.in_threads.cycle(3, &:work) }.should < measure{ enum.cycle(3, &:work) } * speed_coef
|
275
|
+
end
|
276
|
+
|
277
|
+
it "should return same enum without block" do
|
278
|
+
enum.in_threads.cycle(3).to_a.should == enum.cycle(3).to_a
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
describe "grep" do
|
283
|
+
let(:matcher){ Item::MiddleMatcher.new }
|
284
|
+
|
285
|
+
it "should fire same objects" do
|
286
|
+
enum.each{ |o| o.should_receive(:touch).exactly(matcher === o ? 1 : 0).times }
|
287
|
+
enum.in_threads.grep(matcher, &:touch_n_value)
|
288
|
+
end
|
289
|
+
|
290
|
+
it "should return same with block" do
|
291
|
+
enum.in_threads.grep(matcher, &:value).should == enum.grep(matcher, &:value)
|
292
|
+
end
|
293
|
+
|
294
|
+
it "should run faster with threads" do
|
295
|
+
measure{ enum.in_threads.grep(matcher, &:value) }.should < measure{ enum.grep(matcher, &:value) } * speed_coef
|
296
|
+
end
|
297
|
+
|
298
|
+
it "should return same without block" do
|
299
|
+
enum.in_threads.grep(matcher).should == enum.grep(matcher)
|
51
300
|
end
|
52
301
|
end
|
53
302
|
|
54
|
-
|
55
|
-
|
303
|
+
context "unthreaded" do
|
304
|
+
%w[inject reduce].each do |method|
|
305
|
+
describe method do
|
306
|
+
it "should return same result" do
|
307
|
+
combiner = proc{ |memo, o| memo + o.value }
|
308
|
+
enum.in_threads.send(method, 0, &combiner).should == enum.send(method, 0, &combiner)
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
%w[max min minmax sort].each do |method|
|
314
|
+
describe method do
|
315
|
+
it "should return same result" do
|
316
|
+
comparer = proc{ |a, b| a.value <=> b.value }
|
317
|
+
enum.in_threads.send(method, &comparer).should == enum.send(method, &comparer)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
%w[to_a entries].each do |method|
|
323
|
+
describe method do
|
324
|
+
it "should return same result" do
|
325
|
+
enum.in_threads.send(method).should == enum.send(method)
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
%w[drop take].each do |method|
|
331
|
+
describe method do
|
332
|
+
it "should return same result" do
|
333
|
+
enum.in_threads.send(method, 2).should == enum.send(method, 2)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
%w[first].each do |method|
|
339
|
+
describe method do
|
340
|
+
it "should return same result" do
|
341
|
+
enum.in_threads.send(method).should == enum.send(method)
|
342
|
+
enum.in_threads.send(method, 3).should == enum.send(method, 3)
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
%w[include? member?].each do |method|
|
348
|
+
describe method do
|
349
|
+
it "should return same result" do
|
350
|
+
enum.in_threads.send(method, enum[10]).should == enum.send(method, enum[10])
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
56
354
|
end
|
57
355
|
end
|
metadata
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
name: in_threads
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
hash: 23
|
5
|
-
prerelease:
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
|
+
- 1
|
7
8
|
- 0
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 0.0.4
|
10
|
+
version: 1.0.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ivan Kuchin
|
@@ -15,43 +15,12 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
19
|
-
default_executable:
|
18
|
+
date: 2011-12-05 00:00:00 Z
|
20
19
|
dependencies:
|
21
|
-
- !ruby/object:Gem::Dependency
|
22
|
-
name: jeweler
|
23
|
-
prerelease: false
|
24
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ~>
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
hash: 1
|
30
|
-
segments:
|
31
|
-
- 1
|
32
|
-
- 5
|
33
|
-
- 1
|
34
|
-
version: 1.5.1
|
35
|
-
type: :development
|
36
|
-
version_requirements: *id001
|
37
|
-
- !ruby/object:Gem::Dependency
|
38
|
-
name: rake-gem-ghost
|
39
|
-
prerelease: false
|
40
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
|
-
requirements:
|
43
|
-
- - ">="
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
hash: 3
|
46
|
-
segments:
|
47
|
-
- 0
|
48
|
-
version: "0"
|
49
|
-
type: :development
|
50
|
-
version_requirements: *id002
|
51
20
|
- !ruby/object:Gem::Dependency
|
52
21
|
name: rspec
|
53
22
|
prerelease: false
|
54
|
-
requirement: &
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
55
24
|
none: false
|
56
25
|
requirements:
|
57
26
|
- - ">="
|
@@ -61,27 +30,23 @@ dependencies:
|
|
61
30
|
- 0
|
62
31
|
version: "0"
|
63
32
|
type: :development
|
64
|
-
version_requirements: *
|
33
|
+
version_requirements: *id001
|
65
34
|
description:
|
66
35
|
email:
|
67
36
|
executables: []
|
68
37
|
|
69
38
|
extensions: []
|
70
39
|
|
71
|
-
extra_rdoc_files:
|
72
|
-
|
73
|
-
- README.markdown
|
40
|
+
extra_rdoc_files: []
|
41
|
+
|
74
42
|
files:
|
75
|
-
- .
|
43
|
+
- .gitignore
|
76
44
|
- LICENSE.txt
|
77
45
|
- README.markdown
|
78
|
-
- Rakefile
|
79
|
-
- VERSION
|
80
46
|
- in_threads.gemspec
|
81
47
|
- lib/in_threads.rb
|
82
48
|
- spec/in_threads_spec.rb
|
83
49
|
- spec/spec_helper.rb
|
84
|
-
has_rdoc: true
|
85
50
|
homepage: http://github.com/toy/in_threads
|
86
51
|
licenses:
|
87
52
|
- MIT
|
@@ -110,11 +75,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
110
75
|
version: "0"
|
111
76
|
requirements: []
|
112
77
|
|
113
|
-
rubyforge_project:
|
114
|
-
rubygems_version: 1.
|
78
|
+
rubyforge_project: in_threads
|
79
|
+
rubygems_version: 1.8.11
|
115
80
|
signing_key:
|
116
81
|
specification_version: 3
|
117
|
-
summary: Execute ruby
|
82
|
+
summary: Execute ruby code in parallel
|
118
83
|
test_files:
|
119
84
|
- spec/in_threads_spec.rb
|
120
85
|
- spec/spec_helper.rb
|
86
|
+
has_rdoc:
|
data/.tmignore
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
/*.gemspec
|
data/Rakefile
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'rake'
|
2
|
-
require 'jeweler'
|
3
|
-
require 'rake/gem_ghost_task'
|
4
|
-
require 'rspec/core/rake_task'
|
5
|
-
|
6
|
-
name = 'in_threads'
|
7
|
-
|
8
|
-
Jeweler::Tasks.new do |gem|
|
9
|
-
gem.name = name
|
10
|
-
gem.summary = %Q{Execute ruby blocks in parallel}
|
11
|
-
gem.homepage = "http://github.com/toy/#{name}"
|
12
|
-
gem.license = 'MIT'
|
13
|
-
gem.authors = ['Ivan Kuchin']
|
14
|
-
gem.add_development_dependency 'jeweler', '~> 1.5.1'
|
15
|
-
gem.add_development_dependency 'rake-gem-ghost'
|
16
|
-
gem.add_development_dependency 'rspec'
|
17
|
-
end
|
18
|
-
Jeweler::RubygemsDotOrgTasks.new
|
19
|
-
Rake::GemGhostTask.new
|
20
|
-
|
21
|
-
RSpec::Core::RakeTask.new(:spec) do |spec|
|
22
|
-
spec.rspec_opts = ['--colour --format progress']
|
23
|
-
spec.pattern = 'spec/**/*_spec.rb'
|
24
|
-
end
|
25
|
-
task :default => :spec
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.0.4
|