orchestrated 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +32 -23
- data/lib/orchestrated/base.rb +22 -19
- data/lib/orchestrated/object.rb +1 -1
- data/lib/orchestrated/version.rb +1 -1
- data/spec/unit/cancellation_spec.rb +7 -7
- data/spec/unit/completion_spec.rb +11 -11
- data/spec/unit/failure_spec.rb +1 -1
- data/spec/unit/orchestrated_spec.rb +8 -8
- data/spec/unit/static_analysis_spec.rb +3 -3
- metadata +2 -2
data/README.markdown
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
Orchestrated
|
2
|
-
|
1
|
+
[Orchestrated](https://github.com/paydici/orchestrated)
|
2
|
+
=======================================================
|
3
3
|
|
4
4
|
The [delayed_job](https://github.com/collectiveidea/delayed_job) Ruby Gem provides a restartable queuing system for Ruby. It implements an elegant API for delaying execution of any Ruby object method invocation. Not only is the message delivery delayed in time, it is potentially shifted in space too. By shifting in space, i.e. running in a different virtual machine, possibly on a separate computer, multiple CPUs can be brought to bear on a computing problem.
|
5
5
|
|
@@ -10,13 +10,13 @@ Queuing works well for simple, independent tasks. By simple we mean the task can
|
|
10
10
|
1. pipelined (multi-step) generation of a complex PDF document
|
11
11
|
2. an extract/transfer/load (ETL) job that must acquire data from source systems, transform it and load it into the target system
|
12
12
|
|
13
|
-
If we would like to scale these compound operations, breaking them into smaller parts, and managing the execution of those parts across many computers, we need an "orchestrator". This project implements just such a framework, called "Orchestrated".
|
13
|
+
If we would like to scale these compound operations, breaking them into smaller parts, and managing the execution of those parts across many computers, we need an "orchestrator". This project implements just such a framework, called "[Orchestrated](https://github.com/paydici/orchestrated)".
|
14
14
|
|
15
|
-
Orchestrated introduces the ```acts_as_orchestrated``` Object class method. When invoked on your class, this will define the ```
|
15
|
+
[Orchestrated](https://github.com/paydici/orchestrated) introduces the ```acts_as_orchestrated``` Object class method. When invoked on your class, this will define the ```orchestrate``` instance method. You use ```orchestrate``` in a mannner similar to [delayed_job](https://github.com/collectiveidea/delayed_job)'s ```delay```—the difference being that ```orchestrate``` takes a parameter that lets you specify dependencies between your jobs.
|
16
16
|
|
17
17
|
The reason we refer to [delayed_job](https://github.com/collectiveidea/delayed_job) as a restartable queueing system is because, even if computers (database host, worker hosts) in the cluster crash, the work on the queues progresses. If no worker is servicing a particular queue, then work accumulates there. Once workers are available, they consume the jobs. This is a resilient architecture.
|
18
18
|
|
19
|
-
With Orchestrated you can create restartable workflows, a workflow consisting of one or more dependent, queueable, tasks. This means that your workflows will continue to make progress even in the face of database and (queue) worker crashes.
|
19
|
+
With [Orchestrated](https://github.com/paydici/orchestrated) you can create restartable workflows, a workflow consisting of one or more dependent, queueable, tasks. This means that your workflows will continue to make progress even in the face of database and (queue) worker crashes.
|
20
20
|
|
21
21
|
In summary, orchestrated workflows running atop [active_record](https://github.com/rails/rails/tree/master/activerecord) and [delayed_job](https://github.com/collectiveidea/delayed_job) have these characteristics:
|
22
22
|
|
@@ -52,13 +52,13 @@ If you do not already have [delayed_job](https://github.com/collectiveidea/delay
|
|
52
52
|
The API
|
53
53
|
-------
|
54
54
|
|
55
|
-
To orchestrate (methods) on your own classes you simply call ```acts_as_orchestrated``` in the class definition. Declaring ```acts_as_orchestrated``` on your class defines the ```
|
55
|
+
To orchestrate (methods) on your own classes you simply call ```acts_as_orchestrated``` in the class definition. Declaring ```acts_as_orchestrated``` on your class defines the ```orchestrate``` method:
|
56
56
|
|
57
|
-
* ```
|
57
|
+
* ```orchestrate```—call this to specify your workflow prerequisite, and designate a workflow step
|
58
58
|
|
59
|
-
Use ```
|
59
|
+
Use ```orchestrate``` to orchestrate any method on your class.
|
60
60
|
|
61
|
-
Let's say for example you needed to download a couple files from remote systems (a slow process), merge their content and then load the results into your system. Imagine you have a ```Downloader``` class that knows how to download and an ```Xform``` class that knows how to merge the content and load the results into your system. Your ```Xform``` class might look something like this:
|
61
|
+
Let's say for example you needed to download a couple files from remote systems (a slow process), merge their content and then load the results into your system. This sort of workflow is sometimes referred to as extract/transfer/load or ETL. Imagine you have a ```Downloader``` class that knows how to download and an ```Xform``` class that knows how to merge the content and load the results into your system. Your ```Xform``` class might look something like this:
|
62
62
|
|
63
63
|
```ruby
|
64
64
|
class Xform
|
@@ -80,13 +80,13 @@ You might write an orchestration like this:
|
|
80
80
|
|
81
81
|
```ruby
|
82
82
|
xform = Xform.new
|
83
|
-
xform.
|
84
|
-
xform.
|
83
|
+
xform.orchestrate(
|
84
|
+
xform.orchestrate(
|
85
85
|
Orchestrated::LastCompletion.new(
|
86
|
-
Downloader.new.
|
86
|
+
Downloader.new.orchestrate.download(
|
87
87
|
:from=>'http://fred.com/stuff', :to=>'fred_records'
|
88
88
|
),
|
89
|
-
Downloader.new.
|
89
|
+
Downloader.new.orchestrate.download(
|
90
90
|
:from=>'http://sally.com/stuff', :to=>'sally_records'
|
91
91
|
)
|
92
92
|
)
|
@@ -99,24 +99,26 @@ The next time you process delayed jobs, the ```download``` messages will be deli
|
|
99
99
|
What happened there? The pattern is:
|
100
100
|
|
101
101
|
1. create an orchestrated object (instantiate it)
|
102
|
-
2. call ```
|
102
|
+
2. call ```orchestrate``` on it: this returns a *magic proxy* object that can respond to any of the messages your object can respond to
|
103
103
|
3. send any message to the *magic proxy* (returned in the second step) and the framework will delay delivery of that message and immediately return a "completion expression" you can use as a prerequisite for other orchestrations
|
104
104
|
4. (optionally) use the "completion expression" returned in (3) as a prerequisite for other orchestrations
|
105
105
|
|
106
106
|
Now the messages you can send in (3) can be anything that your object can respond to. The message will be remembered by the framework and "replayed" (on a new instance of your object) somewhere on the network (later).
|
107
107
|
|
108
|
-
Not accidentally, this is similar to the way [delayed_job](https://github.com/collectiveidea/delayed_job)'s delay method works. Under the covers, orchestrated is conspiring with [delayed_job](https://github.com/collectiveidea/delayed_job) when it comes time to actually execute a workflow step. Before that time though, orchestrated keeps track of everything.
|
108
|
+
Not accidentally, this is similar to the way [delayed_job](https://github.com/collectiveidea/delayed_job)'s delay method works. Under the covers, [Orchestrated](https://github.com/paydici/orchestrated) is conspiring with [delayed_job](https://github.com/collectiveidea/delayed_job) when it comes time to actually execute a workflow step. Before that time though, [Orchestrated](https://github.com/paydici/orchestrated) keeps track of everything.
|
109
109
|
|
110
110
|
Key Concept: Prerequisites (Completion Expressions)
|
111
111
|
---------------------------------------------------
|
112
112
|
|
113
|
-
Unlike [delayed_job](https://github.com/collectiveidea/delayed_job) ```delay```, the orchestrated ```
|
113
|
+
Unlike [delayed_job](https://github.com/collectiveidea/delayed_job) ```delay```, the orchestrated ```orchestrate``` method takes an optional parameter: the prerequisite. The prerequisite determines when your workflow step is ready to run.
|
114
114
|
|
115
|
-
The return value from messaging the *magic proxy* is itself a ready-to-use prerequisite. You saw this in the
|
115
|
+
The return value from messaging the *magic proxy* is itself a ready-to-use prerequisite. You saw this in the ETL example above. The result of the first call to ```orchestrate``` calls (to ```download```) were sent as an argument to the third (```merge```). In this way, the ```merge``` workflow step was suspended until after the ```download```s finished.
|
116
|
+
|
117
|
+
You may have also noticed from that example that if you specify no prerequisite then the step will be ready to run immediately, as was the case for the ```download``` calls). If calling ```orchestrate``` with no parameters makes the step ready to run immediately then why should we bother to call it at all? Why not just call the method directly? The answer is that by calling ```orchestrate``` we are submitting the step to the underlying queueing system, enabling the step to be run on other resources (computers). Had we called the ```download``` directly it would have blocked the Ruby thread and would not have taken advantage of (potentially many) ```delayed_job``` job workers.
|
116
118
|
|
117
119
|
Users of the framework deal directly with three kinds of prerequisite or "completion expression":
|
118
120
|
|
119
|
-
1. ```OrchestrationCompletion```—returned
|
121
|
+
1. ```OrchestrationCompletion```—returned from any message to a *magic proxy*: complete when its associated orchestration is complete
|
120
122
|
2. ```FirstCompletion```—aggregates other completions: complete after the first one completes
|
121
123
|
3. ```LastCompletion```—aggregates other completions: complete after all of them are complete
|
122
124
|
|
@@ -135,7 +137,7 @@ A "ready" orchestration will use [delayed_job](https://github.com/collectiveidea
|
|
135
137
|
|
136
138
|
After your workflow step executes, the orchestration moves into either the "succeeded" or "failed" state.
|
137
139
|
|
138
|
-
When an orchestration is "ready" or "waiting" it may be canceled by sending it the ```cancel!``` message (i.e. a ```cancel!``` message to the ```OrchestrationCompletion
|
140
|
+
When an orchestration is "ready" or "waiting" it may be canceled by sending it the ```cancel!``` message (i.e. a ```cancel!``` message to the ```OrchestrationCompletion```). This moves the orchestration to the "canceled" state and prevents subsequent delivery of the orchestrated message.
|
139
141
|
|
140
142
|
It is important to understand that both of the states: "succeeded" and "failed" are part of a "super-state": "complete". When an orchestration is in either of those two states, it will return ```true``` in response to the ```complete?``` message.
|
141
143
|
|
@@ -144,7 +146,7 @@ It is not just successful completion of orchestrated methods that causes depende
|
|
144
146
|
Failure (An Option)
|
145
147
|
-------------------
|
146
148
|
|
147
|
-
Orchestration is built atop [delayed_job](https://github.com/collectiveidea/delayed_job) and borrows [delayed_job](https://github.com/collectiveidea/delayed_job)'s failure semantics. Neither framework imposes any special constraints on the (delayed or orchestrated) methods. In particular, there are no special return values to signal "failure". Orchestration adopts [delayed_job](https://github.com/collectiveidea/delayed_job)'s semantics for failure detection: a method that raises an exception has failed. After a certain number of retries (configurable in [delayed_job](https://github.com/collectiveidea/delayed_job)) the jobs is deemed permanently failed. When that happens, the corresponding orchestration is marked "failed".
|
149
|
+
Since Orchestration is built atop [delayed_job](https://github.com/collectiveidea/delayed_job) and borrows [delayed_job](https://github.com/collectiveidea/delayed_job)'s failure semantics. Neither framework imposes any special constraints on the (delayed or orchestrated) methods. In particular, there are no special return values to signal "failure". Orchestration adopts [delayed_job](https://github.com/collectiveidea/delayed_job)'s semantics for failure detection: a method that raises an exception has failed. After a certain number of retries (configurable in [delayed_job](https://github.com/collectiveidea/delayed_job)) the jobs is deemed permanently failed. When that happens, the corresponding orchestration is marked "failed". Until all the retries have been attempted, the orchestration remains in the "ready" state (as it was before the first failed attempt).
|
148
150
|
|
149
151
|
See the failure_spec if you'd like to understand more.
|
150
152
|
|
@@ -169,7 +171,14 @@ Future Work
|
|
169
171
|
|
170
172
|
Some possible avenues for exploration:
|
171
173
|
|
172
|
-
*
|
173
|
-
*
|
174
|
-
*
|
174
|
+
* orchestrate option: :max_attempts to configure max_attempts on underlying delayed job instance
|
175
|
+
* orchestrate option: :max_run_time to configure max_run_time on underlying delayed job instance
|
176
|
+
* orchestrate options: :queue to specify a particular named queue for the underlying delayed job
|
175
177
|
* some way to change the run_at recalculation for failed attempts (f(n) = 5 + n**4 is not always right and what's right varies by job)
|
178
|
+
|
179
|
+
License
|
180
|
+
-------
|
181
|
+
|
182
|
+
Copyright © 2013 Paydici Inc. Distributed under the MIT License. See [LICENSE.txt](https://github.com/paydici/orchestrated/blob/master/LICENSE.txt) for further details.
|
183
|
+
|
184
|
+
Contains code originally from [delayed_job](https://github.com/collectiveidea/delayed_job) Copyright © 2005 Tobias Luetke, [Ruby on Rails](https://github.com/rails/rails), and [Ick](https://github.com/raganwald-deprecated/ick); all of which are also under the MIT License.
|
data/lib/orchestrated/base.rb
CHANGED
@@ -11,25 +11,28 @@ module Orchestrated
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
class
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
14
|
+
# because this class is called "Orchestrate", the method defined (on Object) will be "orchestrate"
|
15
|
+
class Orchestrate
|
16
|
+
class << self
|
17
|
+
#snarfed from Ruby On Rails
|
18
|
+
def underscore(camel_cased_word)
|
19
|
+
camel_cased_word.to_s.gsub(/::/, '/').
|
20
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
21
|
+
tr("-", "_").
|
22
|
+
downcase
|
23
|
+
end
|
24
|
+
def belongs_to clazz
|
25
|
+
# borrowed from Ick
|
26
|
+
method_name = self.underscore(self.name.split('::')[-1])
|
27
|
+
unless clazz.method_defined?(method_name)
|
28
|
+
clazz.class_eval "
|
29
|
+
def #{method_name}(prerequisite=Complete.new)
|
30
|
+
raise ArgumentError.new('orchestrate does not take a block') if block_given?
|
31
|
+
raise ArgumentError.new(%[cannot use \#{prerequisite.class.name} as a prerequisite]) unless
|
32
|
+
prerequisite.kind_of?(CompletionExpression)
|
33
|
+
Proxy.new(prerequisite, self)
|
34
|
+
end"
|
35
|
+
end
|
33
36
|
end
|
34
37
|
end
|
35
38
|
end
|
data/lib/orchestrated/object.rb
CHANGED
@@ -8,7 +8,7 @@ module Orchestrated
|
|
8
8
|
class ::Object
|
9
9
|
class << self
|
10
10
|
def acts_as_orchestrated
|
11
|
-
|
11
|
+
Orchestrate.belongs_to self # define "orchestrated instance method"
|
12
12
|
include InstanceMethods
|
13
13
|
end
|
14
14
|
end
|
data/lib/orchestrated/version.rb
CHANGED
@@ -34,7 +34,7 @@ describe 'cancellation' do
|
|
34
34
|
def dependent;@dependent;end
|
35
35
|
context 'directly on an orchestration' do
|
36
36
|
before(:each) do
|
37
|
-
@first_prerequisite = @dependent = First.new.
|
37
|
+
@first_prerequisite = @dependent = First.new.orchestrate.do_first_thing(1)
|
38
38
|
end
|
39
39
|
it 'should be ready' do
|
40
40
|
expect(@dependent.orchestration.state).to eq('ready')
|
@@ -71,16 +71,16 @@ describe 'cancellation' do
|
|
71
71
|
end
|
72
72
|
context 'of an orchestration that is depended on directly' do
|
73
73
|
before(:each) do
|
74
|
-
@dependent = Second.new.
|
74
|
+
@dependent = Second.new.orchestrate( @first_prerequisite = First.new.orchestrate.do_first_thing(1)).do_second_thing(2)
|
75
75
|
end
|
76
76
|
include_context 'cancelling first prerequisite'
|
77
77
|
it_should_behave_like 'cancellation:'
|
78
78
|
end
|
79
79
|
context 'of an orchestration that is depended on through a LastCompletion' do
|
80
80
|
before(:each) do
|
81
|
-
@dependent = Second.new.
|
81
|
+
@dependent = Second.new.orchestrate(
|
82
82
|
Orchestrated::LastCompletion.new <<
|
83
|
-
(@first_prerequisite = First.new.
|
83
|
+
(@first_prerequisite = First.new.orchestrate.do_first_thing(1))
|
84
84
|
).do_second_thing(2)
|
85
85
|
end
|
86
86
|
include_context 'cancelling first prerequisite'
|
@@ -88,10 +88,10 @@ describe 'cancellation' do
|
|
88
88
|
end
|
89
89
|
context 'of an orchestration that is depended on through a FirstCompletion with two prerequisites' do
|
90
90
|
before(:each) do
|
91
|
-
@dependent = Second.new.
|
91
|
+
@dependent = Second.new.orchestrate(
|
92
92
|
Orchestrated::FirstCompletion.new <<
|
93
|
-
(@first_prerequisite = First.new.
|
94
|
-
(@last_prerequisite = First.new.
|
93
|
+
(@first_prerequisite = First.new.orchestrate.do_first_thing(3)) <<
|
94
|
+
(@last_prerequisite = First.new.orchestrate.do_first_thing(1))
|
95
95
|
).do_second_thing(2)
|
96
96
|
end
|
97
97
|
context 'after first prerequisite is canceled' do
|
@@ -15,21 +15,21 @@ end
|
|
15
15
|
describe Orchestrated::CompletionExpression do
|
16
16
|
context 'Complete' do
|
17
17
|
context 'implicitly specified' do
|
18
|
-
before(:each){ First.new.
|
18
|
+
before(:each){ First.new.orchestrate.do_first_thing(12) }
|
19
19
|
it_should_behave_like 'literally complete'
|
20
20
|
end
|
21
21
|
context 'explicitly specified' do
|
22
|
-
before(:each){ First.new.
|
22
|
+
before(:each){ First.new.orchestrate(Orchestrated::Complete.new).do_first_thing(12) }
|
23
23
|
it_should_behave_like 'literally complete'
|
24
24
|
end
|
25
25
|
end
|
26
26
|
context 'Incomplete' do
|
27
27
|
it 'should immediately raise an error' do
|
28
|
-
expect{First.new.
|
28
|
+
expect{First.new.orchestrate(Orchestrated::Incomplete.new).do_first_thing(12)}.to raise_error(ArgumentError)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
context 'OrchestrationCompletion' do
|
32
|
-
before(:each){Second.new.
|
32
|
+
before(:each){Second.new.orchestrate( First.new.orchestrate.do_first_thing(3)).do_second_thing(4)}
|
33
33
|
it 'should block second orchestration until after first runs' do
|
34
34
|
expect(DJ.job_count).to eq(1)
|
35
35
|
end
|
@@ -41,7 +41,7 @@ describe Orchestrated::CompletionExpression do
|
|
41
41
|
context 'FirstCompletion' do
|
42
42
|
context 'given a (literal) Complete' do
|
43
43
|
before(:each) do
|
44
|
-
Second.new.
|
44
|
+
Second.new.orchestrate( Orchestrated::FirstCompletion.new <<
|
45
45
|
Orchestrated::Complete.new
|
46
46
|
).do_second_thing(5)
|
47
47
|
end
|
@@ -51,9 +51,9 @@ describe Orchestrated::CompletionExpression do
|
|
51
51
|
end
|
52
52
|
context 'given two OrchestrationCompletions' do
|
53
53
|
before(:each) do
|
54
|
-
Second.new.
|
55
|
-
First.new.
|
56
|
-
First.new.
|
54
|
+
Second.new.orchestrate( Orchestrated::FirstCompletion.new <<
|
55
|
+
First.new.orchestrate.do_first_thing(3) <<
|
56
|
+
First.new.orchestrate.do_first_thing(4)
|
57
57
|
).do_second_thing(5)
|
58
58
|
end
|
59
59
|
it 'should enqueue the dependent orchestration as soon as the first prerequisite completes' do
|
@@ -74,9 +74,9 @@ describe Orchestrated::CompletionExpression do
|
|
74
74
|
context 'LastCompletion' do
|
75
75
|
context 'given two OrchestrationCompletions' do
|
76
76
|
before(:each) do
|
77
|
-
Second.new.
|
78
|
-
First.new.
|
79
|
-
First.new.
|
77
|
+
Second.new.orchestrate( Orchestrated::LastCompletion.new <<
|
78
|
+
First.new.orchestrate.do_first_thing(3) <<
|
79
|
+
First.new.orchestrate.do_first_thing(4)
|
80
80
|
).do_second_thing(5)
|
81
81
|
end
|
82
82
|
it 'should not enqueue the dependent orchestration as soon as the first prerequisite completes' do
|
data/spec/unit/failure_spec.rb
CHANGED
@@ -5,7 +5,7 @@ require 'orchestrated'
|
|
5
5
|
describe 'failure' do
|
6
6
|
context 'orchestrating a method that always fails' do
|
7
7
|
before(:each) do
|
8
|
-
Failer.new.
|
8
|
+
Failer.new.orchestrate.always_fail('important stuff')
|
9
9
|
end
|
10
10
|
context 'after first exception from orchestrated method' do
|
11
11
|
before(:each) do
|
@@ -14,14 +14,14 @@ end
|
|
14
14
|
|
15
15
|
describe Orchestrated do
|
16
16
|
context 'initializing' do
|
17
|
-
it 'should not define
|
18
|
-
expect(Object.public_method_defined?(:
|
17
|
+
it 'should not define orchestrate on Object' do
|
18
|
+
expect(Object.public_method_defined?(:orchestrate)).to be_false
|
19
19
|
end
|
20
|
-
it 'should not define
|
21
|
-
expect(ActiveRecord::Base.public_method_defined?(:
|
20
|
+
it 'should not define orchestrate on ActiveRecord::Base' do
|
21
|
+
expect(ActiveRecord::Base.public_method_defined?(:orchestrate)).to be_false
|
22
22
|
end
|
23
|
-
it 'should define
|
24
|
-
expect(First.public_method_defined?(:
|
23
|
+
it 'should define orchestrate on First' do
|
24
|
+
expect(First.public_method_defined?(:orchestrate)).to be_true
|
25
25
|
end
|
26
26
|
end
|
27
27
|
context 'a new object' do
|
@@ -76,7 +76,7 @@ describe Orchestrated do
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
context 'orchestrating with no prerequisites' do
|
79
|
-
before(:each){@result = f.
|
79
|
+
before(:each){@result = f.orchestrate.do_first_thing(2)}
|
80
80
|
after(:each){DJ.clear_all_jobs}
|
81
81
|
it 'should not immediately invoke an orchestrated method' do
|
82
82
|
First.any_instance.should_not_receive(:do_first_thing)
|
@@ -91,7 +91,7 @@ describe Orchestrated do
|
|
91
91
|
end
|
92
92
|
context 'invocation' do
|
93
93
|
before(:each) do
|
94
|
-
First.new.
|
94
|
+
First.new.orchestrate.do_first_thing(1)
|
95
95
|
end
|
96
96
|
it 'should have access to Orchestration' do
|
97
97
|
First.any_instance.should_receive(:orchestration=).with(kind_of(Orchestrated::Orchestration))
|
@@ -8,13 +8,13 @@ describe 'performing static analysis' do
|
|
8
8
|
context 'that is empty' do
|
9
9
|
# chose this behavior to align with Ruby Enumerable#any?
|
10
10
|
it 'should raise an error since it can never be complete' do
|
11
|
-
expect{Second.new.
|
11
|
+
expect{Second.new.orchestrate(completion).do_second_thing(5)}.to raise_error(ArgumentError)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
context 'that contains only (static) Incompletes' do
|
15
15
|
before(:each){completion<<Orchestrated::Incomplete.new}
|
16
16
|
it 'should raise an error since it can never be complete' do
|
17
|
-
expect{Second.new.
|
17
|
+
expect{Second.new.orchestrate(completion).do_second_thing(5)}.to raise_error(ArgumentError)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
context 'that directly containins a (static) Complete' do
|
@@ -41,7 +41,7 @@ describe 'performing static analysis' do
|
|
41
41
|
context 'that directly contains a (static) Incomplete' do
|
42
42
|
before(:each){completion<<Orchestrated::Incomplete.new}
|
43
43
|
it 'should raise an error since it can never be complete' do
|
44
|
-
expect{Second.new.
|
44
|
+
expect{Second.new.orchestrate(completion).do_second_thing(5)}.to raise_error(ArgumentError)
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: orchestrated
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-02-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: delayed_job_active_record
|