contrails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +15 -0
- data/Gemfile.lock +41 -0
- data/README.markdown +29 -0
- data/Rakefile +97 -0
- data/lib/contrails/chainable.rb +30 -0
- data/lib/contrails/helpers.rb +9 -0
- data/lib/contrails/parallel.rb +44 -0
- data/lib/contrails/process.rb +32 -0
- data/lib/contrails/semaphore.rb +23 -0
- data/lib/contrails/utils.rb +16 -0
- data/lib/contrails.rb +5 -0
- data/spec/process_spec.rb +167 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/utils_spec.rb +40 -0
- metadata +94 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
archive-tar-minitar (0.5.2)
|
5
|
+
columnize (0.3.4)
|
6
|
+
diff-lcs (1.1.2)
|
7
|
+
eventmachine (0.12.10)
|
8
|
+
linecache19 (0.5.12)
|
9
|
+
ruby_core_source (>= 0.1.4)
|
10
|
+
mocha (0.9.12)
|
11
|
+
rake (0.8.7)
|
12
|
+
rantly (0.3.0)
|
13
|
+
rspec (2.6.0)
|
14
|
+
rspec-core (~> 2.6.0)
|
15
|
+
rspec-expectations (~> 2.6.0)
|
16
|
+
rspec-mocks (~> 2.6.0)
|
17
|
+
rspec-core (2.6.4)
|
18
|
+
rspec-expectations (2.6.0)
|
19
|
+
diff-lcs (~> 1.1.2)
|
20
|
+
rspec-mocks (2.6.0)
|
21
|
+
ruby-debug-base19 (0.11.25)
|
22
|
+
columnize (>= 0.3.1)
|
23
|
+
linecache19 (>= 0.5.11)
|
24
|
+
ruby_core_source (>= 0.1.4)
|
25
|
+
ruby-debug19 (0.11.6)
|
26
|
+
columnize (>= 0.3.1)
|
27
|
+
linecache19 (>= 0.5.11)
|
28
|
+
ruby-debug-base19 (>= 0.11.19)
|
29
|
+
ruby_core_source (0.1.5)
|
30
|
+
archive-tar-minitar (>= 0.5.2)
|
31
|
+
|
32
|
+
PLATFORMS
|
33
|
+
ruby
|
34
|
+
|
35
|
+
DEPENDENCIES
|
36
|
+
eventmachine
|
37
|
+
mocha
|
38
|
+
rake
|
39
|
+
rantly
|
40
|
+
rspec
|
41
|
+
ruby-debug19
|
data/README.markdown
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
Contrails - declarative concurrency for EventMachine
|
2
|
+
====================================================
|
3
|
+
|
4
|
+
|
5
|
+
![Contrails](http://github.com/likely/contrails/raw/master/contrib/contrails.jpg)
|
6
|
+
|
7
|
+
Contrails is a lightweight DSL that allows concurrent processes to be specified using an intuitive, declarative syntax for execution by EventMachine. It consists of a process class that wraps a block, and can then be chained with other processes in series using the `>>` operator, and composed into a single process that executes its constituent processes in parallel using the `*` operator. A trivial example:
|
8
|
+
|
9
|
+
Contrails::Process.new { get_an_integer_from_somewhere } >> Contrails.Process.new {|x| x*2 } * {Contrails::Process.new {|x| x * 3} >> Contrails::Process.new {|x,y| x+y }
|
10
|
+
|
11
|
+
This takes a number from some input, computes its multiplication by two and three in parallel, then sums both values once both computations have finished.
|
12
|
+
|
13
|
+
There's still rather a lot of unsightly syntax there, right? Perhaps even more so than before. However, if you import the Contrails::Utils module, you'll get a few other useful methods:
|
14
|
+
|
15
|
+
* the 'trails' method is an alias for Contrails::Process.new
|
16
|
+
|
17
|
+
`trail { get_integer } >> trail { |x| x * 3} * trail { |x| x * 2 } >> trail {|x, y| x+y }`
|
18
|
+
|
19
|
+
* the 'seq' method sequences all its arguments
|
20
|
+
|
21
|
+
`seq(trail, trail2, trail3) == trail >> trail2 >> trail3`
|
22
|
+
|
23
|
+
* the 'par' method composes all its arguments in parallel
|
24
|
+
|
25
|
+
`par(trail, trail2, trail3) == trail * trail2 * trail3`
|
26
|
+
|
27
|
+
Enjoy! Questions, comments, feature requests and patches always welcome.
|
28
|
+
|
29
|
+
(Photo by [FrancoisRoche](http://www.flickr.com/photos/francoisroche/2563417399/) on flickr, Licence: CC BY-SA)
|
data/Rakefile
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
|
5
|
+
task :default => :test
|
6
|
+
task :test => :spec
|
7
|
+
|
8
|
+
if !defined?(RSpec)
|
9
|
+
puts "spec targets require RSpec"
|
10
|
+
else
|
11
|
+
desc "Run all examples"
|
12
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
13
|
+
t.pattern = 'spec/**/*_spec.rb'
|
14
|
+
t.rspec_opts = ['-cfs']
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
require "rubygems"
|
20
|
+
require "rubygems/package_task"
|
21
|
+
require "rdoc/task"
|
22
|
+
|
23
|
+
require "rspec"
|
24
|
+
require "rspec/core/rake_task"
|
25
|
+
RSpec::Core::RakeTask.new do |t|
|
26
|
+
t.rspec_opts = %w(--format documentation --colour)
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
task :default => ["spec"]
|
31
|
+
|
32
|
+
# This builds the actual gem. For details of what all these options
|
33
|
+
# mean, and other ones you can add, check the documentation here:
|
34
|
+
#
|
35
|
+
# http://rubygems.org/read/chapter/20
|
36
|
+
#
|
37
|
+
spec = Gem::Specification.new do |s|
|
38
|
+
|
39
|
+
# Change these as appropriate
|
40
|
+
s.name = "contrails"
|
41
|
+
s.version = "0.1.0"
|
42
|
+
s.summary = "Declarative concurrency for EventMachine"
|
43
|
+
s.author = "Tim Cowlishaw"
|
44
|
+
s.email = "tim@timcowlishaw.co.uk"
|
45
|
+
s.homepage = "http://github.com/likely/contrails"
|
46
|
+
|
47
|
+
s.has_rdoc = true
|
48
|
+
s.extra_rdoc_files = %w(README.markdown)
|
49
|
+
s.rdoc_options = %w(--main README.markdown)
|
50
|
+
|
51
|
+
# Add any extra files to include in the gem
|
52
|
+
s.files = %w(Gemfile.lock Rakefile README.markdown Gemfile) + Dir.glob("{spec,lib}/**/*")
|
53
|
+
s.require_paths = ["lib"]
|
54
|
+
|
55
|
+
# If you want to depend on other gems, add them here, along with any
|
56
|
+
# relevant versions
|
57
|
+
s.add_dependency("eventmachine")
|
58
|
+
|
59
|
+
# If your tests use any gems, include them here
|
60
|
+
s.add_development_dependency("rspec")
|
61
|
+
end
|
62
|
+
|
63
|
+
# This task actually builds the gem. We also regenerate a static
|
64
|
+
# .gemspec file, which is useful if something (i.e. GitHub) will
|
65
|
+
# be automatically building a gem for this project. If you're not
|
66
|
+
# using GitHub, edit as appropriate.
|
67
|
+
#
|
68
|
+
# To publish your gem online, install the 'gemcutter' gem; Read more
|
69
|
+
# about that here: http://gemcutter.org/pages/gem_docs
|
70
|
+
Gem::PackageTask.new(spec) do |pkg|
|
71
|
+
pkg.gem_spec = spec
|
72
|
+
end
|
73
|
+
|
74
|
+
desc "Build the gemspec file #{spec.name}.gemspec"
|
75
|
+
task :gemspec do
|
76
|
+
file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
|
77
|
+
File.open(file, "w") {|f| f << spec.to_ruby }
|
78
|
+
end
|
79
|
+
|
80
|
+
# If you don't want to generate the .gemspec file, just remove this line. Reasons
|
81
|
+
# why you might want to generate a gemspec:
|
82
|
+
# - using bundler with a git source
|
83
|
+
# - building the gem without rake (i.e. gem build blah.gemspec)
|
84
|
+
# - maybe others?
|
85
|
+
task :package => :gemspec
|
86
|
+
|
87
|
+
# Generate documentation
|
88
|
+
RDoc::Task.new do |rd|
|
89
|
+
rd.main = "README.markdown"
|
90
|
+
rd.rdoc_files.include("README.markdown", "lib/**/*.rb")
|
91
|
+
rd.rdoc_dir = "rdoc"
|
92
|
+
end
|
93
|
+
|
94
|
+
desc 'Clear out RDoc and generated packages'
|
95
|
+
task :clean => [:clobber_rdoc, :clobber_package] do
|
96
|
+
rm "#{spec.name}.gemspec"
|
97
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Contrails
|
2
|
+
module Chainable
|
3
|
+
|
4
|
+
def bind(other)
|
5
|
+
raise NotImplementedError
|
6
|
+
end
|
7
|
+
|
8
|
+
def distribute(other)
|
9
|
+
raise NotImplementedError
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(*a)
|
13
|
+
raise NotImplementedError
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_proc
|
17
|
+
lambda {|*a| self.call(*a) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def >>(other)
|
21
|
+
self.bind(other)
|
22
|
+
end
|
23
|
+
|
24
|
+
def *(other)
|
25
|
+
self.distribute(other)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'em/deferrable'
|
2
|
+
require 'contrails/chainable'
|
3
|
+
require 'contrails/semaphore'
|
4
|
+
module Contrails
|
5
|
+
class Parallel
|
6
|
+
include EventMachine::Deferrable
|
7
|
+
include Contrails::Chainable
|
8
|
+
def initialize(*procs)
|
9
|
+
results = []
|
10
|
+
@procs = procs
|
11
|
+
@semaphore = Semaphore.new(@procs.length, results)
|
12
|
+
ps = @procs.map.with_index do |p, i|
|
13
|
+
p = p.dup
|
14
|
+
p.callback {|*r| results[i] = r; @semaphore.signal}
|
15
|
+
p
|
16
|
+
end
|
17
|
+
internal_callback { |*a|
|
18
|
+
ps.each {|p| EM.next_tick(lambda { p.call(*a) }) }
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
alias_method :internal_callback, :callback
|
23
|
+
private :internal_callback
|
24
|
+
|
25
|
+
def callback(&b)
|
26
|
+
@semaphore.callback(&b)
|
27
|
+
end
|
28
|
+
|
29
|
+
def bind(other)
|
30
|
+
@semaphore.callback(&other)
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def distribute(other)
|
35
|
+
Contrails::Parallel.new(*(@procs + [other]))
|
36
|
+
end
|
37
|
+
|
38
|
+
def call(*a)
|
39
|
+
self.succeed(*a)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'em/deferrable'
|
2
|
+
require 'contrails/parallel'
|
3
|
+
module Contrails
|
4
|
+
class Process
|
5
|
+
include EventMachine::Deferrable
|
6
|
+
include Contrails::Chainable
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def return(&b)
|
10
|
+
self.new(&b)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(&l)
|
15
|
+
@lambda = l
|
16
|
+
end
|
17
|
+
|
18
|
+
def bind(other)
|
19
|
+
p = Contrails::Process.new(&self)
|
20
|
+
p.callback(&other)
|
21
|
+
return p
|
22
|
+
end
|
23
|
+
|
24
|
+
def distribute(other)
|
25
|
+
Contrails::Parallel.new(self, other)
|
26
|
+
end
|
27
|
+
|
28
|
+
def call(*a)
|
29
|
+
self.succeed(*@lambda.call(*a))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'em/deferrable'
|
2
|
+
require 'contrails/helpers'
|
3
|
+
module Contrails
|
4
|
+
class Semaphore
|
5
|
+
|
6
|
+
include EventMachine::Deferrable
|
7
|
+
|
8
|
+
def initialize(n, results)
|
9
|
+
@value = n
|
10
|
+
@results = results
|
11
|
+
@mutex = Mutex.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def signal
|
15
|
+
@mutex.synchronize do
|
16
|
+
if(@value -= 1) == 0
|
17
|
+
self.succeed(*Contrails::Helpers.unwrap_array(@results))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'contrails/process'
|
2
|
+
module Contrails
|
3
|
+
module Utils
|
4
|
+
def trail(&block)
|
5
|
+
return Contrails::Process.new(&block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def seq(*ps)
|
9
|
+
return ps.inject {|m,n| m.bind(n) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def par(*ps)
|
13
|
+
return ps.inject {|m,n| m.distribute(n)}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/contrails.rb
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Contrails::Process do
|
4
|
+
describe "class methods:" do
|
5
|
+
|
6
|
+
describe "new" do
|
7
|
+
it "wraps the passed lambda in a process" do
|
8
|
+
for_all do
|
9
|
+
int = integer
|
10
|
+
async_assertion(Contrails::Process.new { |x| x**2 }, int) { |result|
|
11
|
+
result.should == int**2
|
12
|
+
}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "return" do
|
18
|
+
it "delegates to the constructor method" do
|
19
|
+
lamb = lambda {|x, y| x + y }
|
20
|
+
Contrails::Process.expects(:new) #sadly we can't test for block arguments with mocha
|
21
|
+
Contrails::Process.return(&lamb)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "instance methods:" do
|
27
|
+
describe "bind" do
|
28
|
+
it "returns a process which sequences the second operation after the first" do
|
29
|
+
for_all do
|
30
|
+
context = mock
|
31
|
+
context.expects(:first).in_sequence
|
32
|
+
context.expects(:second).in_sequence
|
33
|
+
proc1 = Contrails::Process.new { context.first }
|
34
|
+
proc2 = Contrails::Process.new { context.second }
|
35
|
+
async_assertion(proc1.bind(proc2))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
it "successively calls other processes bound onto the argument" do
|
39
|
+
for_all do
|
40
|
+
context = mock
|
41
|
+
context.expects(:first).in_sequence
|
42
|
+
context.expects(:second).in_sequence
|
43
|
+
context.expects(:third).in_sequence
|
44
|
+
proc1 = Contrails::Process.new { context.first }
|
45
|
+
proc2 = Contrails::Process.new { context.second }
|
46
|
+
proc3 = Contrails::Process.new { context.third }
|
47
|
+
async_assertion(proc1.bind(proc2).bind(proc3))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it "passes the return value of each process to its successors" do
|
52
|
+
for_all do
|
53
|
+
int= integer
|
54
|
+
proc1 = Contrails::Process.new { |x| x.should == int; x+1}
|
55
|
+
proc2 = Contrails::Process.new { |x| x.should == int+1; x+1}
|
56
|
+
proc3 = Contrails::Process.new { |x| x.should == int+2; x+1}
|
57
|
+
async_assertion(proc1.bind(proc2).bind(proc3), int) {|x| x.should == int+3}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "distribute" do
|
63
|
+
|
64
|
+
it "returns a process that executes both processes, yielding the results of both as a pair" do
|
65
|
+
for_all {
|
66
|
+
int = integer
|
67
|
+
proc1 = Contrails::Process.new {|x| x*2}
|
68
|
+
proc2 = Contrails::Process.new {|x| x*3}
|
69
|
+
async_assertion(proc1.distribute(proc2), int) {|x1, x2|
|
70
|
+
x1.should == int*2
|
71
|
+
x2.should == int*3
|
72
|
+
}
|
73
|
+
}
|
74
|
+
end
|
75
|
+
it "yields the return value of both to subsequently bound processes" do
|
76
|
+
for_all {
|
77
|
+
int = integer
|
78
|
+
proc1 = Contrails::Process.new {|x| x*2 }
|
79
|
+
proc2 = Contrails::Process.new {|x| x*3 }
|
80
|
+
proc3 = Contrails::Process.new {|x,y| x + y }
|
81
|
+
async_assertion(proc1.distribute(proc2).bind(proc3), int) {|r|
|
82
|
+
r.should == int*2 + int*3
|
83
|
+
}
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
it "executes both processes in parallel" do
|
88
|
+
for_all {
|
89
|
+
time = float
|
90
|
+
guard time > 0.05
|
91
|
+
guard time < 0.25
|
92
|
+
context = mock
|
93
|
+
context.expects(:first).in_sequence
|
94
|
+
context.expects(:second).in_sequence
|
95
|
+
proc1 = Contrails::Process.new { sleep(time); context.second }
|
96
|
+
proc2 = Contrails::Process.new { context.first }
|
97
|
+
async_assertion(proc1.distribute(proc2))
|
98
|
+
}
|
99
|
+
end
|
100
|
+
it "executes any subsequently distributed processes in parallel" do
|
101
|
+
for_all {
|
102
|
+
time = float
|
103
|
+
guard time > 0.05
|
104
|
+
guard time < 0.25
|
105
|
+
context = mock
|
106
|
+
context.expects(:first).in_sequence
|
107
|
+
context.expects(:second).in_sequence
|
108
|
+
context.expects(:third).in_sequence
|
109
|
+
proc1 = Contrails::Process.new { sleep(time); context.third }
|
110
|
+
proc2 = Contrails::Process.new { sleep(time/2); context.second }
|
111
|
+
proc3 = Contrails::Process.new { context.first }
|
112
|
+
async_assertion(proc1.distribute(proc2).distribute(proc3))
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
it "collects the results of all processes into a single list of arguments to its callback" do
|
117
|
+
for_all {
|
118
|
+
int = integer
|
119
|
+
proc1 = Contrails::Process.new {|x| x+1 }
|
120
|
+
proc2 = Contrails::Process.new {|x| x+2 }
|
121
|
+
proc3 = Contrails::Process.new {|x| x+3 }
|
122
|
+
async_assertion(proc1.distribute(proc2).distribute(proc3), int) {|a|
|
123
|
+
a.should == [int+1, int+2, int+3]
|
124
|
+
}
|
125
|
+
}
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "call" do
|
130
|
+
it "calls the lambda and sets itself as succeeded, passing the blocks return value" do
|
131
|
+
for_all do
|
132
|
+
int = integer
|
133
|
+
async_assertion(Contrails::Process.new { |x| x + 2}, int) { |result|
|
134
|
+
result.should == int + 2
|
135
|
+
}
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "to proc" do
|
141
|
+
it "returns a lambda which wraps the computation" do
|
142
|
+
p = Contrails::Process.new { 1+2 }
|
143
|
+
p.expects(:call)
|
144
|
+
p.to_proc.should be_a(Proc)
|
145
|
+
p.to_proc.call
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe ">>" do
|
150
|
+
it "delegates to the bind method" do
|
151
|
+
proc1 = Contrails::Process.new {|x| x**2}
|
152
|
+
proc2 = Contrails::Process.new {|x| x+1 }
|
153
|
+
proc1.expects(:bind).with(proc2)
|
154
|
+
proc1 >> proc2
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe "*" do
|
159
|
+
it "delegates to the distribute method" do
|
160
|
+
proc1 = Contrails::Process.new {|x| x**2}
|
161
|
+
proc2 = Contrails::Process.new {|x| x+1 }
|
162
|
+
proc1.expects(:distribute).with(proc2)
|
163
|
+
proc1 * proc2
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
$: << File.join(File.dirname(__FILE__),"..")
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'rspec'
|
5
|
+
require 'rantly'
|
6
|
+
require 'mocha'
|
7
|
+
require 'eventmachine'
|
8
|
+
require 'em/deferrable'
|
9
|
+
require 'lib/contrails'
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.mock_framework = :mocha
|
12
|
+
end
|
13
|
+
|
14
|
+
Rantly.send(:include, Mocha::API)
|
15
|
+
|
16
|
+
def for_all(&block)
|
17
|
+
Rantly.each(ENV['RANTLY_LIMIT'] ? ENV['RANTLY_LIMIT'].to_i : 100, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
class DeferrableTest
|
21
|
+
include EventMachine::Deferrable
|
22
|
+
def initialize(*a, &b)
|
23
|
+
@args = a
|
24
|
+
@lambda = b
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_proc
|
28
|
+
lambda { succeed(*@lambda.call(*@args)) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def async_assertion(process, *args, &callback)
|
33
|
+
EM.run_block do
|
34
|
+
deferrable = DeferrableTest.new(*args, &process)
|
35
|
+
deferrable.callback(&callback) if callback
|
36
|
+
EM.defer(&deferrable)
|
37
|
+
end
|
38
|
+
end
|
data/spec/utils_spec.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class TestImplementation
|
3
|
+
include Contrails::Utils
|
4
|
+
end
|
5
|
+
|
6
|
+
describe Contrails::Utils do
|
7
|
+
before(:all) do
|
8
|
+
@ti = TestImplementation.new
|
9
|
+
end
|
10
|
+
describe "trail" do
|
11
|
+
it "creates a new process with the passed block" do
|
12
|
+
Contrails::Process.expects(:new)
|
13
|
+
@ti.trail {|x| x+1 }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "seq" do
|
18
|
+
it "binds together the supplied processes" do
|
19
|
+
p1 = mock
|
20
|
+
p2 = mock
|
21
|
+
p3 = mock
|
22
|
+
intermediate = mock
|
23
|
+
p1.expects(:bind).with(p2).returns(intermediate)
|
24
|
+
intermediate.expects(:bind).with(p3)
|
25
|
+
@ti.seq(p1,p2,p3)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "par" do
|
30
|
+
it "distributes between the supplied processes" do
|
31
|
+
p1 = mock
|
32
|
+
p2 = mock
|
33
|
+
p3 = mock
|
34
|
+
intermediate = mock
|
35
|
+
p1.expects(:distribute).with(p2).returns(intermediate)
|
36
|
+
intermediate.expects(:distribute).with(p3)
|
37
|
+
@ti.par(p1,p2,p3)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: contrails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tim Cowlishaw
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-08-19 00:00:00 +01:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: eventmachine
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: *id002
|
38
|
+
description:
|
39
|
+
email: tim@timcowlishaw.co.uk
|
40
|
+
executables: []
|
41
|
+
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
extra_rdoc_files:
|
45
|
+
- README.markdown
|
46
|
+
files:
|
47
|
+
- Gemfile.lock
|
48
|
+
- Rakefile
|
49
|
+
- README.markdown
|
50
|
+
- Gemfile
|
51
|
+
- spec/utils_spec.rb
|
52
|
+
- spec/spec_helper.rb
|
53
|
+
- spec/process_spec.rb
|
54
|
+
- lib/contrails/process.rb
|
55
|
+
- lib/contrails/chainable.rb
|
56
|
+
- lib/contrails/parallel.rb
|
57
|
+
- lib/contrails/utils.rb
|
58
|
+
- lib/contrails/semaphore.rb
|
59
|
+
- lib/contrails/helpers.rb
|
60
|
+
- lib/contrails.rb
|
61
|
+
has_rdoc: true
|
62
|
+
homepage: http://github.com/likely/contrails
|
63
|
+
licenses: []
|
64
|
+
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options:
|
67
|
+
- --main
|
68
|
+
- README.markdown
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: -1260005474418307725
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
version: "0"
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: "0"
|
86
|
+
requirements: []
|
87
|
+
|
88
|
+
rubyforge_project:
|
89
|
+
rubygems_version: 1.5.2
|
90
|
+
signing_key:
|
91
|
+
specification_version: 3
|
92
|
+
summary: Declarative concurrency for EventMachine
|
93
|
+
test_files: []
|
94
|
+
|