pants 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,31 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require 'pants/version'
3
+
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "pants"
7
+ s.version = Pants::VERSION
8
+ s.author = %q(Steve Loveless)
9
+ s.homepage = %q(http://github.com/turboladen/pants)
10
+ s.email = %q(steve.loveless@gmail.com)
11
+ s.summary = %q(Easy, fast, I/O multiplexer)
12
+ s.description = %q[Pants redirects IO using EventMachine from one input source
13
+ to many different destinations. In some senses, pants is like a *nix pipe that
14
+ (works on Windows and) allows for duplicating data across many pipes (like
15
+ splice and tee).]
16
+
17
+ s.required_rubygems_version = ">=1.8.0"
18
+ s.files = `git ls-files`.split($/)
19
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
20
+ s.require_paths = %w(lib)
21
+ s.executables = %w(pants)
22
+
23
+ s.add_dependency "eventmachine", ">= 1.0.0"
24
+ s.add_dependency "log_switch", ">= 0.4.0"
25
+ s.add_dependency "thor"
26
+
27
+ s.add_development_dependency "rake"
28
+ s.add_development_dependency "rspec", ">= 2.6.0"
29
+ s.add_development_dependency "simplecov", ">= 0"
30
+ s.add_development_dependency "yard", ">= 0.7.2"
31
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+ require 'pants'
3
+
4
+
5
+ describe "File reads" do
6
+ let(:original_file_path) do
7
+ File.expand_path(File.dirname(__FILE__) + "/../support/pants.wav")
8
+ end
9
+
10
+ let(:dest_file_path) { 'acceptance_test_dest_file' }
11
+
12
+ after do
13
+ FileUtils.rm(dest_file_path)
14
+ end
15
+
16
+ it "reads from a file and copies all data to all other writer types" do
17
+ Pants.read(original_file_path) do |reader|
18
+ reader.write_to(dest_file_path)
19
+ reader.write_to('udp://127.0.0.1:0')
20
+ end
21
+
22
+ original_file_path.should be_the_same_size_as dest_file_path
23
+ end
24
+ end
@@ -0,0 +1,10 @@
1
+ require 'simplecov'
2
+
3
+ SimpleCov.start
4
+
5
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
6
+ Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each { |f| require f }
7
+
8
+ require 'pants'
9
+ Pants.log = true
10
+
@@ -0,0 +1,20 @@
1
+ RSpec::Matchers.define :be_the_same_size_as do |expected|
2
+ match do |actual|
3
+ actual_size = File.stat(actual).size
4
+ expected_size = File.stat(expected).size
5
+ @difference = expected_size - actual_size
6
+ @difference == 0
7
+ end
8
+
9
+ failure_message_for_should do |actual|
10
+ "expected #{actual} to be the same size as #{expected}; diff: #{@difference}"
11
+ end
12
+
13
+ failure_message_for_should_not do |actual|
14
+ "expected #{actual} to not be the same size as #{expected}"
15
+ end
16
+
17
+ description do
18
+ "be the same size as #{expected}"
19
+ end
20
+ end
Binary file
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+ require "pants/core"
3
+
4
+
5
+ describe Pants::Core do
6
+ let(:callback) { double "EM.Callback" }
7
+
8
+ describe "#read" do
9
+ context "unknown URI scheme" do
10
+ it "raises an ArgumentError" do
11
+ expect {
12
+ subject.read("test://stuff")
13
+ }.to raise_error ArgumentError
14
+ end
15
+ end
16
+
17
+ context "known URI scheme" do
18
+ let(:test_reader) do
19
+ double "Pants::TestReader"
20
+ end
21
+
22
+ let(:readers) do
23
+ [{ uri_scheme: 'test', klass: test_reader }]
24
+ end
25
+
26
+ before do
27
+ Pants.stub(:readers).and_return readers
28
+ end
29
+
30
+ it "creates the new reader and adds it to @readers" do
31
+ uri = URI "test://somehost"
32
+
33
+ subject.should_receive(:new_reader_from_uri) do |arg1, arg2|
34
+ arg1.should == uri
35
+ arg2.should be_a EM.Callback
36
+ end.and_return(test_reader)
37
+
38
+ subject.read('test://somehost')
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "#run" do
44
+ let(:reader) do
45
+ r = double "Pants::TestReader"
46
+ r.stub(:read_object)
47
+ r.stub_chain(:writers, :size)
48
+ r.stub_chain(:writers, :each_with_index)
49
+
50
+ r
51
+ end
52
+
53
+ let(:iterator) do
54
+ i = double "EventMachine::Iterator"
55
+ i.should_receive(:each).and_yield(reader, i)
56
+ i.stub(:next)
57
+
58
+ i
59
+ end
60
+
61
+ before do
62
+ EM.stub(:run).and_yield
63
+ EM::Iterator.stub(:new).and_return(iterator)
64
+ end
65
+
66
+ it "starts all of the readers" do
67
+ reader.should_receive(:start)
68
+ subject.instance_variable_set(:@readers, [reader])
69
+ subject.run
70
+ end
71
+ end
72
+
73
+ describe "#new_reader_from_uri" do
74
+ context "uri_scheme exists in readers" do
75
+ let(:test_reader) do
76
+ double "Pants::TestReader"
77
+ end
78
+
79
+ let(:readers) do
80
+ [{ uri_scheme: 'test', klass: test_reader, args: [:host] }]
81
+ end
82
+
83
+ before do
84
+ Pants.stub(:readers).and_return readers
85
+ end
86
+
87
+ it "creates a new Reader based on the scheme mapping" do
88
+ uri = URI "test://testhost"
89
+ test_reader.should_receive(:new).with("testhost", callback)
90
+
91
+ subject.send(:new_reader_from_uri, uri, callback)
92
+ end
93
+ end
94
+
95
+ context "uri_scheme does not exist in readers" do
96
+ it "raises" do
97
+ uri = URI "bobo://host:1234/path"
98
+
99
+ expect {
100
+ subject.send(:new_reader_from_uri, uri, callback)
101
+ }.to raise_error(ArgumentError)
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,340 @@
1
+ require 'spec_helper'
2
+ require 'pants/readers/base_reader'
3
+
4
+
5
+ describe Pants::Readers::BaseReader do
6
+ let(:test_writer) { double "Pants::Writers::TestWriter" }
7
+ let(:core_stopper_callback) { double "EM.Callback" }
8
+ subject { Pants::Readers::BaseReader.new(core_stopper_callback) }
9
+
10
+ describe "#initialize" do
11
+ it "creates a write_to_channel" do
12
+ reader = Pants::Readers::BaseReader.new(core_stopper_callback)
13
+ reader.write_to_channel.should be_a EM::Channel
14
+ end
15
+ end
16
+
17
+ describe "#start" do
18
+ around(:each) do |example|
19
+ EM.run do
20
+ example.run
21
+ EM.stop
22
+ end
23
+ end
24
+
25
+ let(:callback) { double "EventMachine.Callback" }
26
+
27
+ let(:em_iterator) do
28
+ EventMachine::Iterator.new([test_writer])
29
+ end
30
+
31
+ let(:tick_loop) do
32
+ t = double "EventMachine::TickLoop"
33
+ t.should_receive(:on_stop).and_yield
34
+
35
+ t
36
+ end
37
+
38
+ before do
39
+ subject.instance_variable_set(:@writers, [test_writer])
40
+ end
41
+
42
+ it "starts the writers first, then the readers" do
43
+ EventMachine.stub(:tick_loop).and_yield.and_return(tick_loop)
44
+ test_writer.stub(:running?).and_return(false, true)
45
+
46
+ em_iterator.should_receive(:each).and_yield(test_writer, em_iterator)
47
+ em_iterator.stub(:next)
48
+ EventMachine::Iterator.should_receive(:new).and_return(em_iterator)
49
+ test_writer.should_receive(:start)
50
+
51
+ callback.should_receive(:call)
52
+
53
+ subject.start(callback)
54
+ end
55
+ end
56
+
57
+ describe "#stop!" do
58
+ let(:stopper) do
59
+ s = double "EventMachine::Callback"
60
+ s.should_receive(:call)
61
+
62
+ s
63
+ end
64
+
65
+ it "calls succeed on the stopper" do
66
+ subject.should_receive(:stopper).and_return(stopper)
67
+
68
+ subject.stop!
69
+ end
70
+ end
71
+
72
+ describe "#write_to" do
73
+ context "unknown URI scheme" do
74
+ it "raises an ArgumentError" do
75
+ expect {
76
+ subject.write_to("test://stuff")
77
+ }.to raise_error ArgumentError
78
+ end
79
+ end
80
+
81
+ context "known URI scheme" do
82
+ it "creates the new writer, adds it to @writers, and returns it" do
83
+ uri = URI "test://somehost"
84
+ subject.should_receive(:new_writer_from_uri) do |arg1, arg2|
85
+ arg1.should == uri
86
+ arg2.should be_a EventMachine::Channel
87
+ end.and_return(test_writer)
88
+
89
+ subject.write_to('test://somehost').should == test_writer
90
+ end
91
+ end
92
+ end
93
+
94
+ describe "#add_writer" do
95
+ let(:obj_object) { double "TestWriter" }
96
+
97
+ context "obj is a Class" do
98
+ let(:obj) do
99
+ class TestWriter; self; end
100
+ end
101
+
102
+ it "creates a new object of that class with all args and the write channel" do
103
+ first_arg = double "first arg"
104
+ second_arg = double "second arg"
105
+
106
+ obj.should_receive(:new) do |arg1, arg2, arg3|
107
+ arg1.should == first_arg
108
+ arg2.should == second_arg
109
+ arg3.should be_a EventMachine::Channel
110
+ end.and_return(obj_object)
111
+
112
+ subject.add_writer(obj, first_arg, second_arg).should == obj_object
113
+ end
114
+ end
115
+
116
+ context "obj is a instantiated Writer object" do
117
+ it "adds that object to the list of writers and returns it" do
118
+ obj_object.should_receive(:kind_of?).with(Pants::Writers::BaseWriter).
119
+ and_return(true)
120
+
121
+ subject.add_writer(obj_object).should == obj_object
122
+ end
123
+ end
124
+
125
+ context "obj isn't a Class and isn't a Writer" do
126
+ it "raises a Pants::Error" do
127
+ expect {
128
+ subject.add_writer("meow")
129
+ }.to raise_error Pants::Error
130
+ end
131
+ end
132
+ end
133
+
134
+ describe '#remove_writer' do
135
+ before do
136
+ subject.instance_variable_set(:@writers, writers)
137
+ end
138
+
139
+ context "using URI" do
140
+ let(:writer1) do
141
+ double "Pants::Writers::FileWriter", file_path: "file1"
142
+ end
143
+
144
+ let(:writer2) do
145
+ double "Pants::Writers::FileWriter", file_path: "file2"
146
+ end
147
+
148
+ let(:writer3) do
149
+ double "Pants::Writers::UDPWriter",
150
+ host: '127.0.0.1',
151
+ port: 1234
152
+ end
153
+
154
+ let(:writer4) do
155
+ double "Pants::Writers::UDPWriter",
156
+ host: '127.0.0.2',
157
+ port: 1234
158
+ end
159
+
160
+ let(:writers) { [writer1, writer2, writer3, writer4] }
161
+
162
+ context "Pants.writers doesn't define a writer by the given scheme" do
163
+ it "raises an ArgumentError" do
164
+ expect {
165
+ subject.remove_writer("http://1.2.3.4")
166
+ }.to raise_error ArgumentError
167
+ end
168
+ end
169
+
170
+ context "@writers contains a writer type by the given URI" do
171
+ before do
172
+ writer1.should_receive(:is_a?).with(Pants::Writers::UDPWriter).and_return(false)
173
+ writer2.should_receive(:is_a?).with(Pants::Writers::UDPWriter).and_return(false)
174
+ writer3.should_receive(:is_a?).with(Pants::Writers::UDPWriter).and_return(true)
175
+ writer4.should_receive(:is_a?).with(Pants::Writers::UDPWriter).and_return(true)
176
+ end
177
+
178
+ context "but doesn't match criteria given by the URI" do
179
+ it "doesn't remove anything" do
180
+ subject.remove_writer("udp://127.0.0.1:9000")
181
+ subject.writers.should be writers
182
+ end
183
+ end
184
+
185
+ context "and criteria matches given key_value_pairs" do
186
+ it "removes the matching object" do
187
+ subject.remove_writer("udp://127.0.0.1:1234")
188
+ subject.writers.should == [writer1, writer2, writer4]
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+ context "using class and args" do
195
+ let(:writers) do
196
+ [String.new, Hash.new, Array.new]
197
+ end
198
+
199
+ context "@writers doesn't contain a writer by the given class" do
200
+ it "doesn't remove anything" do
201
+ subject.remove_writer(URI, {})
202
+ subject.writers.size.should be 3
203
+ end
204
+ end
205
+
206
+ context "@writers contains a writer by the given class" do
207
+ context "but doesn't match criteria given by key_value_pairs" do
208
+ it "doesn't remove anything" do
209
+ subject.remove_writer(String, size: 1)
210
+ subject.writers.size.should be 3
211
+ end
212
+ end
213
+
214
+ context "and criteria matches given key_value_pairs" do
215
+ it "removes the matching object" do
216
+ subject.remove_writer(String, size: 0)
217
+ subject.writers.size.should be 2
218
+ end
219
+ end
220
+
221
+ context "and criteria of more than 1 object matches given key_value_pairs" do
222
+ let(:writers) do
223
+ [String.new, String.new, String.new]
224
+ end
225
+
226
+ it "removes the matching object" do
227
+ subject.remove_writer(String, size: 0)
228
+ subject.writers.size.should be 0
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
234
+
235
+ describe "#add_seam" do
236
+ context "klass doesn't exist" do
237
+ it "raises a NameError" do
238
+ expect {
239
+ subject.add_seam(BananaSplitPantsParty)
240
+ }.to raise_error NameError
241
+ end
242
+ end
243
+
244
+ context "klass exists" do
245
+ let(:channel) do
246
+ double "EventMachine::Channel"
247
+ end
248
+
249
+ let(:seam_class) do
250
+ s = double "Pants::Seam"
251
+ s.should_receive(:new).with(core_stopper_callback, channel).and_return(seam)
252
+
253
+ s
254
+ end
255
+
256
+ let(:seam) do
257
+ double "Pants::Seam"
258
+ end
259
+
260
+ before do
261
+ subject.instance_variable_set(:@write_to_channel, channel)
262
+ end
263
+
264
+ it "creates the new object and adds it to the internal list of writers" do
265
+ subject.add_seam(seam_class).should == seam
266
+ end
267
+ end
268
+ end
269
+
270
+ describe "#stopper" do
271
+ context "when called back with success" do
272
+ before do
273
+ subject.instance_variable_set(:@writers, [test_writer])
274
+ EM.stub(:next_tick).and_yield
275
+ EM.stub(:tick_loop).and_yield.and_return(tick_loop)
276
+ EM::Iterator.stub(:new).and_return(iterator)
277
+ end
278
+
279
+ let(:tick_loop) do
280
+ t = double "EventMachine::TickLoop"
281
+ t.should_receive(:on_stop).and_yield
282
+
283
+ t
284
+ end
285
+
286
+ let(:iterator) do
287
+ i = double "EventMachine::Iterator"
288
+ i.should_receive(:each).and_yield(test_writer, i)
289
+ i.stub(:next)
290
+
291
+ i
292
+ end
293
+
294
+ it "calls each writer's stopper and the main callback" do
295
+ core_stopper_callback.should_receive(:call)
296
+ test_writer.should_receive(:running?)
297
+ test_writer.should_receive(:stop)
298
+
299
+ subject.send(:stopper).call
300
+ end
301
+ end
302
+ end
303
+
304
+ describe "#new_writer_from_uri" do
305
+ let(:channel) do
306
+ double "EventMachine::Channel"
307
+ end
308
+
309
+ context "uri_scheme exists in Pants.writers" do
310
+ let(:test_writer) do
311
+ double "Pants::Writers::TestWriter"
312
+ end
313
+
314
+ let(:writers) do
315
+ [{ uri_scheme: 'test', klass: test_writer, args: [:host] }]
316
+ end
317
+
318
+ before do
319
+ Pants.stub(:writers).and_return writers
320
+ end
321
+
322
+ it "creates a new Writer based on the scheme mapping" do
323
+ uri = URI "test://testhost"
324
+ test_writer.should_receive(:new).with("testhost", channel)
325
+
326
+ subject.send(:new_writer_from_uri, uri, channel)
327
+ end
328
+ end
329
+
330
+ context "uri_scheme does not exist in writers" do
331
+ it "raises" do
332
+ uri = URI "bobo://host:1234/path"
333
+
334
+ expect {
335
+ subject.send(:new_writer_from_uri, uri, channel)
336
+ }.to raise_error(ArgumentError)
337
+ end
338
+ end
339
+ end
340
+ end