wukong 3.0.1 → 4.0.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.
- data/.gitignore +1 -0
- data/Gemfile +1 -1
- data/README.md +253 -45
- data/bin/wu +34 -0
- data/bin/wu-source +5 -0
- data/examples/Gemfile +0 -1
- data/examples/deploy_pack/Gemfile +0 -1
- data/examples/improver/tweet_summary.rb +73 -0
- data/examples/ruby_project/Gemfile +0 -1
- data/examples/splitter.rb +94 -0
- data/examples/twitter.rb +5 -0
- data/lib/hanuman.rb +1 -1
- data/lib/hanuman/graph.rb +39 -22
- data/lib/hanuman/stage.rb +46 -13
- data/lib/hanuman/tree.rb +67 -0
- data/lib/wukong.rb +6 -1
- data/lib/wukong/dataflow.rb +19 -48
- data/lib/wukong/driver.rb +176 -65
- data/lib/wukong/{local → driver}/event_machine_driver.rb +1 -13
- data/lib/wukong/driver/wiring.rb +68 -0
- data/lib/wukong/local.rb +6 -4
- data/lib/wukong/local/runner.rb +14 -16
- data/lib/wukong/local/stdio_driver.rb +72 -12
- data/lib/wukong/processor.rb +1 -30
- data/lib/wukong/runner.rb +2 -0
- data/lib/wukong/runner/command_runner.rb +44 -0
- data/lib/wukong/source.rb +33 -0
- data/lib/wukong/source/source_driver.rb +74 -0
- data/lib/wukong/source/source_runner.rb +38 -0
- data/lib/wukong/spec_helpers/shared_examples.rb +0 -1
- data/lib/wukong/spec_helpers/unit_tests.rb +6 -5
- data/lib/wukong/spec_helpers/unit_tests/unit_test_driver.rb +4 -14
- data/lib/wukong/spec_helpers/unit_tests/unit_test_runner.rb +7 -8
- data/lib/wukong/version.rb +1 -1
- data/lib/wukong/widget/echo.rb +55 -0
- data/lib/wukong/widget/{processors.rb → extract.rb} +0 -106
- data/lib/wukong/widget/filters.rb +15 -0
- data/lib/wukong/widget/logger.rb +56 -0
- data/lib/wukong/widget/operators.rb +82 -0
- data/lib/wukong/widget/reducers.rb +2 -0
- data/lib/wukong/widget/reducers/improver.rb +71 -0
- data/lib/wukong/widget/reducers/join_xml.rb +37 -0
- data/lib/wukong/widget/serializers.rb +21 -6
- data/lib/wukong/widgets.rb +6 -3
- data/spec/hanuman/graph_spec.rb +73 -10
- data/spec/hanuman/stage_spec.rb +15 -0
- data/spec/hanuman/tree_spec.rb +119 -0
- data/spec/spec_helper.rb +13 -1
- data/spec/support/example_test_helpers.rb +0 -1
- data/spec/support/model_test_helpers.rb +1 -1
- data/spec/support/shared_context_for_graphs.rb +57 -0
- data/spec/support/shared_examples_for_builders.rb +8 -15
- data/spec/wukong/driver_spec.rb +152 -0
- data/spec/wukong/local/runner_spec.rb +1 -12
- data/spec/wukong/local/stdio_driver_spec.rb +73 -0
- data/spec/wukong/processor_spec.rb +0 -1
- data/spec/wukong/runner_spec.rb +2 -2
- data/spec/wukong/source_spec.rb +6 -0
- data/spec/wukong/widget/extract_spec.rb +101 -0
- data/spec/wukong/widget/logger_spec.rb +23 -0
- data/spec/wukong/widget/operators_spec.rb +25 -0
- data/spec/wukong/widget/reducers/join_xml_spec.rb +25 -0
- data/spec/wukong/wu-source_spec.rb +32 -0
- data/spec/wukong/wu_spec.rb +14 -0
- data/wukong.gemspec +1 -2
- metadata +45 -28
- data/lib/wukong/local/tcp_driver.rb +0 -47
- data/spec/wu/geo/geolocated_spec.rb +0 -247
- data/spec/wukong/widget/processors_spec.rb +0 -125
@@ -9,7 +9,7 @@ shared_context 'model', :model_spec do
|
|
9
9
|
|
10
10
|
after(:each){ Gorillib::Test.nuke_constants ; Meta::Gorillib::Test.nuke_constants }
|
11
11
|
|
12
|
-
let(:mock_val){
|
12
|
+
let(:mock_val){ double('mock value') }
|
13
13
|
|
14
14
|
let(:smurf_class) do
|
15
15
|
class Gorillib::Test::Smurf
|
@@ -0,0 +1,57 @@
|
|
1
|
+
shared_context "graphs" do
|
2
|
+
|
3
|
+
let(:empty_graph) do
|
4
|
+
Hanuman::Graph.receive({})
|
5
|
+
end
|
6
|
+
|
7
|
+
let(:single_stage_graph) do
|
8
|
+
Hanuman::Graph.receive({stages: {first: Hanuman::Stage.receive(label: :first)}})
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:graph) do
|
12
|
+
Hanuman::Graph.receive(stages: {
|
13
|
+
first_a: Hanuman::Stage.receive(label: :first_a), # ancestor
|
14
|
+
first_b: Hanuman::Stage.receive(label: :first_b), # ancestor,
|
15
|
+
second: Hanuman::Stage.receive(label: :second), # ancestor, descendent
|
16
|
+
third_a: Hanuman::Stage.receive(label: :third_a), # ancestor, descendent
|
17
|
+
third_b: Hanuman::Stage.receive(label: :third_b), # descendent
|
18
|
+
fourth: Hanuman::Stage.receive(label: :fourth), # descendent
|
19
|
+
},
|
20
|
+
links: [
|
21
|
+
Hanuman::LinkFactory.connect(:simple, :first_a, :second),
|
22
|
+
Hanuman::LinkFactory.connect(:simple, :first_b, :second),
|
23
|
+
Hanuman::LinkFactory.connect(:simple, :second, :third_a),
|
24
|
+
Hanuman::LinkFactory.connect(:simple, :second, :third_b),
|
25
|
+
Hanuman::LinkFactory.connect(:simple, :third_a, :fourth),
|
26
|
+
]
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:empty_tree) do
|
31
|
+
Hanuman::Tree.receive({})
|
32
|
+
end
|
33
|
+
|
34
|
+
let(:single_stage_tree) do
|
35
|
+
Hanuman::Tree.receive({stages: { first: Hanuman::Stage.receive(label: :first)}})
|
36
|
+
end
|
37
|
+
|
38
|
+
let(:tree) do
|
39
|
+
Hanuman::Tree.receive(stages: {
|
40
|
+
# important to mix up the order here so we
|
41
|
+
# can ensure that tree-sorting is working.
|
42
|
+
first: Hanuman::Stage.receive(label: :first), # ancestor,
|
43
|
+
third_b: Hanuman::Stage.receive(label: :third_b), # descendent
|
44
|
+
second: Hanuman::Stage.receive(label: :second), # ancestor, descendent
|
45
|
+
fourth: Hanuman::Stage.receive(label: :fourth), # descendent
|
46
|
+
third_a: Hanuman::Stage.receive(label: :third_a), # ancestor, descendent
|
47
|
+
},
|
48
|
+
links: [
|
49
|
+
Hanuman::LinkFactory.connect(:simple, :first, :second),
|
50
|
+
Hanuman::LinkFactory.connect(:simple, :second, :third_a),
|
51
|
+
Hanuman::LinkFactory.connect(:simple, :second, :third_b),
|
52
|
+
Hanuman::LinkFactory.connect(:simple, :third_a, :fourth),
|
53
|
+
]
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -1,5 +1,12 @@
|
|
1
1
|
shared_examples_for 'a Stage::Builder' do
|
2
|
-
before(:each)
|
2
|
+
before(:each) do
|
3
|
+
@registry = Hanuman::GlobalRegistry.show
|
4
|
+
Hanuman::GlobalRegistry.clear!
|
5
|
+
end
|
6
|
+
|
7
|
+
after(:each) do
|
8
|
+
Hanuman::GlobalRegistry.merge! @registry
|
9
|
+
end
|
3
10
|
|
4
11
|
context '.receive' do
|
5
12
|
it 'extra arguments are stored in the :args attribute' do
|
@@ -84,18 +91,4 @@ shared_examples_for 'a Stage::Builder' do
|
|
84
91
|
subject.serialize.should_not include(:args, :for_class)
|
85
92
|
end
|
86
93
|
end
|
87
|
-
|
88
|
-
context '#into' do
|
89
|
-
subject { described_class.receive(label: :pyro) }
|
90
|
-
let(:other_stage){ described_class.receive(label: :iceman) }
|
91
|
-
|
92
|
-
it 'returns the linked into stage for chaining' do
|
93
|
-
subject.into(other_stage).should be(other_stage)
|
94
|
-
end
|
95
|
-
|
96
|
-
it 'links two stages together with a simple link' do
|
97
|
-
subject.into(other_stage)
|
98
|
-
subject.links.should be_any{ |link| link.from == :pyro and link.into == :iceman }
|
99
|
-
end
|
100
|
-
end
|
101
94
|
end
|
data/spec/wukong/driver_spec.rb
CHANGED
@@ -1,2 +1,154 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
describe Wukong::DriverMethods do
|
4
|
+
|
5
|
+
describe "#construct_dataflow" do
|
6
|
+
|
7
|
+
context "given a label registered to a processor" do
|
8
|
+
let(:driver) { Wukong::SpecHelpers::UnitTestDriver.new(:regexp, match: /hi/) }
|
9
|
+
|
10
|
+
context "constructs an anonymous dataflow" do
|
11
|
+
let(:dataflow) { driver.dataflow }
|
12
|
+
subject { dataflow }
|
13
|
+
|
14
|
+
it { should_not be_nil }
|
15
|
+
its(:label) { should be_nil }
|
16
|
+
its(:links) { should be_empty }
|
17
|
+
|
18
|
+
context "with a single stage" do
|
19
|
+
let(:stage) { dataflow.stages.values.first }
|
20
|
+
subject { stage }
|
21
|
+
|
22
|
+
it { should_not be_nil }
|
23
|
+
its(:label) { should == :regexp }
|
24
|
+
its(:match) { should == /hi/ }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "given a serialization argument" do
|
30
|
+
context "that does not match a registered serializer" do
|
31
|
+
it "raises an error" do
|
32
|
+
expect { Wukong::SpecHelpers::UnitTestDriver.new(:regexp, match: /hi/, to: 'fake') }.to raise_error(Wukong::Error)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
context "that matches a registered serializer" do
|
36
|
+
let(:driver) { Wukong::SpecHelpers::UnitTestDriver.new(:regexp, match: /hi/, to: 'json') }
|
37
|
+
let(:dataflow) { driver.dataflow }
|
38
|
+
it "appends the serializer to each of the leaves" do
|
39
|
+
dataflow.leaves.size.should == 1
|
40
|
+
dataflow.leaves.map(&:label).should_not include(:regexp)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "given a deserialization argument" do
|
46
|
+
let(:driver) { Wukong::SpecHelpers::UnitTestDriver.new(:regexp, match: /hi/, from: 'json') }
|
47
|
+
let(:dataflow) { driver.dataflow }
|
48
|
+
|
49
|
+
context "adds a deserializer" do
|
50
|
+
subject { dataflow.stages[:from_json] }
|
51
|
+
it { should_not be_nil }
|
52
|
+
it "which is the root of the dataflow" do
|
53
|
+
dataflow.root.should == subject
|
54
|
+
end
|
55
|
+
it "which is linked to the original root" do
|
56
|
+
dataflow.ancestors(dataflow.stages[:regexp]).should include(subject)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "given a recordization argument" do
|
62
|
+
let(:driver) { Wukong::SpecHelpers::UnitTestDriver.new(:regexp, match: /hi/, as: Time) }
|
63
|
+
let(:dataflow) { driver.dataflow }
|
64
|
+
|
65
|
+
context "adds a recordizer" do
|
66
|
+
subject { dataflow.stages[:recordize] }
|
67
|
+
it { should_not be_nil }
|
68
|
+
it "which is the root of the dataflow" do
|
69
|
+
dataflow.root.should == subject
|
70
|
+
end
|
71
|
+
it "which is linked to the original root" do
|
72
|
+
dataflow.ancestors(dataflow.stages[:regexp]).should include(subject)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "given deserialization and recordization arguments" do
|
78
|
+
let(:driver) { Wukong::SpecHelpers::UnitTestDriver.new(:regexp, match: /hi/, from: 'json', as: Time) }
|
79
|
+
let(:dataflow) { driver.dataflow }
|
80
|
+
let(:recordizer) { dataflow.stages[:recordize] }
|
81
|
+
let(:deserializer) { dataflow.stages[:from_json] }
|
82
|
+
|
83
|
+
context "adds a deserializer" do
|
84
|
+
subject { deserializer }
|
85
|
+
it { should_not be_nil }
|
86
|
+
it "which is the root of the dataflow" do
|
87
|
+
dataflow.root.should == subject
|
88
|
+
end
|
89
|
+
it "which is linked to the recordizer" do
|
90
|
+
dataflow.ancestors(dataflow.stages[:recordizer]).should include(subject)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "adds a recordizer" do
|
95
|
+
subject { recordizer }
|
96
|
+
it { should_not be_nil }
|
97
|
+
it "which is linked to the original root" do
|
98
|
+
dataflow.ancestors(dataflow.stages[:regexp]).should include(subject)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "#setup_dataflow" do
|
106
|
+
let(:driver) { Wukong::SpecHelpers::UnitTestDriver.new(:regexp, match: /hi/) }
|
107
|
+
|
108
|
+
it "calls the driver's #setup method" do
|
109
|
+
driver.should_receive(:setup)
|
110
|
+
driver.setup_dataflow
|
111
|
+
end
|
112
|
+
|
113
|
+
it "calls setup on each stage of the dataflow" do
|
114
|
+
driver.dataflow.each_stage do |stage|
|
115
|
+
stage.should_receive(:setup)
|
116
|
+
end
|
117
|
+
driver.setup_dataflow
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "#finalize_dataflow" do
|
122
|
+
let(:driver) { Wukong::SpecHelpers::UnitTestDriver.new(:regexp, match: /hi/) }
|
123
|
+
it "calls the driver's #finalize method" do
|
124
|
+
driver.should_receive(:finalize)
|
125
|
+
driver.finalize_dataflow
|
126
|
+
end
|
127
|
+
|
128
|
+
it "calls finalize on each stage of the dataflow" do
|
129
|
+
driver.dataflow.each_stage do |stage|
|
130
|
+
stage.should_receive(:finalize)
|
131
|
+
end
|
132
|
+
driver.finalize_dataflow
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "#finalize_and_stop_dataflow" do
|
137
|
+
let(:driver) { Wukong::SpecHelpers::UnitTestDriver.new(:regexp, match: /hi/) }
|
138
|
+
it "calls the driver's #finalize and #stop methods" do
|
139
|
+
driver.should_receive(:finalize)
|
140
|
+
driver.should_receive(:stop)
|
141
|
+
driver.finalize_and_stop_dataflow
|
142
|
+
end
|
143
|
+
|
144
|
+
it "calls finalize and stop on each stage of the dataflow" do
|
145
|
+
driver.dataflow.each_stage do |stage|
|
146
|
+
stage.should_receive(:finalize)
|
147
|
+
stage.should_receive(:stop)
|
148
|
+
end
|
149
|
+
driver.finalize_and_stop_dataflow
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Wukong::Local::LocalRunner do
|
4
|
-
before { EM.stub
|
4
|
+
before { EM.stub(:run) }
|
5
5
|
|
6
6
|
describe "choosing a processor name" do
|
7
7
|
|
@@ -25,16 +25,5 @@ describe Wukong::Local::LocalRunner do
|
|
25
25
|
local_runner(examples_dir('string_reverser.rb')).processor.should == 'string_reverser'
|
26
26
|
end
|
27
27
|
end
|
28
|
-
|
29
|
-
describe "uses a" do
|
30
|
-
it "StdioDriver by default" do
|
31
|
-
local_runner('identity').driver.should == Wukong::Local::StdioDriver
|
32
|
-
end
|
33
|
-
|
34
|
-
it "TCPDriver when given a --port argument" do
|
35
|
-
local_runner('identity','--tcp_port=6000').driver.should == Wukong::Local::TCPDriver
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
28
|
|
40
29
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Wukong::Local::StdioDriver do
|
4
|
+
before { EM.stub(:stop) }
|
5
|
+
|
6
|
+
let(:driver) { Wukong::Local::StdioDriver.new(:bogus_event_machine_inserted_arg, :identity, {}) }
|
7
|
+
|
8
|
+
it "attaches itself as an EventMachine handler for $stdin, providing the given label and settings" do
|
9
|
+
EM.should_receive(:attach).with($stdin, Wukong::Local::StdioDriver, :foo, {})
|
10
|
+
Wukong::Local::StdioDriver.start(:foo, {})
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "setting up a dataflow" do
|
14
|
+
context "#post_init hook from EventMachine" do
|
15
|
+
after { driver.post_init }
|
16
|
+
|
17
|
+
it "sets signal traps for INT and TERM" do
|
18
|
+
Signal.should_receive(:trap).with('INT').at_least(:once)
|
19
|
+
Signal.should_receive(:trap).with('TERM').at_least(:once)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "calls the #setup_dataflow method" do
|
23
|
+
driver.should_receive(:setup_dataflow).at_least(:once)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "syncs $stdout" do
|
27
|
+
$stdout.should_receive(:sync).at_least(:once)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "driving a dataflow" do
|
34
|
+
context "#receive_line hook from EventMachine" do
|
35
|
+
let(:line) { "hello" }
|
36
|
+
|
37
|
+
after { driver.receive_line(line) }
|
38
|
+
it "passes the line to the #send_through_dataflow method" do
|
39
|
+
driver.should_receive(:send_through_dataflow).with(line)
|
40
|
+
end
|
41
|
+
|
42
|
+
context "upon an error within the dataflow" do
|
43
|
+
let(:message) { "whoops" }
|
44
|
+
|
45
|
+
before do
|
46
|
+
driver.should_receive(:send_through_dataflow).with(line) do
|
47
|
+
raise Wukong::Error.new(message)
|
48
|
+
end
|
49
|
+
driver.log.stub(:error)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "logs an error message" do
|
53
|
+
driver.log.should_receive(:error).with(kind_of(String))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "shutting down a dataflow" do
|
60
|
+
context "#unbind hook from EventMachine" do
|
61
|
+
after { driver.unbind }
|
62
|
+
|
63
|
+
it "calls the #finalize_and_stop_dataflow method" do
|
64
|
+
driver.should_receive(:finalize_and_stop_dataflow)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "stops the EventMachine reactor" do
|
68
|
+
EM.should_receive(:stop)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/spec/wukong/runner_spec.rb
CHANGED
@@ -90,7 +90,7 @@ describe Wukong::Runner do
|
|
90
90
|
let(:deploy_pack_dir) { examples_dir('ruby_project') }
|
91
91
|
before do
|
92
92
|
FileUtils.cd(dir)
|
93
|
-
ENV.stub
|
93
|
+
ENV.stub(:[]).with("BUNDLE_GEMFILE").and_return(File.join(deploy_pack_dir, 'Gemfile'))
|
94
94
|
end
|
95
95
|
subject { generic_runner }
|
96
96
|
its(:deploy_pack_dir) { should == '/' }
|
@@ -103,7 +103,7 @@ describe Wukong::Runner do
|
|
103
103
|
let(:deploy_pack_dir) { examples_dir('deploy_pack') }
|
104
104
|
before do
|
105
105
|
FileUtils.cd(dir)
|
106
|
-
ENV.stub
|
106
|
+
ENV.stub(:[]).with("BUNDLE_GEMFILE").and_return(File.join(deploy_pack_dir, 'Gemfile'))
|
107
107
|
end
|
108
108
|
subject { generic_runner }
|
109
109
|
its(:deploy_pack_dir) { should == deploy_pack_dir.to_s }
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :extract do
|
4
|
+
|
5
|
+
let(:hsh) { { "hi" => "there", "top" => { "lower" => { "lowest" => "value" } } } }
|
6
|
+
let(:ary) { ['1', 2, 'three'] }
|
7
|
+
|
8
|
+
subject { processor(:extract) }
|
9
|
+
|
10
|
+
it_behaves_like 'a processor', :named => :extract
|
11
|
+
|
12
|
+
context "on a string" do
|
13
|
+
it "emits the string with no arguments" do
|
14
|
+
processor(:extract).given('hi there', 'buddy').should emit('hi there', 'buddy')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
context "on a Fixnum" do
|
18
|
+
it "emits the number with no arguments" do
|
19
|
+
processor(:extract).given(3, 3.0).should emit(3, 3.0)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
context "on a Hash" do
|
23
|
+
it "emits the hash with no arguments" do
|
24
|
+
processor(:extract).given(hsh).should emit(hsh)
|
25
|
+
end
|
26
|
+
it "can extract a key" do
|
27
|
+
processor(:extract, part: 'hi').given(hsh).should emit('there')
|
28
|
+
end
|
29
|
+
it "emits nil when the value of the key is nil" do
|
30
|
+
processor(:extract, part: 'bye').given(hsh).should emit(nil)
|
31
|
+
end
|
32
|
+
it "can extract a nested key" do
|
33
|
+
processor(:extract, part: 'top.lower.lowest').given(hsh).should emit('value')
|
34
|
+
end
|
35
|
+
it "emits nil when the value of this nested key is nil" do
|
36
|
+
processor(:extract, part: 'foo.bar.baz').given(hsh).should emit(nil)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
context "on an Array" do
|
40
|
+
it "emits the array with no arguments" do
|
41
|
+
processor(:extract).given(ary).should emit(ary)
|
42
|
+
end
|
43
|
+
it "can extract the nth value with an integer argument" do
|
44
|
+
processor(:extract, part: 2).given(ary).should emit(2)
|
45
|
+
end
|
46
|
+
it "can extract the nth value with a string argument" do
|
47
|
+
processor(:extract, part: '2').given(ary).should emit(2)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
context "on JSON" do
|
51
|
+
let(:garbage) { '{"239823:' }
|
52
|
+
it "emits the JSON with no arguments" do
|
53
|
+
processor(:extract).given_json(hsh).should emit_json(hsh)
|
54
|
+
end
|
55
|
+
it "will skip badly formed records" do
|
56
|
+
processor(:extract).given(garbage).should emit(garbage)
|
57
|
+
end
|
58
|
+
it "can extract a key" do
|
59
|
+
processor(:extract, part: 'hi').given_json(hsh).should emit('there')
|
60
|
+
end
|
61
|
+
it "can extract a nested key" do
|
62
|
+
processor(:extract, part: 'top.lower.lowest').given_json(hsh).should emit('value')
|
63
|
+
end
|
64
|
+
it "emits nil when the record is missing the key" do
|
65
|
+
processor(:extract, part: 'foo.bar.baz').given_json(hsh).should emit(nil)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
context "on delimited data" do
|
69
|
+
it "emits the row with no arguments" do
|
70
|
+
processor(:extract).given_delimited('|', ary).should emit(ary.map(&:to_s).join('|'))
|
71
|
+
end
|
72
|
+
it "can extract the nth value with an integer argument" do
|
73
|
+
processor(:extract, part: 2, separator: '|').given_delimited('|', ary).should emit('2')
|
74
|
+
end
|
75
|
+
it "can extract nth value with a string argument" do
|
76
|
+
processor(:extract, part: '2', separator: '|').given_delimited('|', ary).should emit('2')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
context "on TSV" do
|
80
|
+
it "emits the TSV with no arguments" do
|
81
|
+
processor(:extract).given_tsv(ary).should emit(ary.map(&:to_s).join("\t"))
|
82
|
+
end
|
83
|
+
it "can extract the nth value with an integer argument" do
|
84
|
+
processor(:extract, part: 2).given_tsv(ary).should emit('2')
|
85
|
+
end
|
86
|
+
it "can extract the nth value with a string argument" do
|
87
|
+
processor(:extract, part: '2').given_tsv(ary).should emit('2')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
context "on CSV" do
|
91
|
+
it "emits the CSV with no arguments" do
|
92
|
+
processor(:extract).given_csv(ary).should emit(ary.map(&:to_s).join(","))
|
93
|
+
end
|
94
|
+
it "can extract the nth value with an integer argument" do
|
95
|
+
processor(:extract, part: 2, separator: ',').given_csv(ary).should emit('2')
|
96
|
+
end
|
97
|
+
it "can extract the nth value with a string argument" do
|
98
|
+
processor(:extract, part: '2', separator: ',').given_csv(ary).should emit('2')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|