ntl-orchestra 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7a3c2a8f906bac033bc49fd31c5c785d61fcc665
4
- data.tar.gz: b7909a5e4a439b36f0e133a98356a176fe31f31d
3
+ metadata.gz: 04d7c524d1821f1d2dcaec41a3a567c906b69830
4
+ data.tar.gz: 7be0bf35060a1714d0edb8d6489d49f46011ac13
5
5
  SHA512:
6
- metadata.gz: b29d023f1ec08bd492365f926d328aa2bc8568d92e67bfb030dbe9807935a465efbc15eb4a95da256aac47590ab0a23c103e07196a4d2a2526e832b46db15ea0
7
- data.tar.gz: 191c785e486a990ea5a30284f14098174de61f0f55dfb416b5ca5fdee5d4ad95a7dfff89225ff4b95fdd8ca13bffacef488253e162deb37fdfc4f6a46f3afcd4
6
+ metadata.gz: 82a9d1d61b21cbd09464027b6a72c43fc433979ec851be8906b3f7eb590c7baad62ef200cae5cb21a86ab4a20551c34c744aa801d30601d9d7e2b147fb9a0d89
7
+ data.tar.gz: b8101d3196af1ffbb1f7faa578c3f665d76d102395331fcda4242b6b9e0e48cfa5d71976c91cc24f00ecdd3d0a91508733eb4be58b89e587b32df799d5f2f70a
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.5
data/Gemfile CHANGED
@@ -5,7 +5,7 @@ gemspec
5
5
 
6
6
  group :test do
7
7
  gem "minitest"
8
- gem "minitest-red_green"
8
+ gem "minitest-red_green", ">= 0.9.3"
9
9
  gem "sqlite3"
10
10
  gem "webmock"
11
11
  end
data/README.md CHANGED
@@ -1,41 +1,25 @@
1
- # Orchestra
1
+ # Orchestra ![Build Status](https://travis-ci.org/ntl/orchestra.svg?branch=master)
2
2
 
3
3
  Seamlessly chain multiple command or query objects together with a simple, lightweight framework.
4
4
 
5
- ## Installation
6
-
7
- Add this line to your application's Gemfile:
8
-
9
- ```ruby
10
- gem 'ntl-orchestra'
11
- ```
12
-
13
- And then execute:
14
-
15
- $ bundle
16
-
17
- Or install it yourself as:
18
-
19
- $ gem install ntl-orchestra
20
-
21
5
  ## Usage
22
6
 
23
7
  Here's a simple example without a lot of context:
24
8
 
25
9
  ```ruby
26
10
  operation = Orchestra::Operation.new do
27
- node :make_array do
11
+ step :make_array do
28
12
  depends_on :up_to
29
13
  provides :array
30
- perform do
14
+ execute do
31
15
  limit.times.to_a
32
16
  end
33
17
  end
34
18
 
35
- node :apply_fizzbuzz do
19
+ step :apply_fizzbuzz do
36
20
  iterates_over :array
37
21
  provides :fizzbuzz
38
- perform do |num|
22
+ execute do |num|
39
23
  next if num == 0 # filter 0 from the output
40
24
  str = ''
41
25
  str << "Fizz" if num.mod 3 == 0
@@ -47,13 +31,13 @@ operation = Orchestra::Operation.new do
47
31
 
48
32
  finally do
49
33
  iterates_over :fizzbuzz
50
- perform do |str|
34
+ execute do |str|
51
35
  puts str
52
36
  end
53
37
  end
54
38
  end
55
39
 
56
- Orchestra.perform operation, :up_to => 31
40
+ Orchestra.execute operation, :up_to => 31
57
41
  ```
58
42
 
59
43
  There is an easy way to take this gem for a test drive. Clone the repo, and at the project root:
@@ -81,8 +65,8 @@ Finished in 0.379958s, 126.3298 runs/s, 1526.4845 assertions/s.
81
65
  Also, you can access the examples:
82
66
 
83
67
  ```ruby
84
- [1] pry(Orchestra)> Orchestra.perform FizzBuzz, :up_to => 31
85
- [1] pry(Orchestra)> Orchestra.perform InvitationService, :account_name => 'realntl`
68
+ [1] pry(Orchestra)> Orchestra.execute FizzBuzz, :up_to => 31
69
+ [1] pry(Orchestra)> Orchestra.execute InvitationService, :account_name => 'realntl`
86
70
  ```
87
71
 
88
72
  ## Why?
@@ -155,25 +139,25 @@ InvitationService = Orchestra::Operation.new do
155
139
  DEFAULT_MESSAGE = "I would really love for you to try out MyApp."
156
140
  ROBOT_FOLLOWER_THRESHHOLD = 500
157
141
 
158
- node :fetch_followers do
142
+ step :fetch_followers do
159
143
  depends_on :account_name
160
144
  provides :followers
161
- perform do
145
+ execute do
162
146
  FlutterAPI.get_account account_name
163
147
  end
164
148
  end
165
149
 
166
- node :fetch_blacklist do
150
+ step :fetch_blacklist do
167
151
  provides :blacklist
168
- perform do
152
+ execute do
169
153
  Blacklist.pluck :flutter_account_name
170
154
  end
171
155
  end
172
156
 
173
- node :remove_blacklisted_followers do
157
+ step :remove_blacklisted_followers do
174
158
  depends_on :blacklist
175
159
  modifies :followers
176
- perform do
160
+ execute do
177
161
  followers.reject! do |follower|
178
162
  account_name = follower.fetch 'username'
179
163
  blacklist.include? account_name
@@ -181,9 +165,9 @@ InvitationService = Orchestra::Operation.new do
181
165
  end
182
166
  end
183
167
 
184
- node :filter_robots do
168
+ step :filter_robots do
185
169
  modifies :followers, :collection => true
186
- perform do |follower|
170
+ execute do |follower|
187
171
  account_name = follower.fetch 'username'
188
172
  account = FlutterAPI.get_account account_name
189
173
  next unless account['following'] > ROBOT_FOLLOWER_THRESHHOLD
@@ -195,63 +179,63 @@ InvitationService = Orchestra::Operation.new do
195
179
  finally :deliver_emails do
196
180
  depends_on :message => DEFAULT_MESSAGE
197
181
  iterates_over :followers
198
- perform do |follower|
182
+ execute do |follower|
199
183
  EmailDelivery.send message, :to => follower
200
184
  end
201
185
  end
202
186
  end
203
187
  ```
204
188
 
205
- At first sight, that very likely appears to be a giant pile of ruby DSL goop. And you would be correct. I'll show you how to plug in POROs in a bit, and there are some further improvements that will make the indirection worth while. For now, here is how you would perform this command:
189
+ At first sight, that very likely appears to be a giant pile of ruby DSL goop. And you would be correct. I'll show you how to plug in POROs in a bit, and there are some further improvements that will make the indirection worth while. For now, here is how you would execute this command:
206
190
 
207
191
  ```ruby
208
192
  # Use the default message
209
- Orchestra.perform InvitationService, :account_name => 'realntl'
193
+ Orchestra.execute InvitationService, :account_name => 'realntl'
210
194
 
211
195
  # Override the default message
212
- Orchestra.perform InvitationService, :account_name => 'realntl', :message => 'Say cheese!'
196
+ Orchestra.execute InvitationService, :account_name => 'realntl', :message => 'Say cheese!'
213
197
  ```
214
198
 
215
199
  There's a lot more to learn, but let's start by building an understanding of the DSL.
216
200
 
217
- ## Breaking down the DSL: Nodes
201
+ ## Breaking down the DSL: Steps
218
202
 
219
- An *operation* is a collection of nodes that each individually take part in producing some larger behavior. Each node represents one step, or stage, in the whole process. A node, essentially, accepts input, processes, and provides output. Let's look at the first node, called `:fetch_followers`.
203
+ An *operation* is a collection of steps that each individually take part in producing some larger behavior. A step, essentially, accepts input, processes, and provides output. Let's look at the first one, called `:fetch_followers`.
220
204
 
221
205
  ```ruby
222
- node :fetch_followers do
206
+ step :fetch_followers do
223
207
  depends_on :account_name
224
208
  provides :followers
225
- perform do
209
+ execute do
226
210
  FlutterAPI.get_account account_name
227
211
  end
228
212
  end
229
213
  ```
230
214
 
231
- This node depends on something called an `account_name`. This means you must supply `:account_name` when you perform the operation, otherwise, `:fetch_followers` won't work. `orchestra` ensures that your performance can't commence without the required input:
215
+ This node depends on something called an `account_name`. This means you must supply `:account_name` when you execute the operation, otherwise, `:fetch_followers` won't work. `orchestra` ensures that your invokation can't commence without the required input:
232
216
 
233
217
  ```ruby
234
- # Raises Orchestra::MissingInputError: Missing input :account_name
235
- operation.perform
218
+ # Raises Orchestra::MissingInputError: Missing input :account_name
219
+ operation.execute
236
220
  # Works correctly
237
- operation.perform :account_name => 'realntl'
221
+ operation.execute :account_name => 'realntl'
238
222
  ```
239
223
 
240
224
  Dependencies can be optional. In the above example, `deliver_emails` defaults the `:message` input to `DEFAULT_MESSAGE`.
241
225
 
242
- Often, you will need the output of one operation to feed into the input of another. The annotations `depends_on`, `iterates_over`, and `modifies` all describe the inputs, and `provides` describes the output. When `provides` is omitted, the name of the output is set to match the name of the node. Orchestra actually uses the annotations to sort the ordering of the nodes at runtime to ensure that all dependencies are satisfied. In the above example, `remove_blacklisted_followers` would not execute before `fetch_blacklist`, no matter where their definitions were placed within the operation. Orchestra detects that `remove_blacklisted_followers` *depends on* `blacklist`, and `fetch_blacklist` actually provides a `blacklist`, so it knows to run `fetch_blacklist` before `remove_blacklisted_followers`. Similarly, if you had your own list of blacklisted account names lying around, you could bypass the `fetch_blacklist` node altogether, since there is no sense fetching a `blacklist` when you've already got one:
226
+ Often, you will need the output of one operation to feed into the input of another. The annotations `depends_on`, `iterates_over`, and `modifies` all describe the inputs, and `provides` describes the output. When `provides` is omitted, the name of the output is set to match the name of the step. Orchestra actually uses the annotations to sort the ordering of the steps at runtime to ensure that all dependencies are satisfied. In the above example, `remove_blacklisted_followers` would not execute before `fetch_blacklist`, no matter where their definitions were placed within the operation. Orchestra detects that `remove_blacklisted_followers` *depends on* `blacklist`, and `fetch_blacklist` actually provides a `blacklist`, so it knows to run `fetch_blacklist` before `remove_blacklisted_followers`. Similarly, if you had your own list of blacklisted account names lying around, you could bypass the `fetch_blacklist` step altogether, since there is no sense fetching a `blacklist` when you've already got one:
243
227
 
244
228
  ```ruby
245
229
  # Never invokes fetch_blacklist
246
- operation.perform :account_name => 'realntl', :blacklist => %w(dhh unclebobmartin)
230
+ operation.execute :account_name => 'realntl', :blacklist => %w(dhh unclebobmartin)
247
231
  ```
248
232
 
249
233
  This allows your operations to be reused in cases where some of the dependencies can already be satisfied.
250
234
 
251
- Finally, the `modifies` annotiation deserves some explaining. When a node merely mutates an input, you are certainly welcome to declare distinct `depends_on` and `modifies` annotations:
235
+ Finally, the `modifies` annotiation deserves some explaining. When a step merely mutates an input, you are certainly welcome to declare distinct `depends_on` and `modifies` annotations:
252
236
 
253
237
  ```ruby
254
- node :remove_blacklisted_followers do
238
+ step :remove_blacklisted_followers do
255
239
  depends_on :blacklist, :followers
256
240
  provides :followers
257
241
  end
@@ -260,7 +244,7 @@ end
260
244
  However, `modifies` simply condenses the two into one. The following example is identical to the previous:
261
245
 
262
246
  ```ruby
263
- node :remove_blacklisted_followers do
247
+ step :remove_blacklisted_followers do
264
248
  depends_on :blacklist
265
249
  modifies :followers
266
250
  end
@@ -268,16 +252,16 @@ end
268
252
 
269
253
  ## Breaking down the DSL: the operation itself
270
254
 
271
- Configuring the operation is rather simple. You define the various nodes, and then specify the result. There are three ways to specify the result.
255
+ Configuring the operation is rather simple. You define the various steps, and then specify the result. There are three ways to specify the result.
272
256
 
273
257
  The first is very straightforward:
274
258
 
275
259
  ```ruby
276
260
  Orchestra::Operation.new do
277
- node :foo do
261
+ step :foo do
278
262
  depends_on :bar
279
- provides :foo # optional, since the node is called :foo
280
- perform do … end
263
+ provides :foo # optional, since the step is called :foo
264
+ execute do … end
281
265
  end
282
266
 
283
267
  self.result = :foo
@@ -288,38 +272,38 @@ The second is just a shortened form of the first:
288
272
 
289
273
  ```ruby
290
274
  Orchestra::Operation.new do
291
- # Define a node called :foo and make it the result
275
+ # Define a step called :foo and make it the result
292
276
  result :foo do
293
277
  depends_on :bar
294
- perform do … end
278
+ execute do … end
295
279
  end
296
280
  end
297
281
  ```
298
282
 
299
- The third is a minor variation of the second. The only difference is that the operation will always return `true`. `finally` makes sense for operations that perform side effects (e.g. Command objects), whereas `result` will make sense for queries.
283
+ The third is a minor variation of the second. The only difference is that the operation will always return `true`. `finally` makes sense for operations that execute side effects (e.g. Command objects), whereas `result` will make sense for queries.
300
284
 
301
285
  ```ruby
302
286
  Orchestra::Operation.new do
303
287
  finally :foo do
304
288
  depends_on :bar
305
- perform do … end
289
+ execute do … end
306
290
  end
307
291
  end
308
292
  ```
309
293
 
310
294
  ## Hooking in POROs
311
295
 
312
- You can also hook up POROs to operations as nodes. This is important both to manage complex nodes as well as leveraging existing objects in the system. The `filter_robots` node could be expressed as a PORO rather easily:
296
+ You can also hook up POROs to operations as steps. This is important both to manage complex steps as well as leveraging existing objects in the system. The `filter_robots` step could be expressed as a PORO rather easily:
313
297
 
314
298
  ```ruby
315
- node FilterRobots, :iterates_over => :followers, :collection => true
299
+ step FilterRobots, :iterates_over => :followers, :collection => true
316
300
 
317
301
  class FilterRobots
318
302
  def initialize followers
319
303
  @followers = followers
320
304
  end
321
305
 
322
- def perform follower
306
+ def execute follower
323
307
  account_name = follower.fetch 'account_name'
324
308
  account = FlutterAPI.get_account account_name
325
309
  next unless account['following'] > ROBOT_FOLLOWER_THRESHHOLD
@@ -329,16 +313,16 @@ class FilterRobots
329
313
  end
330
314
  ```
331
315
 
332
- Orchestra infers the dependencies from `FilterRobots#initialize`, and automatically instantiates the object for you during the performance. You can alter the name of the method:
316
+ Orchestra infers the dependencies from `FilterRobots#initialize`, and automatically instantiates the object for you during execution. You can alter the name of the method:
333
317
 
334
318
  ```ruby
335
- node MyPoro, :method => :call
319
+ step MyPoro, :method => :call
336
320
  ```
337
321
 
338
- You can also hook into singletons like `Module` (or a `Class` that implements `self.perform`):
322
+ You can also hook into singletons like `Module` (or a `Class` that implements `self.execute`):
339
323
 
340
324
  ```ruby
341
- node MySingleton, :method => :invoke
325
+ step MySingleton, :method => :invoke
342
326
 
343
327
  module MySingleton
344
328
  def self.invoke … end
@@ -349,9 +333,9 @@ By default, the name of the provision will be inferred from the object name.
349
333
 
350
334
  ## Multithreading
351
335
 
352
- Two of the nodes in the `InvitationService` orchestration -- `filter_robots` and `deliver_emails` -- actually operate on *collections*. `deliver_emails` indicates that `:followers` is a collection by using the `iterates_over` annotation instead of `depends_on`. In fact, the two annotations are identical *except* that `iterates_over` indicates that the dependency is in fact going to be a list. Collections can be defined on a `modifies` annotation, as well, by supplying `:collection => true` as in the case of `filter_robots`.
336
+ Two of the steps in the `InvitationService` orchestration -- `filter_robots` and `deliver_emails` -- actually operate on *collections*. `deliver_emails` indicates that `:followers` is a collection by using the `iterates_over` annotation instead of `depends_on`. In fact, the two annotations are identical *except* that `iterates_over` indicates that the dependency is in fact going to be a list. Collections can be defined on a `modifies` annotation, as well, by supplying `:collection => true` as in the case of `filter_robots`.
353
337
 
354
- When nodes iterate over collections, Orchestra invokes `perform do … end` block once for each item in the collection passed in. It also spreads out each invokation across a thread pool. By default, there is only one thread in the thread pool. You can reconfigure that globally in an initializer of some kind:
338
+ When steps iterate over collections, Orchestra invokes `execute do … end` block once for each item in the collection passed in. It also spreads out each invokation across a thread pool. By default, there is only one thread in the thread pool. You can reconfigure that globally in an initializer of some kind:
355
339
 
356
340
  ```ruby
357
341
  Orchestra.configure do
@@ -360,15 +344,15 @@ Orchestra.configure do
360
344
  end
361
345
  ```
362
346
 
363
- These collections can operate as filters; the output list is a mapping of the input list *transformed* by the `perform` block. When the `perform` block returns `nil`, the output shrinks by one element. Consider the FizzBuzz example at the top of this document. Notice that `0` doesn't get printed out. This is because the `perform` block in `apply_fizzbuzz` returned nil when the `num` was zero. `nil` values get `compact`'ed.
347
+ These collections can operate as filters; the output list is a mapping of the input list *transformed* by the `execute` block. When the `execute` block returns `nil`, the output shrinks by one element. Consider the FizzBuzz example at the top of this document. Notice that `0` doesn't get printed out. This is because the `execute` block in `apply_fizzbuzz` returned nil when the `num` was zero. `nil` values get `compact`'ed.
364
348
 
365
349
  ## Invoking an operation through a conductor
366
350
 
367
- Now that you understand how to define operations, we can do some cool things with them. First, though, we need to change the way we invoke operations. Let's instantiate a `Conductor`, and have *that* perform our operations for us:
351
+ Now that you understand how to define operations, we can do some cool things with them. First, though, we need to change the way we invoke operations. Let's instantiate a `Conductor`, and have *that* execute our operations for us:
368
352
 
369
353
  ```ruby
370
354
  conductor = Orchestra::Conductor.new
371
- conductor.perform InvitationService, :account_name => 'realntl'
355
+ conductor.execute InvitationService, :account_name => 'realntl'
372
356
  ```
373
357
 
374
358
  What did that buy us? First, we can configure the size of the thread pool specifically for this conductor:
@@ -380,20 +364,20 @@ conductor.thread_count = 5
380
364
  Second, we can inject *services* into our operation. Our operation needs to be modified such that our database connections and API access are passed in as dependencies:
381
365
 
382
366
  ```ruby
383
- node :fetch_followers do
367
+ step :fetch_followers do
384
368
  depends_on :account_name, :flutter_api
385
369
  provides :followers
386
- perform do
370
+ execute do
387
371
  flutter_api.get_account account_name
388
372
  end
389
373
  end
390
374
 
391
375
  # and
392
376
 
393
- node :fetch_blacklist do
377
+ step :fetch_blacklist do
394
378
  depends_on :blacklist_table
395
379
  provides :blacklist
396
- perform do
380
+ execute do
397
381
  blacklist_table.pluck :flutter_account_name
398
382
  end
399
383
  end
@@ -408,21 +392,21 @@ conductor = Orchestra::Conductor.new(
408
392
  )
409
393
  ```
410
394
 
411
- We can also override the conductor's service registry by supplying them into the performance itself, as we do any other dependency like `account_name`:
395
+ We can also override the conductor's service registry by supplying them into the execution itself, as we do any other dependency like `account_name`:
412
396
 
413
397
  ```ruby
414
- conductor.perform InvitationService, :account_name => 'realntl', :blacklist_table => mock
398
+ conductor.execute InvitationService, :account_name => 'realntl', :blacklist_table => mock
415
399
  ```
416
400
 
417
- What did this buy us? Two big things. We can now attach *observers* to the performance, and we can actually record all calls in and out of the `flutter_api` and `blacklist_table` services. The former allows us to share the internal operation of the performance with the rest of the system without breaking encapsulation, and the latter allows us to actually replay the operation against recorded snapshots of live performances.
401
+ What did this buy us? Two big things. We can now attach *observers* to the execution, and we can actually record all calls in and out of the `flutter_api` and `blacklist_table` services. The former allows us to share the internal operation of the execution with the rest of the system without breaking encapsulation, and the latter allows us to actually replay the operation against recordings of live performances.
418
402
 
419
- Additionally, you can pass the `conductor` into nodes. In this way you can embed one orchestration into another:
403
+ Additionally, you can pass the `conductor` into steps. In this way you can embed one operation inside another:
420
404
 
421
405
  ```ruby
422
406
  inner_operation = Orchestra::Operation.new do
423
407
  result :foo do
424
408
  provides :bar
425
- perform do
409
+ execute do
426
410
  bar * 2
427
411
  end
428
412
  end
@@ -432,14 +416,14 @@ outer_operation = Orchestra::Operation.new do
432
416
  result :baz do
433
417
  depends_on :conductor
434
418
  provides :qux
435
- perform do
436
- conductor.perform inner_operation
419
+ execute do
420
+ conductor.execute inner_operation
437
421
  end
438
422
  end
439
423
  end
440
424
 
441
425
  conductor = Conductor.new
442
- conductor.perform outer_operation
426
+ conductor.execute outer_operation
443
427
  ```
444
428
 
445
429
  To shorten this, the inner operation can be "mounted" inside the outer operation:
@@ -448,7 +432,7 @@ To shorten this, the inner operation can be "mounted" inside the outer operation
448
432
  inner_operation = Orchestra::Operation.new do
449
433
  result :foo do
450
434
  provides :bar
451
- perform do
435
+ execute do
452
436
  bar * 2
453
437
  end
454
438
  end
@@ -471,9 +455,10 @@ class MyObserver
471
455
  case event_name
472
456
  when :operation_entered then "Hello"
473
457
  when :operation_exited then "World!"
474
- when :node_entered then "Hello from within a node"
475
- when :node_exited then "Goodbye from within a node"
458
+ when :step_entered then "Hello from within a step"
459
+ when :step_exited then "Goodbye from within a step"
476
460
  when :error_raised then "Ruh roh!"
461
+ when :service_accessed then "Yay, service call"
477
462
  end
478
463
  end
479
464
  end
@@ -485,17 +470,18 @@ The arguments passed to `update` will vary based on the event:
485
470
  | ------------------------ | ------------------------------------ | --------------------------------- |
486
471
  | `:operation_entered` | The name of the operation starting | Input going into the operation |
487
472
  | `:operation_exited` | The name of the operation finishing | Output of the operation |
488
- | `:node_entered` | The name of the node | Input going into the node |
489
- | `:node_exited` | The name of the node | Output of the node |
473
+ | `:step_entered` | The name of the step | Input going into the step |
474
+ | `:step_exited` | The name of the step | Output of the step |
490
475
  | `:error_raised` | The error itself | `nil` |
476
+ | `:service_accessed` | The name of the service | Recording of the service call |
491
477
 
492
- Embedded performances will inherit the observers of the outer operation.
478
+ All observers attached to the execution of the outer operation will also attach to the inner operation.
493
479
 
494
480
  ## Recording and playing back services
495
481
 
496
- The final main feature of Orchestra is the ability to record the service calls throughout an operation. These recordings can then be used to replay operations. This could be helpful, for instance, to attach to exceptions in your exception logging service so that programmers can replay failed performances on their development environments. In addition, these recordings could be used to drive integration testing. Thus, instead of using separate tools such as ActiveRecord fixtures, FactoryGirl, and VCR for every service dependency, you can test your operations with one single setup artifact.
482
+ The final main feature of Orchestra is the ability to record the service calls throughout an operation. These recordings can then be used to replay operations. This could be helpful, for instance, to attach to exceptions in your exception logging service so that programmers can replay failed executions on their development environments. In addition, these recordings could be used to drive integration testing. Thus, instead of using separate tools such as ActiveRecord fixtures, FactoryGirl, and VCR for every service dependency, you can test your operations with one single setup artifact.
497
483
 
498
- You can record a performance on any `Conductor` by calling `#record` instead of `#perform`:
484
+ You can record a performance put on by any `Conductor` by calling `#record` instead of `#execute`:
499
485
 
500
486
  ```ruby
501
487
  recording = conductor.record InvitationService, :account_name => 'realntl'
@@ -529,6 +515,21 @@ recording = JSON.load json
529
515
  Orchestra.replay_recording InvitationService, recording
530
516
  ```
531
517
 
518
+ ## Installation
519
+
520
+ Add this line to your application's Gemfile:
521
+
522
+ ```ruby
523
+ gem 'ntl-orchestra'
524
+ ```
525
+
526
+ And then execute:
527
+
528
+ $ bundle
529
+
530
+ Or install it yourself as:
531
+
532
+ $ gem install ntl-orchestra
532
533
 
533
534
  ## Contributing
534
535
 
@@ -9,18 +9,18 @@ module Orchestra
9
9
  self.thread_count = Configuration.thread_count
10
10
  end
11
11
 
12
- def perform operation, input = {}
13
- operation.perform self, input do |performance|
14
- copy_observers performance
15
- yield performance if block_given?
12
+ def execute operation, input = {}
13
+ operation.execute self, input do |execution|
14
+ copy_observers execution
15
+ yield execution if block_given?
16
16
  end
17
17
  end
18
18
 
19
19
  def record *args
20
20
  recording = Recording.new
21
21
  add_observer recording
22
- perform *args do |performance|
23
- performance.add_observer recording
22
+ execute *args do |execution|
23
+ execution.add_observer recording
24
24
  end
25
25
  recording
26
26
  ensure
@@ -1,12 +1,12 @@
1
1
  module Orchestra
2
2
  module DSL
3
3
  class ObjectAdapter
4
- def self.build_node object, args = {}
5
- method_name = args.delete :method do :perform end
4
+ def self.build_step object, args = {}
5
+ method_name = args.delete :method do :execute end
6
6
  collection = args.delete :iterates_over
7
7
  adapter_type = determine_type object, method_name
8
8
  adapter = adapter_type.new object, method_name, collection
9
- NodeFactory.build adapter, args
9
+ StepFactory.build adapter, args
10
10
  end
11
11
 
12
12
  def self.determine_type object, method_name
@@ -23,12 +23,12 @@ module Orchestra
23
23
 
24
24
  def initialize object, method_name, collection
25
25
  @collection = collection
26
- @method_name = method_name || :perform
26
+ @method_name = method_name || :execute
27
27
  @object = object
28
28
  end
29
29
 
30
30
  def build_context state
31
- ExecutionContext.new self, state
31
+ ObjectContext.new self, state
32
32
  end
33
33
 
34
34
  def collection?
@@ -36,7 +36,7 @@ module Orchestra
36
36
  end
37
37
 
38
38
  def context_class
39
- @context_class ||= Node.build_execution_context_class dependencies
39
+ @context_class ||= Step.build_execution_context_class dependencies
40
40
  end
41
41
 
42
42
  def dependencies
@@ -56,7 +56,7 @@ module Orchestra
56
56
  end
57
57
  end
58
58
 
59
- def perform state
59
+ def execute state
60
60
  deps = object_method.dependencies
61
61
  input = state.select do |key, _| deps.include? key end
62
62
  Invokr.invoke :method => method_name, :on => object, :with => input
@@ -74,7 +74,7 @@ module Orchestra
74
74
  "#{object} does not implement instance method `#{method_name}'"
75
75
  end
76
76
 
77
- def perform state, maybe_item = nil
77
+ def execute state, maybe_item = nil
78
78
  instance = Invokr.inject object, :using => state
79
79
  args = [method_name]
80
80
  args << maybe_item if collection?
@@ -86,10 +86,10 @@ module Orchestra
86
86
  end
87
87
  end
88
88
 
89
- class NodeFactory
89
+ class StepFactory
90
90
  def self.build *args
91
91
  instance = new *args
92
- instance.build_node
92
+ instance.build_step
93
93
  end
94
94
 
95
95
  attr :adapter, :compact, :provides, :thread_count
@@ -100,12 +100,12 @@ module Orchestra
100
100
  :provides => nil, :compact => false, :thread_count => nil
101
101
  end
102
102
 
103
- def build_node
103
+ def build_step
104
104
  adapter.validate!
105
- Node::DelegateNode.new adapter, build_node_args
105
+ Step::ObjectStep.new adapter, build_step_args
106
106
  end
107
107
 
108
- def build_node_args
108
+ def build_step_args
109
109
  hsh = {
110
110
  :dependencies => adapter.dependencies,
111
111
  :provides => Array(provides),
@@ -115,7 +115,7 @@ module Orchestra
115
115
  end
116
116
  end
117
117
 
118
- class ExecutionContext
118
+ class ObjectContext
119
119
  def initialize adapter, state
120
120
  @__adapter__ = adapter
121
121
  @__state__ = state
@@ -125,8 +125,8 @@ module Orchestra
125
125
  end
126
126
  end
127
127
 
128
- def perform *args
129
- @__adapter__.perform @__state__, *args
128
+ def execute *args
129
+ @__adapter__.execute @__state__, *args
130
130
  end
131
131
  end
132
132