pants 0.1.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.
@@ -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