contrails 0.1.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/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
|
+

|
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
|
+
|