ntl-orchestra 0.9.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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +539 -0
- data/Rakefile +21 -0
- data/bin/rake +16 -0
- data/lib/orchestra/conductor.rb +119 -0
- data/lib/orchestra/configuration.rb +12 -0
- data/lib/orchestra/dsl/nodes.rb +72 -0
- data/lib/orchestra/dsl/object_adapter.rb +134 -0
- data/lib/orchestra/dsl/operations.rb +108 -0
- data/lib/orchestra/errors.rb +44 -0
- data/lib/orchestra/node/output.rb +61 -0
- data/lib/orchestra/node.rb +130 -0
- data/lib/orchestra/operation.rb +49 -0
- data/lib/orchestra/performance.rb +137 -0
- data/lib/orchestra/recording.rb +83 -0
- data/lib/orchestra/run_list.rb +171 -0
- data/lib/orchestra/thread_pool.rb +163 -0
- data/lib/orchestra/util.rb +98 -0
- data/lib/orchestra/version.rb +3 -0
- data/lib/orchestra.rb +35 -0
- data/orchestra.gemspec +26 -0
- data/test/examples/fizz_buzz.rb +32 -0
- data/test/examples/invitation_service.rb +118 -0
- data/test/integration/multithreading_test.rb +38 -0
- data/test/integration/recording_telemetry_test.rb +86 -0
- data/test/integration/replayable_operation_test.rb +53 -0
- data/test/lib/console.rb +103 -0
- data/test/lib/test_runner.rb +19 -0
- data/test/support/telemetry_recorder.rb +49 -0
- data/test/test_helper.rb +16 -0
- data/test/unit/conductor_test.rb +25 -0
- data/test/unit/dsl_test.rb +122 -0
- data/test/unit/node_test.rb +122 -0
- data/test/unit/object_adapter_test.rb +100 -0
- data/test/unit/operation_test.rb +224 -0
- data/test/unit/run_list_test.rb +131 -0
- data/test/unit/thread_pool_test.rb +105 -0
- data/test/unit/util_test.rb +20 -0
- data/tmp/.keep +0 -0
- metadata +159 -0
data/lib/orchestra.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require "invokr"
|
3
|
+
require "observer"
|
4
|
+
require "securerandom"
|
5
|
+
|
6
|
+
module Orchestra
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def configure &block
|
10
|
+
Configuration.module_eval &block
|
11
|
+
end
|
12
|
+
|
13
|
+
def define &block
|
14
|
+
builder = DSL::Operations::Builder.new
|
15
|
+
DSL::Operations::Context.evaluate builder, &block
|
16
|
+
builder.build_operation
|
17
|
+
end
|
18
|
+
|
19
|
+
def perform operation, inputs = {}
|
20
|
+
Conductor.new.perform operation, inputs
|
21
|
+
end
|
22
|
+
|
23
|
+
def replay_recording operation, store, input = {}
|
24
|
+
store = Util.recursively_symbolize store
|
25
|
+
input = input.merge store[:input]
|
26
|
+
svc_recordings = store[:service_recordings]
|
27
|
+
Recording.replay operation, input, svc_recordings
|
28
|
+
end
|
29
|
+
|
30
|
+
load File.expand_path('../orchestra/errors.rb', __FILE__)
|
31
|
+
end
|
32
|
+
|
33
|
+
Dir[File.expand_path '../orchestra/**/*.rb', __FILE__].each do |rb_file|
|
34
|
+
load rb_file
|
35
|
+
end
|
data/orchestra.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'orchestra/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ntl-orchestra"
|
8
|
+
spec.version = Orchestra::VERSION
|
9
|
+
spec.authors = ["ntl"]
|
10
|
+
spec.email = ["nathanladd+github@gmail.com"]
|
11
|
+
spec.summary = %q{Orchestrate complex operations with ease.}
|
12
|
+
spec.description = %q{Orchestra is an orchestration framework for designing complex operations in an object oriented fashion.}
|
13
|
+
spec.homepage = "https://github.com/ntl/orchestra"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = []
|
18
|
+
spec.test_files = spec.files.grep(%r{^test/})
|
19
|
+
spec.require_paths = %w(lib)
|
20
|
+
|
21
|
+
spec.add_dependency "invokr"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
24
|
+
spec.add_development_dependency "pry"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Examples
|
2
|
+
FizzBuzz = Orchestra.define do
|
3
|
+
node :make_array do
|
4
|
+
depends_on :up_to
|
5
|
+
provides :array
|
6
|
+
perform do
|
7
|
+
up_to.times.to_a
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
node :apply_fizzbuzz do
|
12
|
+
iterates_over :array
|
13
|
+
provides :fizzbuzz
|
14
|
+
perform do |num|
|
15
|
+
next if num == 0 # filter 0 from the output
|
16
|
+
str = ''
|
17
|
+
str << "Fizz" if num % 3 == 0
|
18
|
+
str << "Buzz" if num % 5 == 0
|
19
|
+
str << num.to_s if str.empty?
|
20
|
+
str
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
finally :print do
|
25
|
+
depends_on :io
|
26
|
+
iterates_over :fizzbuzz
|
27
|
+
perform do |str|
|
28
|
+
io.puts str
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Examples
|
2
|
+
InvitationService = Orchestra.define do
|
3
|
+
DEFAULT_MESSAGE = "I would really love for you to try out MyApp."
|
4
|
+
ROBOT_FOLLOWER_THRESHHOLD = 500
|
5
|
+
|
6
|
+
node :fetch_followers do
|
7
|
+
depends_on :account_name, :http
|
8
|
+
provides :followers
|
9
|
+
perform do
|
10
|
+
json = http.get "flutter.io", "/users/#{account_name}/followers"
|
11
|
+
JSON.parse json
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
node :fetch_blacklist do
|
16
|
+
depends_on :db
|
17
|
+
provides :blacklist
|
18
|
+
perform do
|
19
|
+
rows = db.execute "SELECT account_name FROM blacklists"
|
20
|
+
rows.map do |row| row.fetch 0 end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
node :remove_blacklisted_followers do
|
25
|
+
depends_on :blacklist
|
26
|
+
modifies :followers
|
27
|
+
perform do
|
28
|
+
followers.reject! do |follower|
|
29
|
+
account_name = follower.fetch 'username'
|
30
|
+
blacklist.include? account_name
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
node :filter_robots do
|
36
|
+
depends_on :http
|
37
|
+
modifies :followers, :collection => true
|
38
|
+
perform do |follower|
|
39
|
+
account_name = follower.fetch 'username'
|
40
|
+
json = http.get "flutter.io", "/users/#{account_name}"
|
41
|
+
account = JSON.load json
|
42
|
+
next unless account['following'] > ROBOT_FOLLOWER_THRESHHOLD
|
43
|
+
next unless account['following'] > (account['followers'] / 2)
|
44
|
+
follower
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
finally :deliver_emails do
|
49
|
+
depends_on :smtp, :message => DEFAULT_MESSAGE
|
50
|
+
iterates_over :followers
|
51
|
+
perform do |follower|
|
52
|
+
email = follower.fetch 'email_address'
|
53
|
+
smtp.deliver message, :to => email
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module InvitationService::TestSetup
|
59
|
+
private
|
60
|
+
|
61
|
+
def build_example_database
|
62
|
+
db = SQLite3::Database.new ':memory:'
|
63
|
+
db.execute <<-SQL
|
64
|
+
CREATE TABLE blacklists (
|
65
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
66
|
+
account_name VARCHAR(255)
|
67
|
+
)
|
68
|
+
SQL
|
69
|
+
db.execute 'INSERT INTO blacklists(account_name) VALUES("mister_ed")'
|
70
|
+
db.execute 'INSERT INTO blacklists(account_name) VALUES("palpatine4")'
|
71
|
+
db
|
72
|
+
end
|
73
|
+
|
74
|
+
def build_example_smtp
|
75
|
+
SMTPCatcher.new
|
76
|
+
end
|
77
|
+
|
78
|
+
def stub_accounts_requests
|
79
|
+
requests = {
|
80
|
+
'mister_ed' => { 'following' => 5192, 'followers' => 4820 },
|
81
|
+
'captain_sheridan' => { 'following' => 12840, 'followers' => 523 },
|
82
|
+
}
|
83
|
+
|
84
|
+
requests.each do |account_name, response|
|
85
|
+
stub = stub_request :get, "http://flutter.io/users/#{account_name}"
|
86
|
+
stub.to_return :body => response.to_json
|
87
|
+
stub.times 1
|
88
|
+
stub
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def stub_followers_request
|
93
|
+
response = [
|
94
|
+
{ 'username' => 'mister_ed', 'email_address' => 'ed@mistered.com' },
|
95
|
+
{ 'username' => 'captain_sheridan', 'email_address' => 'captain_sheridan@babylon5.earth.gov' },
|
96
|
+
]
|
97
|
+
|
98
|
+
followers_stub = stub_request :get, "http://flutter.io/users/realntl/followers"
|
99
|
+
followers_stub.to_return :body => response.to_json
|
100
|
+
followers_stub.times 1
|
101
|
+
followers_stub
|
102
|
+
end
|
103
|
+
|
104
|
+
class SMTPCatcher
|
105
|
+
attr :delivered
|
106
|
+
|
107
|
+
def initialize
|
108
|
+
@delivered = {}
|
109
|
+
end
|
110
|
+
|
111
|
+
def deliver message, args = {}
|
112
|
+
recipient, _ = Orchestra::Util.extract_key_args args, :to
|
113
|
+
delivered[recipient] = message
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class MultithreadingTest < Minitest::Test
|
2
|
+
CustomError = Class.new StandardError
|
3
|
+
|
4
|
+
def setup
|
5
|
+
@operation = Orchestra.define do
|
6
|
+
node :map_thread_ids do
|
7
|
+
iterates_over :list
|
8
|
+
provides :thread_ids
|
9
|
+
perform do |item|
|
10
|
+
raise CustomError, "blow up" if item == :blow_up
|
11
|
+
Thread.current.object_id
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
self.result = :thread_ids
|
16
|
+
end
|
17
|
+
|
18
|
+
@conductor = Orchestra::Conductor.new
|
19
|
+
@conductor.thread_count = 5
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_multithreading
|
23
|
+
list = (1..100).to_a
|
24
|
+
|
25
|
+
thread_ids = @conductor.perform @operation, :list => list
|
26
|
+
|
27
|
+
assert thread_ids.uniq.size > 2, "performance must be spread across threads"
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_exception_during_multithreading
|
31
|
+
list = (1..100).to_a
|
32
|
+
list[23] = :blow_up
|
33
|
+
|
34
|
+
assert_raises CustomError do
|
35
|
+
@conductor.perform @operation, :list => list
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
class RecordingTelemetryTest < Minitest::Test
|
2
|
+
def test_recording_telemetry
|
3
|
+
output = StringIO.new
|
4
|
+
telemetry = {}
|
5
|
+
|
6
|
+
perform_with_telemetry telemetry, output
|
7
|
+
|
8
|
+
assert_equal_telemetry expected_telemetry, telemetry
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def assert_equal_telemetry expected, actual
|
14
|
+
expected.keys.each do |key|
|
15
|
+
assert_equal expected[key], actual[key]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def expected_telemetry
|
20
|
+
{
|
21
|
+
:input => { :up_to => 16 },
|
22
|
+
:movements => {
|
23
|
+
:make_array => {
|
24
|
+
:input => { :up_to => 16 },
|
25
|
+
:output => { :array => [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] },
|
26
|
+
},
|
27
|
+
:apply_fizzbuzz => {
|
28
|
+
:input => { :array => [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] },
|
29
|
+
:output => {
|
30
|
+
:fizzbuzz => [
|
31
|
+
"1",
|
32
|
+
"2",
|
33
|
+
"Fizz",
|
34
|
+
"4",
|
35
|
+
"Buzz",
|
36
|
+
"Fizz",
|
37
|
+
"7",
|
38
|
+
"8",
|
39
|
+
"Fizz",
|
40
|
+
"Buzz",
|
41
|
+
"11",
|
42
|
+
"Fizz",
|
43
|
+
"13",
|
44
|
+
"14",
|
45
|
+
"FizzBuzz",
|
46
|
+
],
|
47
|
+
},
|
48
|
+
},
|
49
|
+
:print => {
|
50
|
+
:input => {
|
51
|
+
:fizzbuzz => [
|
52
|
+
"1",
|
53
|
+
"2",
|
54
|
+
"Fizz",
|
55
|
+
"4",
|
56
|
+
"Buzz",
|
57
|
+
"Fizz",
|
58
|
+
"7",
|
59
|
+
"8",
|
60
|
+
"Fizz",
|
61
|
+
"Buzz",
|
62
|
+
"11",
|
63
|
+
"Fizz",
|
64
|
+
"13",
|
65
|
+
"14",
|
66
|
+
"FizzBuzz",
|
67
|
+
],
|
68
|
+
},
|
69
|
+
:output => { :print => [] },
|
70
|
+
}
|
71
|
+
},
|
72
|
+
:output => [],
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
def perform_with_telemetry telemetry, io
|
77
|
+
conductor = Orchestra::Conductor.new :io => io
|
78
|
+
|
79
|
+
conductor.add_observer TelemetryRecorder.new telemetry
|
80
|
+
|
81
|
+
conductor.perform(
|
82
|
+
Examples::FizzBuzz,
|
83
|
+
:up_to => 16,
|
84
|
+
)
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class ReplayableOperationTest < Minitest::Test
|
2
|
+
include Examples::InvitationService::TestSetup
|
3
|
+
|
4
|
+
def test_replaying_an_operation_from_a_previous_recording
|
5
|
+
# Perform the operation against real services, saving a recording
|
6
|
+
recording = perform_for_real
|
7
|
+
|
8
|
+
# Write the recording out to a file. In this case, a StringIO is used for
|
9
|
+
# simplicity, and we serialize into JSON
|
10
|
+
file = StringIO.new
|
11
|
+
file.write JSON.dump recording
|
12
|
+
file.rewind
|
13
|
+
|
14
|
+
# Replay the operation, directing SMTP to an alternative service object
|
15
|
+
smtp_service = build_example_smtp
|
16
|
+
Orchestra.replay_recording(
|
17
|
+
Examples::InvitationService,
|
18
|
+
JSON.load(file.read),
|
19
|
+
:smtp => smtp_service,
|
20
|
+
)
|
21
|
+
|
22
|
+
# While replaying, the operation delivered all email using the alterantive
|
23
|
+
# SMTP service object we passed in
|
24
|
+
assert_equal(
|
25
|
+
["captain_sheridan@babylon5.earth.gov"],
|
26
|
+
smtp_service.delivered.keys,
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def perform_for_real
|
33
|
+
mock_smtp = build_example_smtp
|
34
|
+
db = build_example_database
|
35
|
+
stub_followers_request
|
36
|
+
stub_accounts_requests
|
37
|
+
|
38
|
+
conductor = Orchestra::Conductor.new(
|
39
|
+
:db => db,
|
40
|
+
:http => Net::HTTP,
|
41
|
+
:smtp => mock_smtp,
|
42
|
+
)
|
43
|
+
|
44
|
+
recording = conductor.record(
|
45
|
+
Examples::InvitationService,
|
46
|
+
:account_name => 'realntl',
|
47
|
+
)
|
48
|
+
|
49
|
+
db.close
|
50
|
+
|
51
|
+
recording.to_h
|
52
|
+
end
|
53
|
+
end
|
data/test/lib/console.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
module Console
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def load
|
5
|
+
ENV['N'] ||= '4'
|
6
|
+
Bundler.require "debugger_#{RUBY_ENGINE}"
|
7
|
+
define_reload
|
8
|
+
define_rake
|
9
|
+
load_examples
|
10
|
+
clean_backtraces
|
11
|
+
puts <<-MESSAGE
|
12
|
+
Debug console. Type `rake` to run the test suite. `bin/rake` also works for
|
13
|
+
develompent environments that rely on binstubs.
|
14
|
+
|
15
|
+
The example operations located in `test/examples` are loaded for you. To try one
|
16
|
+
out:
|
17
|
+
|
18
|
+
[1] pry(Orchestra)> Orchestra.perform Examples::FizzBuzz, :up_to => 16, :io => $stdout
|
19
|
+
|
20
|
+
See the README.md as well as the examples themselves for more information. You
|
21
|
+
will need to reload the examples after raking (or invoking reload!)
|
22
|
+
MESSAGE
|
23
|
+
end
|
24
|
+
|
25
|
+
def load_examples
|
26
|
+
Dir["test/examples/**/*.rb"].each do |example_rb| Kernel.load example_rb end
|
27
|
+
end
|
28
|
+
|
29
|
+
def define_reload
|
30
|
+
Pry::Commands.block_command "reload!", "Reload gem code" do
|
31
|
+
puts "Reloading..."
|
32
|
+
Object.send :remove_const, :Examples if Object.const_defined? :Examples
|
33
|
+
Object.send :remove_const, :Orchestra if Object.const_defined? :Orchestra
|
34
|
+
Kernel.load "lib/orchestra.rb"
|
35
|
+
Console.load_examples
|
36
|
+
orchestra = Object.const_get :Orchestra
|
37
|
+
_pry_.binding_stack.push orchestra.__binding__
|
38
|
+
_pry_.binding_stack.shift
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def define_rake
|
43
|
+
Pry::Commands.create_command %r{(?:bin/)?rake}, keep_retval: true do
|
44
|
+
description "Run the test suite"
|
45
|
+
|
46
|
+
def process
|
47
|
+
run "reload!"
|
48
|
+
|
49
|
+
status = TestRunner.run_in_subprocess
|
50
|
+
|
51
|
+
build_passed = status.exitstatus == 0
|
52
|
+
operator = args.shift
|
53
|
+
run_system_command(build_passed, operator, args) if operator
|
54
|
+
build_passed
|
55
|
+
end
|
56
|
+
|
57
|
+
def run_system_command build_passed, operator, system_args
|
58
|
+
if operator == '&&'
|
59
|
+
system *system_args if build_passed
|
60
|
+
elsif operator == '||' && !build_passed
|
61
|
+
system *system_args unless build_passed
|
62
|
+
else
|
63
|
+
raise ArgumentError, "Must supply either '&&' or '||' operators, followed by a shell command"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def clean_backtraces
|
70
|
+
if RUBY_ENGINE == "rbx"
|
71
|
+
Exception.class_eval do
|
72
|
+
def render_with_filtering *args
|
73
|
+
io = args[1] || STDERR
|
74
|
+
args[1] = BacktraceFiltering::Rubinius.new io
|
75
|
+
render_without_filtering *args
|
76
|
+
end
|
77
|
+
alias_method :render_without_filtering, :render
|
78
|
+
alias_method :render, :render_with_filtering
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
module BacktraceFiltering
|
84
|
+
REGEX = %r{(?:lib/orchestra|test)}i
|
85
|
+
|
86
|
+
class Rubinius
|
87
|
+
def initialize io
|
88
|
+
@io = io
|
89
|
+
end
|
90
|
+
|
91
|
+
def puts message = "\n"
|
92
|
+
dont_filter_yet = true
|
93
|
+
message.each_line do |line|
|
94
|
+
if line.match %r{ at }
|
95
|
+
next unless dont_filter_yet or line.match REGEX
|
96
|
+
dont_filter_yet = false
|
97
|
+
end
|
98
|
+
@io.puts line
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module TestRunner
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def run_in_subprocess
|
5
|
+
pid = fork do run end
|
6
|
+
_, status = Process.wait2 pid
|
7
|
+
status
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
Bundler.require :test
|
12
|
+
load 'test/test_helper.rb'
|
13
|
+
tests = Dir["test/**/*_test.rb"]
|
14
|
+
tests.select! do |test| test == ENV['TEST'] end if ENV['TEST']
|
15
|
+
tests.each &method(:load)
|
16
|
+
argv = (ENV['TESTOPTS'] || '').split %r{[[:space:]]+}
|
17
|
+
exit Minitest.run argv
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class TelemetryRecorder
|
2
|
+
def initialize store
|
3
|
+
@store = store
|
4
|
+
@current_operation = nil
|
5
|
+
@embedded = false
|
6
|
+
end
|
7
|
+
|
8
|
+
def update message, *payload
|
9
|
+
method = "handle_#{message}"
|
10
|
+
public_send method, *payload if respond_to? method
|
11
|
+
end
|
12
|
+
|
13
|
+
def embedded?
|
14
|
+
@embedded
|
15
|
+
end
|
16
|
+
|
17
|
+
def handle_operation_entered operation_name, input
|
18
|
+
return if embedded?
|
19
|
+
@nodes = Hash.new do |hsh, key| hsh[key] = {} end
|
20
|
+
@store.update(
|
21
|
+
:input => input,
|
22
|
+
:movements => @nodes,
|
23
|
+
:performance_name => operation_name,
|
24
|
+
:service_calls => [],
|
25
|
+
)
|
26
|
+
@embedded = true
|
27
|
+
end
|
28
|
+
|
29
|
+
def handle_operation_exited operation_name, output
|
30
|
+
@store[:output] = output
|
31
|
+
@embedded = false
|
32
|
+
end
|
33
|
+
|
34
|
+
def handle_node_entered name, input
|
35
|
+
@nodes[name][:input] = input
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle_node_exited name, output
|
39
|
+
@nodes[name][:output] = output
|
40
|
+
end
|
41
|
+
|
42
|
+
def handle_error_raised error
|
43
|
+
@store[:error] = error
|
44
|
+
end
|
45
|
+
|
46
|
+
def handle_service_accessed service_name, record
|
47
|
+
@store[:service_calls].<< record.merge :service => service_name
|
48
|
+
end
|
49
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "json"
|
2
|
+
require "minitest/hell"
|
3
|
+
require "minitest/mock"
|
4
|
+
require "stringio"
|
5
|
+
require "webmock/minitest"
|
6
|
+
|
7
|
+
Dir['test/examples/**/*.rb'].each &method(:load) unless defined? Examples
|
8
|
+
Dir['test/support/**/*.rb'].each &method(:load)
|
9
|
+
|
10
|
+
WebMock.disable_net_connect!
|
11
|
+
|
12
|
+
class Minitest::Test
|
13
|
+
def before_setup
|
14
|
+
Orchestra::Configuration.reset
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class ConductorTest < Minitest::Test
|
2
|
+
def test_defaults_to_single_thread
|
3
|
+
conductor = Orchestra::Conductor.new
|
4
|
+
|
5
|
+
assert_equal 1, conductor.thread_count
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_configuring_thread_pool_globally
|
9
|
+
Orchestra.configure do |defaults|
|
10
|
+
defaults.thread_count = 5
|
11
|
+
end
|
12
|
+
|
13
|
+
conductor = Orchestra::Conductor.new
|
14
|
+
|
15
|
+
assert_equal 5, conductor.thread_count
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_configuring_thread_pool_on_an_instance
|
19
|
+
conductor = Orchestra::Conductor.new
|
20
|
+
|
21
|
+
conductor.thread_count = 5
|
22
|
+
|
23
|
+
assert_equal 5, conductor.thread_count
|
24
|
+
end
|
25
|
+
end
|