wukong 3.0.0.pre2 → 3.0.0.pre3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +13 -0
- data/README.md +182 -6
- data/bin/wu-local +13 -5
- data/bin/wu-server +1 -1
- data/examples/Gemfile +2 -1
- data/examples/basic/string_reverser.rb +23 -0
- data/examples/{tiny_count.rb → basic/tiny_count.rb} +0 -0
- data/examples/{word_count → basic/word_count}/accumulator.rb +0 -0
- data/examples/{word_count → basic/word_count}/tokenizer.rb +0 -0
- data/examples/{word_count → basic/word_count}/word_count.rb +0 -0
- data/examples/deploy_pack/Gemfile +7 -0
- data/examples/deploy_pack/README.md +6 -0
- data/examples/{text/latinize_text.rb → deploy_pack/a/b/c/.gitkeep} +0 -0
- data/examples/deploy_pack/app/processors/string_reverser.rb +5 -0
- data/examples/deploy_pack/config/environment.rb +1 -0
- data/examples/{dataflow → dsl/dataflow}/fibonacci_series.rb +0 -0
- data/examples/dsl/dataflow/scraper_macro_flow.rb +28 -0
- data/examples/{dataflow → dsl/dataflow}/simple.rb +0 -0
- data/examples/{dataflow → dsl/dataflow}/telegram.rb +0 -0
- data/examples/{workflow → dsl/workflow}/cherry_pie.dot +0 -0
- data/examples/{workflow → dsl/workflow}/cherry_pie.md +0 -0
- data/examples/{workflow → dsl/workflow}/cherry_pie.png +0 -0
- data/examples/{workflow → dsl/workflow}/cherry_pie.rb +0 -0
- data/examples/empty/.gitkeep +0 -0
- data/examples/graph/implied_geolocation/README.md +63 -0
- data/examples/graph/{minimum_spanning_tree.rb → minimum_spanning_tree/airfares_graphviz.rb} +0 -0
- data/examples/munging/airline_flights/indexable.rb +75 -0
- data/examples/munging/airline_flights/indexable_spec.rb +90 -0
- data/examples/munging/geo/geonames_models.rb +29 -0
- data/examples/munging/wikipedia/dbpedia/dbpedia_common.rb +1 -0
- data/examples/munging/wikipedia/dbpedia/extract_links-cruft.rb +66 -0
- data/examples/munging/wikipedia/dbpedia/extract_links.rb +213 -146
- data/examples/rake_helper.rb +12 -0
- data/examples/ruby_project/Gemfile +7 -0
- data/examples/ruby_project/README.md +6 -0
- data/examples/ruby_project/a/b/c/.gitkeep +0 -0
- data/examples/serverlogs/geo_ip_mapping/munge_geolite.rb +82 -0
- data/examples/serverlogs/models/logline.rb +102 -0
- data/examples/{dataflow/parse_apache_logs.rb → serverlogs/parser/apache_parser_widget.rb} +0 -0
- data/examples/serverlogs/visit_paths/common.rb +4 -0
- data/examples/serverlogs/visit_paths/page_counts.pig +48 -0
- data/examples/serverlogs/visit_paths/serverlogs-01-parse-script.rb +11 -0
- data/examples/serverlogs/visit_paths/serverlogs-02-histograms-full.rb +31 -0
- data/examples/serverlogs/visit_paths/serverlogs-02-histograms-mapper.rb +12 -0
- data/examples/serverlogs/visit_paths/serverlogs-03-breadcrumbs-full.rb +67 -0
- data/examples/serverlogs/visit_paths/serverlogs-04-page_page_edges-full.rb +38 -0
- data/examples/text/{pig_latin.rb → pig_latin/pig_latinizer.rb} +0 -0
- data/examples/{dataflow/pig_latinizer.rb → text/pig_latin/pig_latinizer_widget.rb} +0 -0
- data/lib/hanuman/graph.rb +6 -1
- data/lib/wu/geo.rb +4 -0
- data/lib/wu/geo/geo_grids.numbers +0 -0
- data/lib/wu/geo/geolocated.rb +331 -0
- data/lib/wu/geo/quadtile.rb +69 -0
- data/{examples → lib/wu}/graph/union_find.rb +0 -0
- data/lib/wu/model/reconcilable.rb +63 -0
- data/{examples/munging/wikipedia/utils/munging_utils.rb → lib/wu/munging.rb} +7 -4
- data/lib/wu/social/models/twitter.rb +31 -0
- data/{examples/models/wikipedia.rb → lib/wu/wikipedia/models.rb} +0 -0
- data/lib/wukong.rb +9 -4
- data/lib/wukong/boot.rb +10 -1
- data/lib/wukong/driver.rb +65 -71
- data/lib/wukong/logger.rb +93 -0
- data/lib/wukong/processor.rb +38 -29
- data/lib/wukong/runner.rb +144 -0
- data/lib/wukong/server.rb +119 -0
- data/lib/wukong/spec_helpers.rb +1 -0
- data/lib/wukong/spec_helpers/integration_driver.rb +22 -9
- data/lib/wukong/spec_helpers/integration_driver_matchers.rb +26 -4
- data/lib/wukong/spec_helpers/processor_helpers.rb +4 -10
- data/lib/wukong/spec_helpers/shared_examples.rb +12 -13
- data/lib/wukong/version.rb +1 -1
- data/lib/wukong/widget/processors.rb +13 -0
- data/lib/wukong/widget/serializers.rb +55 -65
- data/lib/wukong/widgets.rb +0 -2
- data/spec/hanuman/graph_spec.rb +14 -0
- data/spec/spec_helper.rb +4 -30
- data/spec/support/{wukong_test_helpers.rb → example_test_helpers.rb} +29 -2
- data/spec/support/integration_helper.rb +38 -0
- data/spec/support/model_test_helpers.rb +115 -0
- data/spec/wu/geo/geolocated_spec.rb +247 -0
- data/spec/wu/model/reconcilable_spec.rb +152 -0
- data/spec/wukong/widget/processors_spec.rb +0 -1
- data/spec/wukong/widget/serializers_spec.rb +88 -62
- data/spec/wukong/wu_local_spec.rb +125 -0
- data/wukong.gemspec +3 -16
- metadata +72 -266
- data/examples/dataflow/apache_log_line.rb +0 -100
- data/examples/jabberwocky.txt +0 -36
- data/examples/munging/Gemfile +0 -8
- data/examples/munging/airline_flights/airline.rb +0 -57
- data/examples/munging/airline_flights/airport.rb +0 -211
- data/examples/munging/airline_flights/flight.rb +0 -156
- data/examples/munging/airline_flights/models.rb +0 -4
- data/examples/munging/airline_flights/parse.rb +0 -26
- data/examples/munging/airline_flights/route.rb +0 -35
- data/examples/munging/airline_flights/timezone_fixup.rb +0 -62
- data/examples/munging/airports/40_wbans.txt +0 -40
- data/examples/munging/airports/filter_weather_reports.rb +0 -37
- data/examples/munging/airports/join.pig +0 -31
- data/examples/munging/airports/to_tsv.rb +0 -33
- data/examples/munging/airports/usa_wbans.pig +0 -19
- data/examples/munging/airports/usa_wbans.txt +0 -2157
- data/examples/munging/airports/wbans.pig +0 -19
- data/examples/munging/airports/wbans.txt +0 -2310
- data/examples/munging/rake_helper.rb +0 -62
- data/examples/munging/weather/.gitignore +0 -1
- data/examples/munging/weather/Gemfile +0 -4
- data/examples/munging/weather/Rakefile +0 -28
- data/examples/munging/weather/extract_ish.rb +0 -13
- data/examples/munging/weather/models/weather.rb +0 -119
- data/examples/munging/weather/utils/noaa_downloader.rb +0 -46
- data/examples/munging/wikipedia/README.md +0 -34
- data/examples/munging/wikipedia/Rakefile +0 -193
- data/examples/munging/wikipedia/n1_subuniverse/n1_nodes.pig +0 -18
- data/examples/munging/wikipedia/page_metadata/extract_page_metadata.rb +0 -21
- data/examples/munging/wikipedia/page_metadata/extract_page_metadata.rb.old +0 -27
- data/examples/munging/wikipedia/pagelinks/augment_pagelinks.pig +0 -29
- data/examples/munging/wikipedia/pagelinks/extract_pagelinks.rb +0 -14
- data/examples/munging/wikipedia/pagelinks/extract_pagelinks.rb.old +0 -25
- data/examples/munging/wikipedia/pagelinks/undirect_pagelinks.pig +0 -29
- data/examples/munging/wikipedia/pageviews/augment_pageviews.pig +0 -32
- data/examples/munging/wikipedia/pageviews/extract_pageviews.rb +0 -85
- data/examples/munging/wikipedia/pig_style_guide.md +0 -25
- data/examples/munging/wikipedia/redirects/redirects_page_metadata.pig +0 -19
- data/examples/munging/wikipedia/subuniverse/sub_articles.pig +0 -23
- data/examples/munging/wikipedia/subuniverse/sub_page_metadata.pig +0 -24
- data/examples/munging/wikipedia/subuniverse/sub_pagelinks_from.pig +0 -22
- data/examples/munging/wikipedia/subuniverse/sub_pagelinks_into.pig +0 -22
- data/examples/munging/wikipedia/subuniverse/sub_pagelinks_within.pig +0 -26
- data/examples/munging/wikipedia/subuniverse/sub_pageviews.pig +0 -29
- data/examples/munging/wikipedia/subuniverse/sub_undirected_pagelinks_within.pig +0 -24
- data/examples/munging/wikipedia/utils/get_namespaces.rb +0 -86
- data/examples/munging/wikipedia/utils/namespaces.json +0 -1
- data/examples/string_reverser.rb +0 -26
- data/examples/twitter/locations.rb +0 -29
- data/examples/twitter/models.rb +0 -24
- data/examples/twitter/pt1-fiddle.pig +0 -8
- data/examples/twitter/pt2-simple_parse.pig +0 -31
- data/examples/twitter/pt2-simple_parse.rb +0 -18
- data/examples/twitter/pt3-join_on_zips.pig +0 -39
- data/examples/twitter/pt4-strong_links.rb +0 -20
- data/examples/twitter/pt5-lnglat_and_strong_links.pig +0 -16
- data/examples/twitter/states.tsv +0 -50
- data/examples/workflow/package_gem.rb +0 -55
- data/lib/wukong/widget/sink.rb +0 -16
- data/lib/wukong/widget/source.rb +0 -14
@@ -0,0 +1,144 @@
|
|
1
|
+
module Wukong
|
2
|
+
module CommandlineRunner
|
3
|
+
|
4
|
+
def exit_with_status(status, options = {})
|
5
|
+
warn options[:msg] if options[:msg]
|
6
|
+
@env.dump_help if options[:show_help]
|
7
|
+
exit(status)
|
8
|
+
end
|
9
|
+
|
10
|
+
def env= settings
|
11
|
+
@env = settings
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.included(base)
|
15
|
+
base.extend(ClassMethods)
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
|
20
|
+
def usage(usg = nil)
|
21
|
+
return @usage if usg.nil?
|
22
|
+
@usage = usg
|
23
|
+
end
|
24
|
+
|
25
|
+
def desc(dsc = nil)
|
26
|
+
return @description if dsc.nil?
|
27
|
+
@decription = desc
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_param(*args)
|
31
|
+
defined_params << args
|
32
|
+
end
|
33
|
+
|
34
|
+
def defined_params
|
35
|
+
@defined_params ||= []
|
36
|
+
end
|
37
|
+
|
38
|
+
def base_config(conf = nil)
|
39
|
+
return @base_configuration if conf.nil?
|
40
|
+
@base_configuration = conf
|
41
|
+
end
|
42
|
+
|
43
|
+
def decorate_environment! env
|
44
|
+
usg = self.usage
|
45
|
+
env.define_singleton_method(:usage){ usg }
|
46
|
+
env.description = self.desc
|
47
|
+
defined_params.each{ |params| env.send(:define, *params) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def in_deploy_pack?
|
51
|
+
return @in_deploy_pack unless @in_deploy_pack.nil?
|
52
|
+
@in_deploy_pack = (find_deploy_pack_dir != '/')
|
53
|
+
end
|
54
|
+
|
55
|
+
def find_deploy_pack_dir
|
56
|
+
return @deploy_pack_dir if @deploy_pack_dir
|
57
|
+
wd = Dir.pwd
|
58
|
+
parent = File.dirname(wd)
|
59
|
+
until wd == parent
|
60
|
+
return wd if File.exist?(File.join(wd, 'Gemfile')) && File.exist?(File.join(wd, 'config', 'environment.rb'))
|
61
|
+
wd = parent
|
62
|
+
parent = File.dirname(wd)
|
63
|
+
end
|
64
|
+
@deploy_pack_dir = wd
|
65
|
+
end
|
66
|
+
|
67
|
+
def run!(*run_params)
|
68
|
+
settings = base_configuration || Configliere::Param.use(:commandline)
|
69
|
+
boot_environment(settings) if in_deploy_pack?
|
70
|
+
runner = new(*run_params)
|
71
|
+
runner.env = settings.resolve!
|
72
|
+
runner.run(*settings.rest)
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class LocalRunner
|
79
|
+
include CommandlineRunner
|
80
|
+
base_configuration
|
81
|
+
|
82
|
+
usage 'usage: wu-local PROCESSOR|FLOW [ --param=value | -p value | --param | -p]'
|
83
|
+
desc <<EOF
|
84
|
+
wu-local is a tool for running Wukong processors and flows locally on
|
85
|
+
the command-line. Use wu-local by passing it a processor and feeding
|
86
|
+
in some data:
|
87
|
+
|
88
|
+
$ echo 'UNIX is Clever and Fun...' | wu-local tokenizer.rb
|
89
|
+
UNIX
|
90
|
+
is
|
91
|
+
Clever
|
92
|
+
and
|
93
|
+
Fun
|
94
|
+
|
95
|
+
If your processors have named fields you can pass them in as
|
96
|
+
arguments:
|
97
|
+
|
98
|
+
$ echo 'UNIX is clever and fun...' | wu-local tokenizer.rb --min_length=4
|
99
|
+
UNIX
|
100
|
+
Clever
|
101
|
+
|
102
|
+
You can chain processors and calls to wu-local together:
|
103
|
+
|
104
|
+
$ echo 'UNIX is clever and fun...' | wu-local tokenizer.rb --min_length=4 | wu-local downcaser.rb
|
105
|
+
unix
|
106
|
+
clever
|
107
|
+
|
108
|
+
Which is a good way to develop a combined data flow which you can
|
109
|
+
again test locally:
|
110
|
+
|
111
|
+
$ echo 'UNIX is clever and fun...' | wu-local tokenize_and_downcase_big_words.rb
|
112
|
+
unix
|
113
|
+
clever
|
114
|
+
EOF
|
115
|
+
|
116
|
+
add_param :run, description: "Name of the processor or dataflow to use. Defaults to basename of the given path.", flag: 'r'
|
117
|
+
add_param :tcp_server, description: "Run locally as a server using provided TCP port", default: false, flag: 't'
|
118
|
+
|
119
|
+
def run *args
|
120
|
+
arg = args.first
|
121
|
+
case
|
122
|
+
when arg.nil?
|
123
|
+
exit_with_status(1, show_help: true, msg: "Must pass a processor name or path to a processor file. Got <#{arg}>")
|
124
|
+
when Wukong.registry.registered?(arg.to_sym)
|
125
|
+
processor = arg.to_sym
|
126
|
+
when File.exist?(arg)
|
127
|
+
load arg
|
128
|
+
processor = @env.run || File.basename(arg, '.rb')
|
129
|
+
else
|
130
|
+
exit_with_status(2, show_help: true, msg: "Must pass a processor name or path to a processor file. Got <#{arg}>")
|
131
|
+
end
|
132
|
+
run_em_server(processor, @env)
|
133
|
+
end
|
134
|
+
|
135
|
+
def run_em_server(processor, env)
|
136
|
+
EM.run do
|
137
|
+
env.tcp_server ? Wu::TCPServer.start(processor, env) : Wu::StdioServer.start(processor, env)
|
138
|
+
end
|
139
|
+
rescue Wu::Error => e
|
140
|
+
exit_with_status(3, msg: e.backtrace.join("\n"))
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Wukong
|
2
|
+
module EventMachineServer
|
3
|
+
include DriverMethods
|
4
|
+
|
5
|
+
def self.included klass
|
6
|
+
klass.class_eval do
|
7
|
+
attr_accessor :dataflow, :settings
|
8
|
+
|
9
|
+
def self.add_signal_traps
|
10
|
+
Signal.trap('INT') { log.info 'Received SIGINT. Stopping.' ; EM.stop }
|
11
|
+
Signal.trap('TERM') { log.info 'Received SIGTERM. Stopping.' ; EM.stop }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(label, settings)
|
17
|
+
super
|
18
|
+
@settings = settings
|
19
|
+
@dataflow = construct_dataflow(label, settings)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
class StdioServer < EM::P::LineAndTextProtocol
|
25
|
+
include EventMachineServer
|
26
|
+
include Processor::StdoutProcessor
|
27
|
+
include Logging
|
28
|
+
|
29
|
+
def self.start(label, settings = {})
|
30
|
+
EM.attach($stdin, self, label, settings)
|
31
|
+
end
|
32
|
+
|
33
|
+
def post_init
|
34
|
+
self.class.add_signal_traps
|
35
|
+
setup_dataflow
|
36
|
+
end
|
37
|
+
|
38
|
+
def receive_line line
|
39
|
+
driver.send_through_dataflow(line)
|
40
|
+
end
|
41
|
+
|
42
|
+
def unbind
|
43
|
+
finalize_and_stop_dataflow
|
44
|
+
EM.stop
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
class TCPServer < EM::P::LineAndTextProtocol
|
50
|
+
include EventMachineServer
|
51
|
+
include Processor::BufferedProcessor
|
52
|
+
include Logging
|
53
|
+
|
54
|
+
def self.start(label, settings = {})
|
55
|
+
host = settings[:host] || Socket.gethostname
|
56
|
+
port = settings[:port] || 9000
|
57
|
+
EM.start_server(host, port, self, label, settings)
|
58
|
+
log.info "Server started on #{host} on port #{port}"
|
59
|
+
add_signal_traps
|
60
|
+
end
|
61
|
+
|
62
|
+
def post_init
|
63
|
+
port, ip = Socket.unpack_sockaddr_in(get_peername)
|
64
|
+
log.info "Connected to #{ip} on #{port}"
|
65
|
+
setup_dataflow
|
66
|
+
end
|
67
|
+
|
68
|
+
def receive_line line
|
69
|
+
@buffer = []
|
70
|
+
operation = proc { driver.send_through_dataflow(line) }
|
71
|
+
callback = proc { flush_buffer @buffer }
|
72
|
+
EM.defer(operation, callback)
|
73
|
+
end
|
74
|
+
|
75
|
+
def flush_buffer records
|
76
|
+
send_data(records.join("\n") + "\n")
|
77
|
+
records.clear
|
78
|
+
end
|
79
|
+
|
80
|
+
def unbind
|
81
|
+
EM.stop
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class StupidServer
|
88
|
+
|
89
|
+
attr_accessor :dataflow, :settings
|
90
|
+
|
91
|
+
def initialize(label, settings)
|
92
|
+
@settings = settings
|
93
|
+
builder = (Wukong.registry.retrieve(label.to_sym) or raise Wukong::Error.new("No such processor or dataflow: #{label}"))
|
94
|
+
dataflow = builder.build(settings)
|
95
|
+
@dataflow = dataflow.respond_to?(:stages) ? dataflow.directed_sort.map{ |name| dataflow.stages[name] } : [ dataflow ]
|
96
|
+
@dataflow << self
|
97
|
+
end
|
98
|
+
|
99
|
+
def dumb_driver
|
100
|
+
@dumb_driver ||= Wukong::Driver.new(dataflow)
|
101
|
+
end
|
102
|
+
|
103
|
+
def stop() ; end
|
104
|
+
def setup() ; end
|
105
|
+
def process(record) $stdout.puts record ; end
|
106
|
+
|
107
|
+
def run!
|
108
|
+
dataflow.each(&:setup)
|
109
|
+
|
110
|
+
while line = $stdin.readline.chomp rescue nil do
|
111
|
+
dumb_driver.send_through_dataflow(line)
|
112
|
+
end
|
113
|
+
dataflow.each do |stage|
|
114
|
+
stage.finalize(&dumb_driver.advance(stage)) if stage.respond_to?(:finalize)
|
115
|
+
stage.stop
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
data/lib/wukong/spec_helpers.rb
CHANGED
@@ -87,15 +87,17 @@ module Wukong
|
|
87
87
|
# @return [true, false]
|
88
88
|
def run!
|
89
89
|
return false if ran?
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
90
|
+
FileUtils.cd(cwd) do
|
91
|
+
Open3.popen3(env, cmd) do |i, o, e, wait_thr|
|
92
|
+
self.pid = wait_thr.pid
|
93
|
+
|
94
|
+
@inputs.each { |input| i.puts(input) }
|
95
|
+
i.close
|
96
|
+
|
97
|
+
self.stdout = o.read
|
98
|
+
self.stderr = e.read
|
99
|
+
self.exit_code = wait_thr.value.to_i
|
100
|
+
end
|
99
101
|
end
|
100
102
|
@ran = true
|
101
103
|
end
|
@@ -116,6 +118,17 @@ module Wukong
|
|
116
118
|
@inputs.concat(events)
|
117
119
|
self
|
118
120
|
end
|
121
|
+
alias_method :<, :on
|
122
|
+
|
123
|
+
def in dir
|
124
|
+
@cwd = dir
|
125
|
+
self
|
126
|
+
end
|
127
|
+
|
128
|
+
def using env
|
129
|
+
@env = env
|
130
|
+
self
|
131
|
+
end
|
119
132
|
|
120
133
|
def env
|
121
134
|
ENV.to_hash.merge(@env || {})
|
@@ -80,7 +80,7 @@ module Wukong
|
|
80
80
|
|
81
81
|
# :nodoc:
|
82
82
|
def failure_message
|
83
|
-
"Ran\n\n
|
83
|
+
"Ran\n\n#{formatted_env}\n#{formatted_command}\n\nand expected #{output_description}\n\n#{formatted_output}\n\nto #{match_type}\n\n #{failed_expectation}#{formatted_error_output}"
|
84
84
|
end
|
85
85
|
|
86
86
|
# :nodoc:
|
@@ -93,9 +93,26 @@ module Wukong
|
|
93
93
|
output.split("\n").map { |line| ' ' + line }.join("\n")
|
94
94
|
end
|
95
95
|
|
96
|
+
# :nodoc:
|
97
|
+
def formatted_error_output
|
98
|
+
output_description.to_s =~ /stderr/ ? "\n\nSTDOUT was\n\n#{driver.stdout}" : "\n\nSTDERR was\n\n#{driver.stderr}"
|
99
|
+
end
|
100
|
+
|
96
101
|
# :nodoc:
|
97
102
|
def formatted_command
|
98
|
-
"$ #{driver.cmd}"
|
103
|
+
" $ #{driver.cmd}"
|
104
|
+
end
|
105
|
+
|
106
|
+
# :nodoc:
|
107
|
+
def formatted_env
|
108
|
+
[' {'].tap do |lines|
|
109
|
+
driver.env.each_pair do |key, value|
|
110
|
+
if key =~ /^(BUNDLE_GEMFILE|PATH|RUBYLIB)$/
|
111
|
+
lines << " #{key} => #{value},"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
lines << ' }'
|
115
|
+
end.join("\n")
|
99
116
|
end
|
100
117
|
|
101
118
|
# :nodoc:
|
@@ -182,12 +199,12 @@ module Wukong
|
|
182
199
|
|
183
200
|
# :nodoc:
|
184
201
|
def failure_message
|
185
|
-
"Ran\n\n
|
202
|
+
"Ran\n\n#{formatted_env}\n#{formatted_command}\n\nexpecting #{expected_exit_code_description} Got #{driver.exit_code} instead.#{formatted_error_output}"
|
186
203
|
end
|
187
204
|
|
188
205
|
# :nodoc:
|
189
206
|
def negative_failure_message
|
190
|
-
"Ran\n\n
|
207
|
+
"Ran\n\n#{formatted_env}\n#{formatted_command}\n\nNOT expecting #{expected_exit_code_description}.#{formatted_error_output}"
|
191
208
|
end
|
192
209
|
|
193
210
|
# :nodoc:
|
@@ -214,6 +231,11 @@ module Wukong
|
|
214
231
|
"exit with #{expected_exit_code_description}"
|
215
232
|
end
|
216
233
|
|
234
|
+
# :nodoc:
|
235
|
+
def output_description
|
236
|
+
"STDOUT"
|
237
|
+
end
|
238
|
+
|
217
239
|
end
|
218
240
|
end
|
219
241
|
end
|
@@ -54,18 +54,13 @@ module Wukong
|
|
54
54
|
# ...
|
55
55
|
# end
|
56
56
|
def processor *args, &block
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
when args.first.is_a?(Hash)
|
61
|
-
create_processor(self.class.description, args.first, &block)
|
62
|
-
else
|
63
|
-
create_processor(args[0], (args[1] || {}), &block)
|
64
|
-
end
|
57
|
+
options = args.extract_options!
|
58
|
+
name = args.first || self.class.description
|
59
|
+
create_processor(name, options, &block)
|
65
60
|
end
|
66
61
|
alias_method :flow, :processor
|
67
62
|
|
68
|
-
# Is the given
|
63
|
+
# Is the given +klass+ a Wukong::Processor?
|
69
64
|
#
|
70
65
|
# @param [Class] klass
|
71
66
|
# @return [true, false]
|
@@ -92,4 +87,3 @@ module Wukong
|
|
92
87
|
end
|
93
88
|
end
|
94
89
|
end
|
95
|
-
|
@@ -1,15 +1,14 @@
|
|
1
|
-
shared_examples_for 'a processor' do |options={}|
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
it{ create_processor(name).should respond_to(:setup) }
|
8
|
-
it{ create_processor(name).should respond_to(:process) }
|
9
|
-
it{ create_processor(name).should respond_to(:finalize) }
|
10
|
-
it{ create_processor(name).should respond_to(:stop) }
|
11
|
-
it{ create_processor(name).should respond_to(:notify) }
|
12
|
-
else
|
13
|
-
warn "Must supply a name for a processor you want to test"
|
1
|
+
shared_examples_for 'a processor' do |options = {}|
|
2
|
+
let(:processor_name){ options[:named] || self.class.top_level_description }
|
3
|
+
subject { create_processor(processor_name, on_error: :skip) }
|
4
|
+
|
5
|
+
it 'is registered' do
|
6
|
+
Wukong.registry.retrieve(processor_name.to_sym).should_not be_nil
|
14
7
|
end
|
8
|
+
|
9
|
+
it{ should respond_to(:setup) }
|
10
|
+
it{ should respond_to(:process) }
|
11
|
+
it{ should respond_to(:finalize) }
|
12
|
+
it{ should respond_to(:stop) }
|
13
|
+
it{ should respond_to(:notify) }
|
15
14
|
end
|
data/lib/wukong/version.rb
CHANGED
@@ -152,5 +152,18 @@ module Wukong
|
|
152
152
|
end
|
153
153
|
register
|
154
154
|
end
|
155
|
+
|
156
|
+
# Mixin processor behavior
|
157
|
+
module BufferedProcessor
|
158
|
+
def setup() ; end
|
159
|
+
def process(record) @buffer << record ; end
|
160
|
+
def stop() ; end
|
161
|
+
end
|
162
|
+
|
163
|
+
module StdoutProcessor
|
164
|
+
def setup() $stdout.sync ; end
|
165
|
+
def process(record) $stdout.puts record ; end
|
166
|
+
def stop() ; end
|
167
|
+
end
|
155
168
|
end
|
156
169
|
end
|