fwd 0.3.3 → 0.4.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/.travis.yml +3 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +11 -11
- data/benchmark/performance.rb +5 -7
- data/fwd.gemspec +1 -1
- data/lib/fwd.rb +23 -31
- data/lib/fwd/backend.rb +2 -2
- data/lib/fwd/buffer.rb +19 -6
- data/lib/fwd/cli.rb +6 -2
- data/lib/fwd/output.rb +25 -28
- data/spec/fwd/buffer_spec.rb +18 -3
- data/spec/fwd/cli_spec.rb +4 -0
- data/spec/fwd/input_spec.rb +1 -1
- data/spec/fwd/output_spec.rb +31 -24
- data/spec/fwd_spec.rb +1 -0
- data/spec/spec_helper.rb +2 -3
- metadata +3 -2
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
source
|
1
|
+
source "https://rubygems.org"
|
2
2
|
gemspec
|
data/Gemfile.lock
CHANGED
@@ -7,22 +7,22 @@ PATH
|
|
7
7
|
servolux
|
8
8
|
|
9
9
|
GEM
|
10
|
-
remote:
|
10
|
+
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
12
|
connection_pool (1.0.0)
|
13
|
-
diff-lcs (1.1
|
13
|
+
diff-lcs (1.2.1)
|
14
14
|
eventmachine-le (1.1.4)
|
15
15
|
rake (10.0.3)
|
16
|
-
rspec (2.
|
17
|
-
rspec-core (~> 2.
|
18
|
-
rspec-expectations (~> 2.
|
19
|
-
rspec-mocks (~> 2.
|
20
|
-
rspec-core (2.
|
21
|
-
rspec-expectations (2.
|
22
|
-
diff-lcs (
|
23
|
-
rspec-mocks (2.
|
16
|
+
rspec (2.13.0)
|
17
|
+
rspec-core (~> 2.13.0)
|
18
|
+
rspec-expectations (~> 2.13.0)
|
19
|
+
rspec-mocks (~> 2.13.0)
|
20
|
+
rspec-core (2.13.0)
|
21
|
+
rspec-expectations (2.13.0)
|
22
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
23
|
+
rspec-mocks (2.13.0)
|
24
24
|
servolux (0.10.0)
|
25
|
-
yard (0.8.
|
25
|
+
yard (0.8.5.2)
|
26
26
|
|
27
27
|
PLATFORMS
|
28
28
|
ruby
|
data/benchmark/performance.rb
CHANGED
@@ -13,16 +13,14 @@ FileUtils.rm_rf TMP
|
|
13
13
|
FileUtils.mkdir_p TMP
|
14
14
|
FileUtils.touch(OUT)
|
15
15
|
|
16
|
-
FWD
|
17
|
-
NCC
|
18
|
-
|
19
|
-
sleep(5)
|
20
|
-
|
16
|
+
FWD = spawn "#{root}/bin/fwd-rb --flush 10000:2 -F tcp://0.0.0.0:7291 --path #{TMP} -v"
|
17
|
+
NCC = spawn "nc -kl 7291", out: [OUT, "w"]
|
21
18
|
EVENTS = 10_000_000
|
22
|
-
|
23
|
-
|
19
|
+
DATA = (("A".."Z").to_a + ("a".."z").to_a).join + "\n"
|
20
|
+
LENGTH = DATA.size
|
24
21
|
CCUR = 5
|
25
22
|
|
23
|
+
sleep(5)
|
26
24
|
ds = Benchmark.realtime do
|
27
25
|
(1..CCUR).map do
|
28
26
|
fork do
|
data/fwd.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.name = File.basename(__FILE__, '.gemspec')
|
10
10
|
s.summary = "fwd >>"
|
11
11
|
s.description = "The minimalistic stream forwarder"
|
12
|
-
s.version = "0.
|
12
|
+
s.version = "0.4.0"
|
13
13
|
|
14
14
|
s.authors = ["Black Square Media"]
|
15
15
|
s.email = "info@blacksquaremedia.com"
|
data/lib/fwd.rb
CHANGED
@@ -11,19 +11,6 @@ require 'servolux'
|
|
11
11
|
class Fwd
|
12
12
|
FLUSH = "\000>>"
|
13
13
|
|
14
|
-
class << self
|
15
|
-
|
16
|
-
attr_writer :logger
|
17
|
-
|
18
|
-
# [Logger] logger instance
|
19
|
-
def logger
|
20
|
-
@logger ||= ::Logger.new(STDOUT).tap do |l|
|
21
|
-
l.level = ::Logger::INFO
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
14
|
# @attr_reader [URI] uri to bind to
|
28
15
|
attr_reader :bind
|
29
16
|
|
@@ -36,6 +23,9 @@ class Fwd
|
|
36
23
|
# @attr_reader [Fwd::Output] output
|
37
24
|
attr_reader :output
|
38
25
|
|
26
|
+
# @attr_reader [Logger] logger
|
27
|
+
attr_reader :logger
|
28
|
+
|
39
29
|
# @attr_reader [Hash] opts
|
40
30
|
attr_reader :opts
|
41
31
|
|
@@ -49,18 +39,19 @@ class Fwd
|
|
49
39
|
# @option opts [Integer] flush_rate flush after N messages
|
50
40
|
# @option opts [Integer] flush_interval flush after N seconds
|
51
41
|
def initialize(opts = {})
|
42
|
+
@opts = opts
|
52
43
|
@bind = URI.parse(opts[:bind] || "tcp://0.0.0.0:7289")
|
53
44
|
@root = Pathname.new(opts[:path] || "tmp")
|
54
45
|
@prefix = opts[:prefix] || "buffer"
|
55
|
-
@
|
46
|
+
@logger = ::Logger.new(opts[:log] || STDOUT)
|
47
|
+
@logger.level = opts[:log_level] || ::Logger::INFO
|
56
48
|
@output = Fwd::Output.new(self)
|
57
49
|
end
|
58
50
|
|
59
51
|
# Starts the loop
|
60
52
|
def run!
|
61
|
-
$0 = "fwd-rb (output)"
|
62
|
-
|
63
53
|
@piper = ::Servolux::Piper.new('rw')
|
54
|
+
|
64
55
|
at_exit do
|
65
56
|
@piper.signal("TERM")
|
66
57
|
end
|
@@ -71,16 +62,8 @@ class Fwd
|
|
71
62
|
end
|
72
63
|
|
73
64
|
@piper.parent do
|
74
|
-
|
75
|
-
|
76
|
-
case val = @piper.gets()
|
77
|
-
when FLUSH
|
78
|
-
output.forward!
|
79
|
-
else
|
80
|
-
logger.error "Received unknown message #{val.class.name} "
|
81
|
-
exit
|
82
|
-
end
|
83
|
-
end
|
65
|
+
$0 = "fwd-rb (output)"
|
66
|
+
output_loop!
|
84
67
|
end
|
85
68
|
end
|
86
69
|
|
@@ -91,6 +74,20 @@ class Fwd
|
|
91
74
|
EM.start_server @bind.host, @bind.port, Fwd::Input, self, buffer
|
92
75
|
end
|
93
76
|
|
77
|
+
# Starts the output loop
|
78
|
+
def output_loop!
|
79
|
+
loop do
|
80
|
+
sleep(0.1)
|
81
|
+
case val = @piper.gets()
|
82
|
+
when FLUSH
|
83
|
+
@output.forward!
|
84
|
+
else
|
85
|
+
logger.error "Received unknown message #{val.class.name}: #{val.inspect}"
|
86
|
+
exit
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
94
91
|
# Initiates flush
|
95
92
|
def flush!
|
96
93
|
@piper.child do
|
@@ -98,11 +95,6 @@ class Fwd
|
|
98
95
|
end
|
99
96
|
end
|
100
97
|
|
101
|
-
# [Logger] logger instance
|
102
|
-
def logger
|
103
|
-
self.class.logger
|
104
|
-
end
|
105
|
-
|
106
98
|
end
|
107
99
|
|
108
100
|
%w|buffer output backend input pool cli|.each do |name|
|
data/lib/fwd/backend.rb
CHANGED
data/lib/fwd/buffer.rb
CHANGED
@@ -13,12 +13,15 @@ class Fwd::Buffer
|
|
13
13
|
@rate = (core.opts[:flush_rate] || 10_000).to_i
|
14
14
|
@limit = [core.opts[:buffer_limit].to_i, MAX_LIMIT].reject(&:zero?).min
|
15
15
|
@count = 0
|
16
|
+
cleanup!
|
16
17
|
reschedule!
|
18
|
+
|
19
|
+
at_exit { rotate! }
|
17
20
|
end
|
18
21
|
|
19
22
|
# @param [String] data binary data
|
20
23
|
def concat(data)
|
21
|
-
rotate! if
|
24
|
+
rotate! if limit_reached?
|
22
25
|
@fd.write(data)
|
23
26
|
@count += 1
|
24
27
|
flush! if flush?
|
@@ -43,8 +46,8 @@ class Fwd::Buffer
|
|
43
46
|
return if @fd && @fd.size.zero?
|
44
47
|
|
45
48
|
if @fd
|
46
|
-
|
47
|
-
|
49
|
+
close(@fd.path)
|
50
|
+
logger.debug { "Rotated #{File.basename(@fd.path)}, #{@fd.size / 1024}k" }
|
48
51
|
end
|
49
52
|
|
50
53
|
@fd = new_file
|
@@ -52,8 +55,8 @@ class Fwd::Buffer
|
|
52
55
|
logger.warn "Rotation delayed: #{e.message}"
|
53
56
|
end
|
54
57
|
|
55
|
-
# @return [Boolean] true if
|
56
|
-
def
|
58
|
+
# @return [Boolean] true if limit reached
|
59
|
+
def limit_reached?
|
57
60
|
@fd.nil? || @fd.size >= @limit
|
58
61
|
rescue Errno::ENOENT
|
59
62
|
false
|
@@ -72,6 +75,16 @@ class Fwd::Buffer
|
|
72
75
|
file
|
73
76
|
end
|
74
77
|
|
78
|
+
def close(file)
|
79
|
+
FileUtils.mv(file, file.sub(/\.open$/, ".closed"))
|
80
|
+
end
|
81
|
+
|
82
|
+
def cleanup!
|
83
|
+
Dir[root.join("#{prefix}.*.open")].each do |file|
|
84
|
+
File.size(file).zero? ? File.unlink(file) : close(file)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
75
88
|
def reschedule!
|
76
89
|
return unless @interval > 0
|
77
90
|
|
@@ -80,7 +93,7 @@ class Fwd::Buffer
|
|
80
93
|
end
|
81
94
|
|
82
95
|
def generate_name
|
83
|
-
[prefix, Time.now.utc.
|
96
|
+
[prefix, (Time.now.utc.to_f * 1000).round, SecureRandom.hex(2)].join(".")
|
84
97
|
end
|
85
98
|
|
86
99
|
end
|
data/lib/fwd/cli.rb
CHANGED
@@ -48,8 +48,12 @@ class Fwd::CLI < Hash
|
|
48
48
|
update prefix: prefix
|
49
49
|
end
|
50
50
|
|
51
|
-
o.on("-
|
52
|
-
|
51
|
+
o.on("-l", "--log PATH", "Custom log path. Default: STDOUT") do |path|
|
52
|
+
update log: path
|
53
|
+
end
|
54
|
+
|
55
|
+
o.on("-v", "--verbose", "Enable verbose logging.") do
|
56
|
+
update log_level: ::Logger::DEBUG
|
53
57
|
end
|
54
58
|
|
55
59
|
o.separator ""
|
data/lib/fwd/output.rb
CHANGED
@@ -2,7 +2,6 @@ class Fwd::Output
|
|
2
2
|
extend Forwardable
|
3
3
|
def_delegators :core, :logger, :root, :prefix
|
4
4
|
|
5
|
-
CHUNK_SIZE = 16 * 1024
|
6
5
|
RESCUABLE = [
|
7
6
|
Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EHOSTUNREACH, Errno::EPIPE,
|
8
7
|
Errno::ENETUNREACH, Errno::ENETDOWN, Errno::EINVAL, Errno::ETIMEDOUT,
|
@@ -26,23 +25,24 @@ class Fwd::Output
|
|
26
25
|
return if @forwarding
|
27
26
|
|
28
27
|
@forwarding = true
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
28
|
+
while (q = closed_files) && (file = q.shift)
|
29
|
+
ok = reserve(file) do |reserved|
|
30
|
+
start = Time.now
|
31
|
+
success = stream_file(reserved)
|
32
|
+
real = Time.now - start
|
33
|
+
logger.info { "Flushed #{reserved.basename}, #{reserved.size.fdiv(1024).round}k in #{real.round(1)}s (Q: #{q.size})" }
|
34
|
+
success
|
36
35
|
end
|
37
|
-
|
38
|
-
@forwarding = false
|
36
|
+
ok || break
|
39
37
|
end
|
38
|
+
ensure
|
39
|
+
@forwarding = false
|
40
40
|
end
|
41
41
|
|
42
|
-
# @param [
|
43
|
-
def
|
42
|
+
# @param [Pathname] file file to stream
|
43
|
+
def stream_file(file)
|
44
44
|
pool.any? do |backend|
|
45
|
-
|
45
|
+
stream_to(backend, file)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -54,29 +54,22 @@ class Fwd::Output
|
|
54
54
|
target = Pathname.new(file.sub(/\.closed$/, ".reserved"))
|
55
55
|
FileUtils.mv file, target.to_s
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
result = yield(io)
|
60
|
-
end
|
61
|
-
|
62
|
-
if result
|
57
|
+
success = yield(target)
|
58
|
+
if success
|
63
59
|
target.unlink
|
64
60
|
else
|
65
|
-
logger.error "Flushing
|
66
|
-
FileUtils.mv target.to_s,
|
61
|
+
logger.error "Flushing #{File.basename(file)} failed"
|
62
|
+
FileUtils.mv target.to_s, file
|
67
63
|
end
|
68
64
|
|
69
|
-
|
65
|
+
success
|
70
66
|
rescue Errno::ENOENT => e
|
71
67
|
# Ignore if file was alread flushed by another process
|
72
|
-
logger.warn "Flushing
|
68
|
+
logger.warn "Flushing #{File.basename(file)} postponed: #{e.message}"
|
73
69
|
end
|
74
70
|
|
75
|
-
def
|
76
|
-
|
77
|
-
until io.eof?
|
78
|
-
backend.write(io.read(CHUNK_SIZE))
|
79
|
-
end
|
71
|
+
def stream_to(backend, file)
|
72
|
+
backend.stream(file)
|
80
73
|
true
|
81
74
|
rescue *RESCUABLE => e
|
82
75
|
logger.error "Backend #{backend} failed: #{e.class.name} #{e.message}"
|
@@ -84,4 +77,8 @@ class Fwd::Output
|
|
84
77
|
false
|
85
78
|
end
|
86
79
|
|
80
|
+
def closed_files
|
81
|
+
Dir[root.join("#{prefix}.*.closed")].sort
|
82
|
+
end
|
83
|
+
|
87
84
|
end
|
data/spec/fwd/buffer_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe Fwd::Buffer do
|
4
4
|
|
5
5
|
def files(glob = "*")
|
6
|
-
Dir[root.join(glob)]
|
6
|
+
Dir[root.join(glob)].map {|f| File.basename(f) }.sort
|
7
7
|
end
|
8
8
|
|
9
9
|
let(:buffer) { described_class.new core }
|
@@ -18,7 +18,22 @@ describe Fwd::Buffer do
|
|
18
18
|
its(:limit) { should be(2048) }
|
19
19
|
its(:timer) { should be(timer) }
|
20
20
|
its(:fd) { should be_nil }
|
21
|
-
its(:logger) { should be(
|
21
|
+
its(:logger) { should be(core.logger) }
|
22
|
+
|
23
|
+
it 'should clean up existing files' do
|
24
|
+
FileUtils.mkdir_p(root.to_s)
|
25
|
+
f1, f2 = "buffer.0.blank.open", "buffer.0.filled.open"
|
26
|
+
root.join(f1).open("wb") {}
|
27
|
+
root.join(f2).open("wb") {|f| f << "A" }
|
28
|
+
lambda { subject }.should change { files }.from([f1, f2]).to(["buffer.0.filled.closed"])
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should generate unique buffer file names' do
|
32
|
+
Time.stub now: Time.at(1313131313.2345678)
|
33
|
+
SecureRandom.stub hex: "6a7b8c9d"
|
34
|
+
buffer.concat("x")
|
35
|
+
buffer.fd.path.should == root.join("buffer.1313131313235.6a7b8c9d.open").to_s
|
36
|
+
end
|
22
37
|
|
23
38
|
describe "concat" do
|
24
39
|
it 'should concat data' do
|
@@ -51,7 +66,7 @@ describe Fwd::Buffer do
|
|
51
66
|
it { should change { files.size }.by(1) }
|
52
67
|
|
53
68
|
it 'should archive previous file' do
|
54
|
-
previous = buffer.fd.path
|
69
|
+
previous = File.basename(buffer.fd.path)
|
55
70
|
subject.call
|
56
71
|
files.should include(previous.sub("open", "closed").to_s)
|
57
72
|
end
|
data/spec/fwd/cli_spec.rb
CHANGED
@@ -9,6 +9,8 @@ describe Fwd::CLI do
|
|
9
9
|
"--bind", "tcp://127.0.0.1:7289",
|
10
10
|
"--forward", "tcp://1.2.3.4:1234,tcp://1.2.3.5:1235",
|
11
11
|
"--flush", "1200:90",
|
12
|
+
"--log", "/dev/null",
|
13
|
+
"--verbose",
|
12
14
|
]
|
13
15
|
end
|
14
16
|
|
@@ -19,6 +21,8 @@ describe Fwd::CLI do
|
|
19
21
|
its([:forward]) { should == ["tcp://1.2.3.4:1234", "tcp://1.2.3.5:1235"] }
|
20
22
|
its([:flush_rate]) { should == 1200 }
|
21
23
|
its([:flush_interval]) { should == 90 }
|
24
|
+
its([:log]) { should == "/dev/null" }
|
25
|
+
its([:log_level]) { should == 0 }
|
22
26
|
its(:core) { should be_instance_of(Fwd) }
|
23
27
|
|
24
28
|
end
|
data/spec/fwd/input_spec.rb
CHANGED
data/spec/fwd/output_spec.rb
CHANGED
@@ -2,6 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Fwd::Output do
|
4
4
|
|
5
|
+
before { FileUtils.mkdir_p root.to_s }
|
5
6
|
let(:output) { described_class.new core }
|
6
7
|
subject { output }
|
7
8
|
|
@@ -47,35 +48,37 @@ describe Fwd::Output do
|
|
47
48
|
subject.pool.checkout {|c| c.should be_instance_of(Fwd::Backend) }
|
48
49
|
end
|
49
50
|
|
50
|
-
describe "
|
51
|
-
|
52
|
-
|
51
|
+
describe "streaming data" do
|
52
|
+
|
53
|
+
def stream(data)
|
54
|
+
tmp = root.join("temp.file")
|
55
|
+
tmp.open("wb") {|f| f << data }
|
56
|
+
subject.stream_file(tmp)
|
53
57
|
end
|
54
58
|
|
55
59
|
it 'should forward data to backends' do
|
56
60
|
servers(7291, 7292) do
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
+
stream("A").should be(true)
|
62
|
+
stream("B").should be(true)
|
63
|
+
stream("C").should be(true)
|
64
|
+
stream("D").should be(true)
|
61
65
|
end.should == { 7291=>"BD", 7292=>"AC" }
|
62
66
|
end
|
63
67
|
|
64
68
|
it 'should handle partial fallouts' do
|
65
69
|
servers(7291) do
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
sleep(1)
|
70
|
+
stream("A").should be(true)
|
71
|
+
stream("B").should be(true)
|
72
|
+
stream("C").should be(true)
|
73
|
+
stream("D").should be(true)
|
71
74
|
end.should == { 7291=>"ABCD" }
|
72
75
|
end
|
73
76
|
|
74
77
|
it 'should handle full fallouts' do
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
78
|
+
stream("A").should be(false)
|
79
|
+
stream("B").should be(false)
|
80
|
+
stream("C").should be(false)
|
81
|
+
stream("D").should be(false)
|
79
82
|
end
|
80
83
|
|
81
84
|
end
|
@@ -83,7 +86,7 @@ describe Fwd::Output do
|
|
83
86
|
describe "forwarding" do
|
84
87
|
|
85
88
|
def write(file)
|
86
|
-
file.open("
|
89
|
+
file.open("wb") {|f| f << "x" }
|
87
90
|
file
|
88
91
|
end
|
89
92
|
|
@@ -91,14 +94,18 @@ describe Fwd::Output do
|
|
91
94
|
Dir[root.join(glob)].map {|f| File.basename f }.sort
|
92
95
|
end
|
93
96
|
|
94
|
-
before { subject.stub!
|
95
|
-
before { FileUtils.mkdir_p root.to_s }
|
97
|
+
before { subject.stub! stream_file: true }
|
96
98
|
let!(:f1) { write root.join("buffer.1.closed") }
|
97
99
|
let!(:f2) { write root.join("buffer.2.open") }
|
98
100
|
let!(:f3) { write root.join("buffer.3.closed") }
|
99
101
|
|
100
|
-
it 'should
|
101
|
-
subject.should_receive(:
|
102
|
+
it 'should send the data' do
|
103
|
+
subject.should_receive(:stream_file).twice.and_return(true)
|
104
|
+
subject.forward!
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should stop on first failure' do
|
108
|
+
subject.should_receive(:stream_file).once.and_return(false)
|
102
109
|
subject.forward!
|
103
110
|
end
|
104
111
|
|
@@ -108,9 +115,9 @@ describe Fwd::Output do
|
|
108
115
|
}.to(["buffer.2.open"])
|
109
116
|
end
|
110
117
|
|
111
|
-
it 'should handle
|
112
|
-
subject.should_receive(:
|
113
|
-
subject.should_receive(:
|
118
|
+
it 'should handle revert failed files' do
|
119
|
+
subject.should_receive(:stream_file).and_return(true)
|
120
|
+
subject.should_receive(:stream_file).and_return(false)
|
114
121
|
|
115
122
|
lambda { subject.forward! }.should change {
|
116
123
|
files
|
data/spec/fwd_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -21,6 +21,8 @@ module Fwd::TestHelper
|
|
21
21
|
def core
|
22
22
|
@_core ||= Fwd.new \
|
23
23
|
path: root,
|
24
|
+
log: "/dev/null",
|
25
|
+
log_level: Logger::DEBUG,
|
24
26
|
flush_rate: 20,
|
25
27
|
buffer_limit: 2048,
|
26
28
|
forward: ["tcp://127.0.0.1:7291", "tcp://127.0.0.1:7292"]
|
@@ -34,9 +36,6 @@ end
|
|
34
36
|
|
35
37
|
RSpec.configure do |c|
|
36
38
|
c.include(Fwd::TestHelper)
|
37
|
-
c.before(:suite) do
|
38
|
-
Fwd.logger = Logger.new("/dev/null")
|
39
|
-
end
|
40
39
|
c.before(:each) do
|
41
40
|
FileUtils.rm_rf root.to_s
|
42
41
|
EM.stub add_periodic_timer: timer
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fwd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-03-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine-le
|
@@ -131,6 +131,7 @@ extensions: []
|
|
131
131
|
extra_rdoc_files: []
|
132
132
|
files:
|
133
133
|
- .gitignore
|
134
|
+
- .travis.yml
|
134
135
|
- Gemfile
|
135
136
|
- Gemfile.lock
|
136
137
|
- Rakefile
|