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.
Files changed (146) hide show
  1. data/Gemfile +13 -0
  2. data/README.md +182 -6
  3. data/bin/wu-local +13 -5
  4. data/bin/wu-server +1 -1
  5. data/examples/Gemfile +2 -1
  6. data/examples/basic/string_reverser.rb +23 -0
  7. data/examples/{tiny_count.rb → basic/tiny_count.rb} +0 -0
  8. data/examples/{word_count → basic/word_count}/accumulator.rb +0 -0
  9. data/examples/{word_count → basic/word_count}/tokenizer.rb +0 -0
  10. data/examples/{word_count → basic/word_count}/word_count.rb +0 -0
  11. data/examples/deploy_pack/Gemfile +7 -0
  12. data/examples/deploy_pack/README.md +6 -0
  13. data/examples/{text/latinize_text.rb → deploy_pack/a/b/c/.gitkeep} +0 -0
  14. data/examples/deploy_pack/app/processors/string_reverser.rb +5 -0
  15. data/examples/deploy_pack/config/environment.rb +1 -0
  16. data/examples/{dataflow → dsl/dataflow}/fibonacci_series.rb +0 -0
  17. data/examples/dsl/dataflow/scraper_macro_flow.rb +28 -0
  18. data/examples/{dataflow → dsl/dataflow}/simple.rb +0 -0
  19. data/examples/{dataflow → dsl/dataflow}/telegram.rb +0 -0
  20. data/examples/{workflow → dsl/workflow}/cherry_pie.dot +0 -0
  21. data/examples/{workflow → dsl/workflow}/cherry_pie.md +0 -0
  22. data/examples/{workflow → dsl/workflow}/cherry_pie.png +0 -0
  23. data/examples/{workflow → dsl/workflow}/cherry_pie.rb +0 -0
  24. data/examples/empty/.gitkeep +0 -0
  25. data/examples/graph/implied_geolocation/README.md +63 -0
  26. data/examples/graph/{minimum_spanning_tree.rb → minimum_spanning_tree/airfares_graphviz.rb} +0 -0
  27. data/examples/munging/airline_flights/indexable.rb +75 -0
  28. data/examples/munging/airline_flights/indexable_spec.rb +90 -0
  29. data/examples/munging/geo/geonames_models.rb +29 -0
  30. data/examples/munging/wikipedia/dbpedia/dbpedia_common.rb +1 -0
  31. data/examples/munging/wikipedia/dbpedia/extract_links-cruft.rb +66 -0
  32. data/examples/munging/wikipedia/dbpedia/extract_links.rb +213 -146
  33. data/examples/rake_helper.rb +12 -0
  34. data/examples/ruby_project/Gemfile +7 -0
  35. data/examples/ruby_project/README.md +6 -0
  36. data/examples/ruby_project/a/b/c/.gitkeep +0 -0
  37. data/examples/serverlogs/geo_ip_mapping/munge_geolite.rb +82 -0
  38. data/examples/serverlogs/models/logline.rb +102 -0
  39. data/examples/{dataflow/parse_apache_logs.rb → serverlogs/parser/apache_parser_widget.rb} +0 -0
  40. data/examples/serverlogs/visit_paths/common.rb +4 -0
  41. data/examples/serverlogs/visit_paths/page_counts.pig +48 -0
  42. data/examples/serverlogs/visit_paths/serverlogs-01-parse-script.rb +11 -0
  43. data/examples/serverlogs/visit_paths/serverlogs-02-histograms-full.rb +31 -0
  44. data/examples/serverlogs/visit_paths/serverlogs-02-histograms-mapper.rb +12 -0
  45. data/examples/serverlogs/visit_paths/serverlogs-03-breadcrumbs-full.rb +67 -0
  46. data/examples/serverlogs/visit_paths/serverlogs-04-page_page_edges-full.rb +38 -0
  47. data/examples/text/{pig_latin.rb → pig_latin/pig_latinizer.rb} +0 -0
  48. data/examples/{dataflow/pig_latinizer.rb → text/pig_latin/pig_latinizer_widget.rb} +0 -0
  49. data/lib/hanuman/graph.rb +6 -1
  50. data/lib/wu/geo.rb +4 -0
  51. data/lib/wu/geo/geo_grids.numbers +0 -0
  52. data/lib/wu/geo/geolocated.rb +331 -0
  53. data/lib/wu/geo/quadtile.rb +69 -0
  54. data/{examples → lib/wu}/graph/union_find.rb +0 -0
  55. data/lib/wu/model/reconcilable.rb +63 -0
  56. data/{examples/munging/wikipedia/utils/munging_utils.rb → lib/wu/munging.rb} +7 -4
  57. data/lib/wu/social/models/twitter.rb +31 -0
  58. data/{examples/models/wikipedia.rb → lib/wu/wikipedia/models.rb} +0 -0
  59. data/lib/wukong.rb +9 -4
  60. data/lib/wukong/boot.rb +10 -1
  61. data/lib/wukong/driver.rb +65 -71
  62. data/lib/wukong/logger.rb +93 -0
  63. data/lib/wukong/processor.rb +38 -29
  64. data/lib/wukong/runner.rb +144 -0
  65. data/lib/wukong/server.rb +119 -0
  66. data/lib/wukong/spec_helpers.rb +1 -0
  67. data/lib/wukong/spec_helpers/integration_driver.rb +22 -9
  68. data/lib/wukong/spec_helpers/integration_driver_matchers.rb +26 -4
  69. data/lib/wukong/spec_helpers/processor_helpers.rb +4 -10
  70. data/lib/wukong/spec_helpers/shared_examples.rb +12 -13
  71. data/lib/wukong/version.rb +1 -1
  72. data/lib/wukong/widget/processors.rb +13 -0
  73. data/lib/wukong/widget/serializers.rb +55 -65
  74. data/lib/wukong/widgets.rb +0 -2
  75. data/spec/hanuman/graph_spec.rb +14 -0
  76. data/spec/spec_helper.rb +4 -30
  77. data/spec/support/{wukong_test_helpers.rb → example_test_helpers.rb} +29 -2
  78. data/spec/support/integration_helper.rb +38 -0
  79. data/spec/support/model_test_helpers.rb +115 -0
  80. data/spec/wu/geo/geolocated_spec.rb +247 -0
  81. data/spec/wu/model/reconcilable_spec.rb +152 -0
  82. data/spec/wukong/widget/processors_spec.rb +0 -1
  83. data/spec/wukong/widget/serializers_spec.rb +88 -62
  84. data/spec/wukong/wu_local_spec.rb +125 -0
  85. data/wukong.gemspec +3 -16
  86. metadata +72 -266
  87. data/examples/dataflow/apache_log_line.rb +0 -100
  88. data/examples/jabberwocky.txt +0 -36
  89. data/examples/munging/Gemfile +0 -8
  90. data/examples/munging/airline_flights/airline.rb +0 -57
  91. data/examples/munging/airline_flights/airport.rb +0 -211
  92. data/examples/munging/airline_flights/flight.rb +0 -156
  93. data/examples/munging/airline_flights/models.rb +0 -4
  94. data/examples/munging/airline_flights/parse.rb +0 -26
  95. data/examples/munging/airline_flights/route.rb +0 -35
  96. data/examples/munging/airline_flights/timezone_fixup.rb +0 -62
  97. data/examples/munging/airports/40_wbans.txt +0 -40
  98. data/examples/munging/airports/filter_weather_reports.rb +0 -37
  99. data/examples/munging/airports/join.pig +0 -31
  100. data/examples/munging/airports/to_tsv.rb +0 -33
  101. data/examples/munging/airports/usa_wbans.pig +0 -19
  102. data/examples/munging/airports/usa_wbans.txt +0 -2157
  103. data/examples/munging/airports/wbans.pig +0 -19
  104. data/examples/munging/airports/wbans.txt +0 -2310
  105. data/examples/munging/rake_helper.rb +0 -62
  106. data/examples/munging/weather/.gitignore +0 -1
  107. data/examples/munging/weather/Gemfile +0 -4
  108. data/examples/munging/weather/Rakefile +0 -28
  109. data/examples/munging/weather/extract_ish.rb +0 -13
  110. data/examples/munging/weather/models/weather.rb +0 -119
  111. data/examples/munging/weather/utils/noaa_downloader.rb +0 -46
  112. data/examples/munging/wikipedia/README.md +0 -34
  113. data/examples/munging/wikipedia/Rakefile +0 -193
  114. data/examples/munging/wikipedia/n1_subuniverse/n1_nodes.pig +0 -18
  115. data/examples/munging/wikipedia/page_metadata/extract_page_metadata.rb +0 -21
  116. data/examples/munging/wikipedia/page_metadata/extract_page_metadata.rb.old +0 -27
  117. data/examples/munging/wikipedia/pagelinks/augment_pagelinks.pig +0 -29
  118. data/examples/munging/wikipedia/pagelinks/extract_pagelinks.rb +0 -14
  119. data/examples/munging/wikipedia/pagelinks/extract_pagelinks.rb.old +0 -25
  120. data/examples/munging/wikipedia/pagelinks/undirect_pagelinks.pig +0 -29
  121. data/examples/munging/wikipedia/pageviews/augment_pageviews.pig +0 -32
  122. data/examples/munging/wikipedia/pageviews/extract_pageviews.rb +0 -85
  123. data/examples/munging/wikipedia/pig_style_guide.md +0 -25
  124. data/examples/munging/wikipedia/redirects/redirects_page_metadata.pig +0 -19
  125. data/examples/munging/wikipedia/subuniverse/sub_articles.pig +0 -23
  126. data/examples/munging/wikipedia/subuniverse/sub_page_metadata.pig +0 -24
  127. data/examples/munging/wikipedia/subuniverse/sub_pagelinks_from.pig +0 -22
  128. data/examples/munging/wikipedia/subuniverse/sub_pagelinks_into.pig +0 -22
  129. data/examples/munging/wikipedia/subuniverse/sub_pagelinks_within.pig +0 -26
  130. data/examples/munging/wikipedia/subuniverse/sub_pageviews.pig +0 -29
  131. data/examples/munging/wikipedia/subuniverse/sub_undirected_pagelinks_within.pig +0 -24
  132. data/examples/munging/wikipedia/utils/get_namespaces.rb +0 -86
  133. data/examples/munging/wikipedia/utils/namespaces.json +0 -1
  134. data/examples/string_reverser.rb +0 -26
  135. data/examples/twitter/locations.rb +0 -29
  136. data/examples/twitter/models.rb +0 -24
  137. data/examples/twitter/pt1-fiddle.pig +0 -8
  138. data/examples/twitter/pt2-simple_parse.pig +0 -31
  139. data/examples/twitter/pt2-simple_parse.rb +0 -18
  140. data/examples/twitter/pt3-join_on_zips.pig +0 -39
  141. data/examples/twitter/pt4-strong_links.rb +0 -20
  142. data/examples/twitter/pt5-lnglat_and_strong_links.pig +0 -16
  143. data/examples/twitter/states.tsv +0 -50
  144. data/examples/workflow/package_gem.rb +0 -55
  145. data/lib/wukong/widget/sink.rb +0 -16
  146. 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
@@ -79,3 +79,4 @@ module Wukong
79
79
 
80
80
  Processor.class_eval { include SpecHelpers::ProcessorSpecMethods }
81
81
  end
82
+
@@ -87,15 +87,17 @@ module Wukong
87
87
  # @return [true, false]
88
88
  def run!
89
89
  return false if ran?
90
- Open3.popen3(env, cmd) do |i, o, e, wait_thr|
91
- self.pid = wait_thr.pid
92
-
93
- @inputs.each { |input| i.puts(input) }
94
- i.close
95
-
96
- self.stdout = o.read
97
- self.stderr = e.read
98
- self.exit_code = wait_thr.value.to_i
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 #{formatted_command}\n\nand expected #{output_description}\n\n#{formatted_output}\n\nto #{match_type}\n\n #{failed_expectation}"
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 #{formatted_command}\n\nexpecting #{expected_exit_code_description} Got #{driver.exit_code} instead."
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 #{formatted_command}\n\nNOT expecting #{expected_exit_code_description}."
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
- case
58
- when args.empty?
59
- create_processor(self.class.description, {}, &block)
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 `klass` a Wukong::Processor?
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
- name = options[:named]
3
- if name
4
- it "is registered with the name '#{name}'" do
5
- Wukong.registry.retrieve(name.to_sym).should_not be_nil
6
- end
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
@@ -1,4 +1,4 @@
1
1
  module Wukong
2
2
  # The current version of Wukong.
3
- VERSION = '3.0.0.pre2'
3
+ VERSION = '3.0.0.pre3'
4
4
  end
@@ -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