wukong 3.0.0.pre3 → 3.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/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
|