wukong 3.0.0.pre3 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/README.md +689 -50
- data/bin/wu-local +1 -74
- data/diagrams/wu_local.dot +39 -0
- data/diagrams/wu_local.dot.png +0 -0
- data/examples/loadable.rb +2 -0
- data/examples/string_reverser.rb +7 -0
- data/lib/hanuman/stage.rb +2 -2
- data/lib/wukong.rb +21 -10
- data/lib/wukong/dataflow.rb +2 -5
- data/lib/wukong/doc_helpers.rb +14 -0
- data/lib/wukong/doc_helpers/dataflow_handler.rb +29 -0
- data/lib/wukong/doc_helpers/field_handler.rb +91 -0
- data/lib/wukong/doc_helpers/processor_handler.rb +29 -0
- data/lib/wukong/driver.rb +11 -1
- data/lib/wukong/local.rb +40 -0
- data/lib/wukong/local/event_machine_driver.rb +27 -0
- data/lib/wukong/local/runner.rb +98 -0
- data/lib/wukong/local/stdio_driver.rb +44 -0
- data/lib/wukong/local/tcp_driver.rb +47 -0
- data/lib/wukong/logger.rb +16 -7
- data/lib/wukong/plugin.rb +48 -0
- data/lib/wukong/processor.rb +57 -15
- data/lib/wukong/rake_helper.rb +6 -0
- data/lib/wukong/runner.rb +151 -128
- data/lib/wukong/runner/boot_sequence.rb +123 -0
- data/lib/wukong/runner/code_loader.rb +52 -0
- data/lib/wukong/runner/deploy_pack_loader.rb +75 -0
- data/lib/wukong/runner/help_message.rb +42 -0
- data/lib/wukong/spec_helpers.rb +4 -12
- data/lib/wukong/spec_helpers/integration_tests.rb +150 -0
- data/lib/wukong/spec_helpers/{integration_driver_matchers.rb → integration_tests/integration_test_matchers.rb} +28 -62
- data/lib/wukong/spec_helpers/integration_tests/integration_test_runner.rb +97 -0
- data/lib/wukong/spec_helpers/shared_examples.rb +19 -10
- data/lib/wukong/spec_helpers/unit_tests.rb +134 -0
- data/lib/wukong/spec_helpers/{processor_methods.rb → unit_tests/unit_test_driver.rb} +42 -8
- data/lib/wukong/spec_helpers/{spec_driver_matchers.rb → unit_tests/unit_test_matchers.rb} +6 -32
- data/lib/wukong/spec_helpers/unit_tests/unit_test_runner.rb +54 -0
- data/lib/wukong/version.rb +1 -1
- data/lib/wukong/widget/filters.rb +134 -8
- data/lib/wukong/widget/processors.rb +64 -5
- data/lib/wukong/widget/reducers/bin.rb +68 -18
- data/lib/wukong/widget/reducers/count.rb +12 -0
- data/lib/wukong/widget/reducers/group.rb +48 -5
- data/lib/wukong/widget/reducers/group_concat.rb +30 -2
- data/lib/wukong/widget/reducers/moments.rb +4 -4
- data/lib/wukong/widget/reducers/sort.rb +53 -3
- data/lib/wukong/widget/serializers.rb +37 -12
- data/lib/wukong/widget/utils.rb +1 -1
- data/spec/spec_helper.rb +20 -2
- data/spec/wukong/driver_spec.rb +2 -0
- data/spec/wukong/local/runner_spec.rb +40 -0
- data/spec/wukong/local_spec.rb +6 -0
- data/spec/wukong/logger_spec.rb +49 -0
- data/spec/wukong/processor_spec.rb +22 -0
- data/spec/wukong/runner_spec.rb +128 -8
- data/spec/wukong/widget/filters_spec.rb +28 -10
- data/spec/wukong/widget/processors_spec.rb +5 -5
- data/spec/wukong/widget/reducers/bin_spec.rb +14 -14
- data/spec/wukong/widget/reducers/count_spec.rb +1 -1
- data/spec/wukong/widget/reducers/group_spec.rb +7 -6
- data/spec/wukong/widget/reducers/moments_spec.rb +2 -2
- data/spec/wukong/widget/reducers/sort_spec.rb +1 -1
- data/spec/wukong/widget/serializers_spec.rb +84 -88
- data/spec/wukong/wu-local_spec.rb +109 -0
- metadata +43 -20
- data/bin/wu-server +0 -70
- data/lib/wukong/boot.rb +0 -96
- data/lib/wukong/configuration.rb +0 -8
- data/lib/wukong/emitter.rb +0 -22
- data/lib/wukong/server.rb +0 -119
- data/lib/wukong/spec_helpers/integration_driver.rb +0 -157
- data/lib/wukong/spec_helpers/processor_helpers.rb +0 -89
- data/lib/wukong/spec_helpers/spec_driver.rb +0 -28
- data/spec/wukong/local_runner_spec.rb +0 -31
- data/spec/wukong/wu_local_spec.rb +0 -125
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Wukong::Processor do
|
4
|
+
|
5
|
+
subject { Wukong::Processor.new }
|
6
|
+
|
7
|
+
describe "has an interface" do
|
8
|
+
it{ should respond_to(:setup) }
|
9
|
+
it{ should respond_to(:process) }
|
10
|
+
it{ should respond_to(:finalize) }
|
11
|
+
it{ should respond_to(:stop) }
|
12
|
+
it{ should respond_to(:notify) }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "default process method" do
|
16
|
+
it "yields the original input record by default on process" do
|
17
|
+
expect { |b| subject.process(1, &b) }.to yield_with_args(1)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
data/spec/wukong/runner_spec.rb
CHANGED
@@ -1,12 +1,132 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
# require 'wukong'
|
3
2
|
|
4
|
-
|
3
|
+
describe Wukong::Runner do
|
5
4
|
|
6
|
-
|
7
|
-
# it 'is shorter than a tweet' do
|
8
|
-
# example_script_contents('tiny_count.rb').length.should < 140
|
9
|
-
# end
|
5
|
+
context "can boot itself by" do
|
10
6
|
|
11
|
-
|
12
|
-
|
7
|
+
describe "loading" do
|
8
|
+
it "files passed on the command-line" do
|
9
|
+
loadable = examples_dir('loadable.rb')
|
10
|
+
generic_runner(loadable) do
|
11
|
+
should_receive(:load_ruby_file).with(loadable)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
it "files when its in a deploy pack" do
|
15
|
+
generic_runner { should_receive(:load_deploy_pack) }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "configuring" do
|
20
|
+
subject do
|
21
|
+
Class.new(Wukong::Runner).tap do |configured|
|
22
|
+
configured.class_eval do
|
23
|
+
usage 'a nice usage message'
|
24
|
+
description 'a lovely description'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "a usage message" do
|
30
|
+
runner(subject, 'wu-generic').settings.usage.should =~ /^usage: .* a nice usage message$/
|
31
|
+
end
|
32
|
+
|
33
|
+
it "a description" do
|
34
|
+
runner(subject, 'wu-generic').settings.description.should == 'a lovely description'
|
35
|
+
end
|
36
|
+
|
37
|
+
it "plugins" do
|
38
|
+
generic_runner { Wukong.should_receive(:configure_plugins) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "resolving" do
|
43
|
+
it "doesn't move on if resolve causes an error" do
|
44
|
+
generic_runner do
|
45
|
+
settings.should_receive(:resolve!).and_raise(RuntimeError)
|
46
|
+
should_not_receive(:setup)
|
47
|
+
should_not_receive(:validate)
|
48
|
+
should_not_receive(:run)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "setting up" do
|
54
|
+
it "asks plugins to boot" do
|
55
|
+
generic_runner do
|
56
|
+
Wukong.should_receive(:boot_plugins)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "validating" do
|
62
|
+
it "should run if validatation passes" do
|
63
|
+
generic_runner do
|
64
|
+
should_receive(:validate).and_return(true)
|
65
|
+
should_receive(:run)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
it "dies if validate fails" do
|
69
|
+
generic_runner do
|
70
|
+
should_receive(:validate).and_return(false)
|
71
|
+
should_not_receive(:run)
|
72
|
+
should_receive(:die)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "situating itself within a deploy pack" do
|
79
|
+
context "in an arbitrary directory" do
|
80
|
+
let(:dir) { examples_dir('empty') }
|
81
|
+
before { FileUtils.cd(dir) }
|
82
|
+
subject { generic_runner }
|
83
|
+
its(:deploy_pack_dir) { should == '/' }
|
84
|
+
its(:environment_file) { should == '/config/environment.rb' }
|
85
|
+
its(:in_deploy_pack?) { should be_false }
|
86
|
+
end
|
87
|
+
|
88
|
+
context "when BUNDLE_GEMFILE points at a regular Ruby project" do
|
89
|
+
let(:dir) { examples_dir('empty') }
|
90
|
+
let(:deploy_pack_dir) { examples_dir('ruby_project') }
|
91
|
+
before do
|
92
|
+
FileUtils.cd(dir)
|
93
|
+
ENV.stub!(:[]).with("BUNDLE_GEMFILE").and_return(File.join(deploy_pack_dir, 'Gemfile'))
|
94
|
+
end
|
95
|
+
subject { generic_runner }
|
96
|
+
its(:deploy_pack_dir) { should == '/' }
|
97
|
+
its(:environment_file) { should == '/config/environment.rb' }
|
98
|
+
its(:in_deploy_pack?) { should be_false }
|
99
|
+
end
|
100
|
+
|
101
|
+
context "when BUNDLE_GEMFILE points at a deploy pack" do
|
102
|
+
let(:dir) { examples_dir('empty') }
|
103
|
+
let(:deploy_pack_dir) { examples_dir('deploy_pack') }
|
104
|
+
before do
|
105
|
+
FileUtils.cd(dir)
|
106
|
+
ENV.stub!(:[]).with("BUNDLE_GEMFILE").and_return(File.join(deploy_pack_dir, 'Gemfile'))
|
107
|
+
end
|
108
|
+
subject { generic_runner }
|
109
|
+
its(:deploy_pack_dir) { should == deploy_pack_dir.to_s }
|
110
|
+
its(:environment_file) { should == File.join(deploy_pack_dir, 'config/environment.rb')}
|
111
|
+
its(:in_deploy_pack?) { should be_true }
|
112
|
+
end
|
113
|
+
|
114
|
+
context "in an arbitrary Ruby project with a Gemfile" do
|
115
|
+
let(:dir) { examples_dir('ruby_project') }
|
116
|
+
before { FileUtils.cd(dir) }
|
117
|
+
subject { generic_runner }
|
118
|
+
its(:deploy_pack_dir) { should == '/' }
|
119
|
+
its(:environment_file) { should == '/config/environment.rb' }
|
120
|
+
its(:in_deploy_pack?) { should be_false }
|
121
|
+
end
|
122
|
+
|
123
|
+
context "in a deploy pack with a Gemfile and a config/environment.rb" do
|
124
|
+
let(:dir) { examples_dir('deploy_pack') }
|
125
|
+
before { FileUtils.cd(dir) }
|
126
|
+
subject { generic_runner }
|
127
|
+
its(:deploy_pack_dir) { should == dir }
|
128
|
+
its(:environment_file) { should == File.join(dir, 'config/environment.rb')}
|
129
|
+
its(:in_deploy_pack?) { should be_true }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -2,21 +2,21 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe "Filters" do
|
4
4
|
|
5
|
-
|
5
|
+
describe :null do
|
6
6
|
it_behaves_like 'a processor', :named => :null
|
7
7
|
it "should not pass anything, ever" do
|
8
8
|
processor.given('', 3, 'hi', nil).should emit(0).records
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
|
12
|
+
describe :identity do
|
13
13
|
it_behaves_like 'a processor', :named => :identity
|
14
14
|
it "should pass everything, always" do
|
15
15
|
processor.given('', 3, 'hi', nil).should emit('', 3, 'hi', nil)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
|
19
|
+
describe :regexp do
|
20
20
|
it_behaves_like 'a processor', :named => :regexp
|
21
21
|
it "should pass everything given no 'match' argument" do
|
22
22
|
processor.given('snap', 'crackle', 'pop').should emit('snap', 'crackle', 'pop')
|
@@ -26,7 +26,7 @@ describe "Filters" do
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
|
29
|
+
describe :not_regexp do
|
30
30
|
it_behaves_like 'a processor', :named => :not_regexp
|
31
31
|
it "should pass everything given no 'match' argument" do
|
32
32
|
processor.given('snap', 'crackle', 'pop').should emit('snap', 'crackle', 'pop')
|
@@ -36,7 +36,7 @@ describe "Filters" do
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
|
39
|
+
describe :limit do
|
40
40
|
it_behaves_like 'a processor', :named => :limit
|
41
41
|
it "should pass everything given no 'max' argument" do
|
42
42
|
processor.given('snap', 'crackle', 'pop').should emit('snap', 'crackle', 'pop')
|
@@ -46,16 +46,34 @@ describe "Filters" do
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
|
49
|
+
describe :sample do
|
50
50
|
it_behaves_like 'a processor', :named => :sample
|
51
51
|
it "should pass everything given no 'fraction' argument" do
|
52
52
|
processor.given('snap', 'crackle', 'pop').should emit('snap', 'crackle', 'pop')
|
53
53
|
end
|
54
|
-
it "should pass
|
55
|
-
processor(:fraction => 0.5)
|
56
|
-
|
57
|
-
|
54
|
+
it "should pass a fraction of records matching its 'fraction' argument" do
|
55
|
+
processor(:fraction => 0.5) { |proc| proc.should_receive(:rand).and_return(0.7, 0.1, 0.6) }.given('snap', 'crackle', 'pop').should emit('crackle')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe :head do
|
60
|
+
it_behaves_like 'a processor', :named => :head
|
61
|
+
it "should pass the first 10 records given no argument" do
|
62
|
+
processor.given(*(1..100).to_a).should emit(10).records
|
63
|
+
end
|
64
|
+
it "should pass the first n records" do
|
65
|
+
processor(:n => 5).given(*(1..100).to_a).should emit(5).records
|
58
66
|
end
|
59
67
|
end
|
60
68
|
|
69
|
+
describe :tail do
|
70
|
+
it_behaves_like 'a processor', :named => :tail
|
71
|
+
it "should pass all records given no argument" do
|
72
|
+
processor.given(*(1..100).to_a).should emit(100).records
|
73
|
+
end
|
74
|
+
it "should skip the first n records" do
|
75
|
+
processor(:n => 5).given(*(1..100).to_a).should emit(95).records
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
61
79
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe "Processors" do
|
4
4
|
|
5
5
|
let(:hsh) { { "hi" => "there", "top" => { "lower" => { "lowest" => "value" } } } }
|
6
6
|
let(:ary) { ['1', 2, 'three'] }
|
@@ -12,8 +12,8 @@ describe Wukong::Processor do
|
|
12
12
|
log = mock("logger")
|
13
13
|
log.should_receive(:info).with('hi there')
|
14
14
|
log.should_receive(:info).with('buddy')
|
15
|
-
processor(:logger) do
|
16
|
-
stub!(:log).and_return(log)
|
15
|
+
processor(:logger) do |proc|
|
16
|
+
proc.stub!(:log).and_return(log)
|
17
17
|
end.given('hi there', 'buddy').should emit(0).records
|
18
18
|
end
|
19
19
|
|
@@ -21,8 +21,8 @@ describe Wukong::Processor do
|
|
21
21
|
log = mock("logger")
|
22
22
|
log.should_receive(:debug).with('hi there')
|
23
23
|
log.should_receive(:debug).with('buddy')
|
24
|
-
processor(:logger, level: :debug) do
|
25
|
-
stub!(:log).and_return(log)
|
24
|
+
processor(:logger, level: :debug) do |proc|
|
25
|
+
proc.stub!(:log).and_return(log)
|
26
26
|
end.given('hi there', 'buddy').should emit(0).records
|
27
27
|
end
|
28
28
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe "Reducers" do
|
4
4
|
describe :bin do
|
5
5
|
include_context "reducers"
|
6
6
|
it_behaves_like 'a processor', :named => :bin
|
@@ -32,34 +32,34 @@ describe Wukong::Processor do
|
|
32
32
|
end
|
33
33
|
|
34
34
|
it "counts correctly in each bin" do
|
35
|
-
processor(num_bins: 5).given(*nums).should
|
35
|
+
processor(num_bins: 5).given(*nums).should emit(*bins)
|
36
36
|
end
|
37
37
|
|
38
38
|
it "can express counts logarithmically" do
|
39
|
-
row = processor(num_bins: 5, log_counts: true).given(*nums).
|
39
|
+
row = processor(num_bins: 5, log_counts: true).given(*nums).output.first
|
40
40
|
row.size.should == 3
|
41
41
|
row[2].to_f.should be_within(0.1).of(2.197)
|
42
42
|
end
|
43
43
|
|
44
44
|
it "can add a normalized frequency" do
|
45
|
-
row = processor(num_bins: 5, normalize: true).given(*nums).
|
45
|
+
row = processor(num_bins: 5, normalize: true).given(*nums).output.first
|
46
46
|
row.size.should == 4
|
47
47
|
row[3].to_f.should be_within(0.1).of(0.18)
|
48
48
|
end
|
49
49
|
|
50
50
|
it "can add a normalized frequency and express counts logarithmically" do
|
51
|
-
row = processor(num_bins: 5, normalize: true, log_counts: true).given(*nums).
|
51
|
+
row = processor(num_bins: 5, normalize: true, log_counts: true).given(*nums).output.first
|
52
52
|
row.size.should == 4
|
53
53
|
row[2].to_f.should be_within(0.1).of(2.197)
|
54
54
|
row[3].to_f.should be_within(0.1).of(-1.715)
|
55
55
|
end
|
56
56
|
|
57
57
|
it "can bin on the fly given min, max, and num_bins options" do
|
58
|
-
output = processor(min: -30, max: 30, num_bins: 3) do
|
58
|
+
output = processor(min: -30, max: 30, num_bins: 3) do |proc|
|
59
59
|
# we can bin on the fly
|
60
|
-
values.should_not_receive(:<<)
|
61
|
-
should_not_receive(:bin!)
|
62
|
-
end.given(*nums).
|
60
|
+
proc.values.should_not_receive(:<<)
|
61
|
+
proc.should_not_receive(:bin!)
|
62
|
+
end.given(*nums).output
|
63
63
|
|
64
64
|
output.size.should == 3
|
65
65
|
output.first[0].to_f.should be_within(0.1).of(-30)
|
@@ -67,11 +67,11 @@ describe Wukong::Processor do
|
|
67
67
|
end
|
68
68
|
|
69
69
|
it "can bin on the fly given fixed bin edges" do
|
70
|
-
output = processor(edges: [0,1,5,10]) do
|
70
|
+
output = processor(edges: [0,1,5,10]) do |proc|
|
71
71
|
# we can bin on the fly
|
72
|
-
values.should_not_receive(:<<)
|
73
|
-
should_not_receive(:bin!)
|
74
|
-
end.given(*nums).
|
72
|
+
proc.values.should_not_receive(:<<)
|
73
|
+
proc.should_not_receive(:bin!)
|
74
|
+
end.given(*nums).output
|
75
75
|
output.size.should == 3
|
76
76
|
output[0][0].to_f.should be_within(0.1).of(0.0)
|
77
77
|
output[0][1].to_f.should be_within(0.1).of(1.0)
|
@@ -82,7 +82,7 @@ describe Wukong::Processor do
|
|
82
82
|
end
|
83
83
|
|
84
84
|
it "can extract the value to bin by from an object" do
|
85
|
-
output = processor(by: 'data.n', min: 0).given(*json).
|
85
|
+
output = processor(by: 'data.n', min: 0).given(*json).output
|
86
86
|
output.size.should == 2
|
87
87
|
output.first[0].to_f.should be_within(0.1).of(0.0)
|
88
88
|
output.last[1].to_f.should be_within(0.1).of(100.0)
|
@@ -1,20 +1,21 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe "Reducers" do
|
4
4
|
describe :group do
|
5
5
|
include_context "reducers"
|
6
6
|
it_behaves_like 'a processor', :named => :group
|
7
7
|
|
8
|
-
let(:grouped_strings)
|
9
|
-
let(:
|
8
|
+
let(:grouped_strings) { [['apple', 2], ['banana', 1], ['cookie', 1]] }
|
9
|
+
let(:grouped_nums_from_json) { [[nil, 2], [1, 1], [5, 1], [10, 1], [100, 1]] }
|
10
|
+
let(:grouped_nums_from_tsv) { [[nil, 2], ['1', 1], ['5', 1], ['10', 1], ['100', 1]] }
|
10
11
|
it "will group single values" do
|
11
|
-
processor(:group).given(*strings.sort).should
|
12
|
+
processor(:group).given(*strings.sort).should emit(*grouped_strings)
|
12
13
|
end
|
13
14
|
it "can group from within a JSON hash" do
|
14
|
-
proc = processor(:group, by: 'data.n').given(*json_sorted_n).should
|
15
|
+
proc = processor(:group, by: 'data.n').given(*json_sorted_n).should emit(*grouped_nums_from_json)
|
15
16
|
end
|
16
17
|
it "can group from within a TSV row" do
|
17
|
-
proc = processor(:group, by: '3').given(*tsv_sorted).should
|
18
|
+
proc = processor(:group, by: '3').given(*tsv_sorted).should emit(*grouped_nums_from_tsv)
|
18
19
|
end
|
19
20
|
end
|
20
21
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe "Reducers" do
|
4
4
|
describe :moments do
|
5
5
|
include_context "reducers"
|
6
6
|
it_behaves_like 'a processor', :named => :moments
|
@@ -23,7 +23,7 @@ describe Wukong::Processor do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
it "will leave off the standard deviation if desired" do
|
26
|
-
processor(:moments, group_by: 'outer', of: 'data.n',
|
26
|
+
processor(:moments, group_by: 'outer', of: 'data.n', no_std_dev: true).given(*json_sorted_outer).should emit(
|
27
27
|
{group: nil, count: 2, results: {"data.n" => {}}},
|
28
28
|
{group: 'apple', count: 2, results: {"data.n"=>{:count=>2, :mean=>3.0 }}},
|
29
29
|
{group: 'banana', count: 1, results: {"data.n"=>{:count=>1, :mean=>100.0 }}},
|
@@ -1,118 +1,114 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
let(:bad_record){ nil }
|
5
|
-
let(:serializer){ create_processor(self.class.top_level_description, on_error: :skip) }
|
6
|
-
end
|
3
|
+
describe "Serializers" do
|
7
4
|
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
describe :to_json do
|
6
|
+
it_behaves_like 'a processor', :named => :to_json
|
7
|
+
|
8
|
+
let(:valid_record) { { hi: 'there' } }
|
9
|
+
let(:record_as_json) { '{"hi":"there"}' }
|
10
|
+
let(:model_as_json) { '{"model":"json"}' }
|
11
|
+
let(:valid_model) { double('model', to_json: model_as_json) }
|
12
|
+
|
13
|
+
it 'serializes records to JSON' do
|
14
|
+
processor.given(valid_record).should emit(record_as_json)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'serializes records as pretty JSON when asked' do
|
18
|
+
processor(:pretty => true).given(valid_record).output.first.should include("\n")
|
19
|
+
end
|
11
20
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
serializer.given(bad_record).should emit(0).records
|
21
|
+
it 'defers to models to let them serialize themselves as JSON' do
|
22
|
+
processor.given(valid_model).should emit(model_as_json)
|
23
|
+
end
|
16
24
|
end
|
17
|
-
end
|
18
25
|
|
19
|
-
describe :to_json, serializer: true do
|
20
|
-
let(:valid_record) { { hi: 'there' } }
|
21
|
-
it 'serializes valid records' do
|
22
|
-
serializer.given(valid_record).should emit('{"hi":"there"}')
|
23
|
-
end
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
27
|
+
describe :from_json do
|
28
|
+
it_behaves_like 'a processor', :named => :from_json
|
29
|
+
|
30
|
+
let(:valid_json) { '{"hi": "there"}' }
|
31
|
+
let(:json_parsed) { {"hi" => "there"} }
|
32
|
+
let(:invalid_json) { '{"832323:' }
|
33
|
+
|
34
|
+
it 'deserializes valid JSON' do
|
35
|
+
processor.given(valid_json).should emit(json_parsed)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'handles errors on invalid JSON' do
|
39
|
+
processor { |proc| proc.should_receive(:handle_error).with(invalid_json, kind_of(Exception)) }.given(invalid_json).should emit(0).records
|
40
|
+
end
|
29
41
|
end
|
30
42
|
|
31
|
-
|
32
|
-
|
43
|
+
describe :to_tsv do
|
44
|
+
it_behaves_like 'a processor', :named => :to_tsv
|
33
45
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
context 'given a model' do
|
40
|
-
let(:json_record) { '{"foo":"bar"}' }
|
41
|
-
let(:valid_model) { double('model', to_json: json_record) }
|
46
|
+
let(:valid_record) { ["foo", 2, :a] }
|
47
|
+
let(:invalid_record) { nil }
|
48
|
+
let(:record_as_tsv) { "foo\t2\ta" }
|
49
|
+
let(:model_as_tsv) { "foo\tbar\tbaz" }
|
50
|
+
let(:valid_model) { double('model', to_tsv: model_as_tsv) }
|
42
51
|
|
43
|
-
it '
|
44
|
-
|
45
|
-
serializer.given(valid_model).should emit(json_record)
|
52
|
+
it 'serializes records to JSON' do
|
53
|
+
processor.given(valid_record).should emit(record_as_tsv)
|
46
54
|
end
|
47
|
-
end
|
48
|
-
end
|
49
55
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
it 'serializes valid records' do
|
54
|
-
serializer.given(valid_record).should emit("foo\t2\ta")
|
55
|
-
end
|
56
|
+
it 'defers to models to let them serialize themselves as JSON' do
|
57
|
+
processor.given(valid_model).should emit(model_as_tsv)
|
58
|
+
end
|
56
59
|
|
57
|
-
|
58
|
-
|
59
|
-
let(:valid_model) { double('model', to_tsv: tsv_record) }
|
60
|
-
|
61
|
-
it 'defers to the model to serialize' do
|
62
|
-
valid_model.should_receive(:to_tsv).and_return(tsv_record)
|
63
|
-
serializer.given(valid_model).should emit(tsv_record)
|
60
|
+
it 'handles errors on bad records' do
|
61
|
+
processor { |proc| proc.should_receive(:handle_error) }.given(invalid_record).should emit(0).records
|
64
62
|
end
|
65
63
|
end
|
66
|
-
end
|
67
|
-
|
68
|
-
describe :from_json, serializer: true, handles_errors: true do
|
69
|
-
let(:valid_record) { '{"hi": "there"}' }
|
70
|
-
let(:bad_record) { '{"832323:' }
|
71
|
-
|
72
|
-
it 'deserializes valid records' do
|
73
|
-
serializer.given(valid_record).should emit({'hi' => 'there'})
|
74
|
-
end
|
75
64
|
|
76
|
-
|
77
|
-
|
78
|
-
|
65
|
+
describe :from_tsv, serializer: true, handles_errors: true do
|
66
|
+
it_behaves_like 'a processor', :named => :from_tsv
|
67
|
+
|
68
|
+
let(:valid_tsv) { "foo\t2\ta" }
|
69
|
+
let(:tsv_parsed) { ["foo", "2", "a"] }
|
70
|
+
let(:invalid_tsv) { nil }
|
79
71
|
|
80
|
-
it '
|
81
|
-
|
82
|
-
serializer.given(valid_model).should emit(wire_format)
|
72
|
+
it 'deserializes valid TSV' do
|
73
|
+
processor.given(valid_tsv).should emit(tsv_parsed)
|
83
74
|
end
|
84
|
-
end
|
85
|
-
end
|
86
75
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
it 'deserializes valid records' do
|
91
|
-
serializer.given(valid_record).should emit(['foo', '2', 'a' ])
|
76
|
+
it "handles errors on invalid TSV" do
|
77
|
+
processor { |proc| proc.should_receive(:handle_error).with(invalid_tsv, kind_of(Exception)) }.given(invalid_tsv).should emit(0).records
|
78
|
+
end
|
92
79
|
end
|
93
80
|
|
94
|
-
|
95
|
-
|
96
|
-
|
81
|
+
describe :to_inspect do
|
82
|
+
it_behaves_like 'a processor', :named => :to_inspect
|
83
|
+
|
84
|
+
let(:valid_record) { {"a" => 1 } }
|
85
|
+
let(:record_as_inspect) { valid_record.inspect }
|
86
|
+
let(:model_as_inspect) { '<Model #13e233>' }
|
87
|
+
let(:valid_model) { double('model', inspect: model_as_inspect) }
|
97
88
|
|
98
|
-
it '
|
99
|
-
|
100
|
-
|
89
|
+
it 'serializes records via inspect' do
|
90
|
+
processor.given(valid_record).should emit(record_as_inspect)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'defers to models to let them inspect themselves' do
|
94
|
+
processor.given(valid_model).should emit(model_as_inspect)
|
101
95
|
end
|
102
96
|
end
|
103
|
-
end
|
104
97
|
|
105
|
-
describe :
|
106
|
-
|
107
|
-
|
98
|
+
describe :recordize do
|
99
|
+
let(:model_instance) { double('model') }
|
100
|
+
let(:model_klass) { double('model_def', receive: model_instance) }
|
101
|
+
let(:serializer) { processor(:recordize, model: model_klass, on_error: :skip) }
|
102
|
+
let(:valid_record) { { foo: 'bar' } }
|
103
|
+
let(:invalid_record) { [1,2,3] }
|
108
104
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
let(:serializer) { create_processor(:recordize, model: model_klass, on_error: :skip) }
|
113
|
-
let(:valid_record) { { foo: 'bar' } }
|
105
|
+
it 'recordizes valid records' do
|
106
|
+
processor(model: model_klass).given(valid_record).should emit(model_instance)
|
107
|
+
end
|
114
108
|
|
115
|
-
|
116
|
-
|
109
|
+
it 'handles errors on invalid models' do
|
110
|
+
processor(model: model_klass) { |proc| proc.should_receive(:handle_error).with(invalid_record, kind_of(Exception)) }.given(invalid_record).should emit(0).records
|
111
|
+
end
|
112
|
+
|
117
113
|
end
|
118
114
|
end
|