wormholes 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +83 -0
- data/Rakefile +10 -0
- data/bin/hole +43 -0
- data/bin/worm +43 -0
- data/lib/wormholes.rb +12 -0
- data/lib/wormholes/hole.rb +40 -0
- data/lib/wormholes/utilities.rb +16 -0
- data/lib/wormholes/version.rb +3 -0
- data/lib/wormholes/worm.rb +103 -0
- data/test/hole_test.rb +10 -0
- data/test/socket_wormhole_test.rb +67 -0
- data/test/test_helper.rb +7 -0
- data/test/worm_test.rb +10 -0
- data/wormholes.gemspec +19 -0
- metadata +68 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Aaron Royer
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# wormholes
|
2
|
+
|
3
|
+
shell utility that warps text elsewhere
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
```
|
8
|
+
$ gem install wormholes
|
9
|
+
```
|
10
|
+
Make sure to include the "s". The singular form is something else entirely.
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
Open one end of the wormhole with ```worm```. You can pipe any text into it you would like but a wormhole is generally best suited for some progressive, long-running output like a server log.
|
15
|
+
|
16
|
+
```
|
17
|
+
$ rails server | worm
|
18
|
+
```
|
19
|
+
At this point whatever is piped into the wormhole does not go anywhere. It disappears. You could say it goes into a black hole, I suppose.
|
20
|
+
|
21
|
+
If you want to start seeing any of that text again, open the other end with ```hole```.
|
22
|
+
|
23
|
+
```
|
24
|
+
$ hole
|
25
|
+
Started GET "/" for 127.0.0.1 at 2010-09-10 21:07:07 +1000
|
26
|
+
Processing by UsersController#index as HTML
|
27
|
+
SQL (0.5ms) SELECT name
|
28
|
+
FROM sqlite_master
|
29
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
30
|
+
User Load (0.5ms) SELECT "users".* FROM "users" LIMIT 15 OFFSET 0
|
31
|
+
SQL (1.4ms) SELECT COUNT(*) AS count_id FROM "users"
|
32
|
+
Rendered users/index.html.erb within layouts/application (24.8ms)
|
33
|
+
Completed 200 OK in 51ms (Views: 32.9ms | ActiveRecord: 2.3ms)
|
34
|
+
...
|
35
|
+
```
|
36
|
+
|
37
|
+
You can kill ```hole``` with Ctrl-C when you want to stop seeing the output. As long as ```worm``` is running the wormhole will be open and you can fire up ```hole``` again to see what is going into the ```worm``` end.
|
38
|
+
|
39
|
+
You can open as many ```hole```s as you would like, at the same time, and use them however you would use the original program producing the output.
|
40
|
+
|
41
|
+
```
|
42
|
+
$ hole | grep Rendered
|
43
|
+
Rendered users/index.html.erb within layouts/application (24.8ms)
|
44
|
+
|
45
|
+
```
|
46
|
+
Meanwhile in another shell…
|
47
|
+
|
48
|
+
```
|
49
|
+
$ hole | grep SQL
|
50
|
+
SQL (0.5ms) SELECT name
|
51
|
+
SQL (1.4ms) SELECT COUNT(*) AS count_id FROM "users"
|
52
|
+
```
|
53
|
+
|
54
|
+
### Named wormholes
|
55
|
+
|
56
|
+
So far you can only create one wormhole at a time, with one program piping its output into ```worm```. If you open another wormhole while the previous one is still open it will stop the other one. But you might want to open more than one at a time. To do this you can give a wormhole a name and refer to it when you open the other end.
|
57
|
+
|
58
|
+
```
|
59
|
+
$ yes | worm test
|
60
|
+
```
|
61
|
+
This creates a wormhole with the name 'test'. Open the other end with the same name.
|
62
|
+
|
63
|
+
```
|
64
|
+
$ hole test
|
65
|
+
y
|
66
|
+
y
|
67
|
+
y
|
68
|
+
...
|
69
|
+
```
|
70
|
+
|
71
|
+
This allows you to create as many wormholes as you want, at the same time, as long as they have unique names.
|
72
|
+
|
73
|
+
## Why use this?
|
74
|
+
|
75
|
+
Maybe you can think of ways to use this that I have not thought of. Getting log output wherever I want it, on demand, without restarting a server was the original use case.
|
76
|
+
|
77
|
+
So I use it for things like having multiple ```grep```s running on log output (as above). Or to tuck away a running server in another shell and just ```hole | grep something``` in the shell I'm working in to grab the few lines I'm looking for and Ctrl-C it away and get back to what I was doing.
|
78
|
+
|
79
|
+
All of these things can probably be done in other ways with the nice UNIX tools we know and love. If you have a file being appended to you can ```tail -f``` a bunch of times and get multiple outputs of your text. You can do stuff with ```tee```. You can use named pipes and sockets (wormholes uses UNIX sockets under the hood). You can think of wormholes as a formalization of some other ad hoc techniques.
|
80
|
+
|
81
|
+
## License
|
82
|
+
|
83
|
+
wormholes is MIT licensed
|
data/Rakefile
ADDED
data/bin/hole
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Usage: hole [NAME]
|
3
|
+
#
|
4
|
+
# Opens the other end of an existing wormhole. Pipe text into worm and use hole
|
5
|
+
# to see the text coming out in real-time.
|
6
|
+
#
|
7
|
+
# NAME is an optional name to give the created wormhole. If you use a name
|
8
|
+
# with worm then use the same name with hole to open the other end of the
|
9
|
+
# named wormhole.
|
10
|
+
#
|
11
|
+
# To use, pipe output to worm
|
12
|
+
#
|
13
|
+
# $ output_producing_program | worm
|
14
|
+
#
|
15
|
+
# Then, in another shell, get the output with hole
|
16
|
+
#
|
17
|
+
# $ hole
|
18
|
+
# <output comes out here...>
|
19
|
+
#
|
20
|
+
require 'optparse'
|
21
|
+
|
22
|
+
OptionParser.new do |opts|
|
23
|
+
opts.on('-h', '--help') do
|
24
|
+
IO.read(__FILE__).each_line do |line|
|
25
|
+
break unless line =~ /^#/
|
26
|
+
puts line[2..-1] unless line =~ /^#!/
|
27
|
+
end
|
28
|
+
exit
|
29
|
+
end
|
30
|
+
end.parse!
|
31
|
+
|
32
|
+
|
33
|
+
require 'wormholes'
|
34
|
+
|
35
|
+
opts = {}
|
36
|
+
opts[:name] = ARGV[0] if ARGV[0]
|
37
|
+
|
38
|
+
hole = Wormholes::Hole.new opts
|
39
|
+
hole.open
|
40
|
+
|
41
|
+
trap('EXIT') { hole.close }
|
42
|
+
|
43
|
+
hole.listen
|
data/bin/worm
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Usage: worm [NAME]
|
3
|
+
#
|
4
|
+
# Creates a wormhole for text. Pipe text into worm and use the corresponding
|
5
|
+
# hole utility to see the text coming out in real-time.
|
6
|
+
#
|
7
|
+
# NAME is an optional name to give the created wormhole. If you use a name
|
8
|
+
# with worm then use the same name with hole to open the other end of the
|
9
|
+
# named wormhole.
|
10
|
+
#
|
11
|
+
# To use, pipe output to worm
|
12
|
+
#
|
13
|
+
# $ output_producing_program | worm
|
14
|
+
#
|
15
|
+
# Then, in another shell, get the output with hole
|
16
|
+
#
|
17
|
+
# $ hole
|
18
|
+
# <output comes out here...>
|
19
|
+
#
|
20
|
+
require 'optparse'
|
21
|
+
|
22
|
+
OptionParser.new do |opts|
|
23
|
+
opts.on('-h', '--help') do
|
24
|
+
IO.read(__FILE__).each_line do |line|
|
25
|
+
break unless line =~ /^#/
|
26
|
+
puts line[2..-1] unless line =~ /^#!/
|
27
|
+
end
|
28
|
+
exit
|
29
|
+
end
|
30
|
+
end.parse!
|
31
|
+
|
32
|
+
|
33
|
+
require 'wormholes'
|
34
|
+
|
35
|
+
opts = {}
|
36
|
+
opts[:name] = ARGV[0] if ARGV[0]
|
37
|
+
|
38
|
+
worm = Wormholes::Worm.new opts
|
39
|
+
worm.open
|
40
|
+
|
41
|
+
trap('EXIT') { worm.close }
|
42
|
+
|
43
|
+
worm.send_stream($stdin)
|
data/lib/wormholes.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'wormholes/version'
|
2
|
+
require 'wormholes/utilities'
|
3
|
+
require 'wormholes/worm'
|
4
|
+
require 'wormholes/hole'
|
5
|
+
|
6
|
+
module Wormholes
|
7
|
+
NAME_REGEX = /^[\w\d_-]+$/i
|
8
|
+
|
9
|
+
def self.socket_dir
|
10
|
+
ENV['WORMHOLES_SOCKET_DIR'] || File.join(ENV['HOME'], '.wormholes-sockets')
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module Wormholes
|
4
|
+
class Hole
|
5
|
+
include Utilities
|
6
|
+
|
7
|
+
attr_accessor :name
|
8
|
+
|
9
|
+
def initialize(opts={})
|
10
|
+
@name = opts[:name] ? test_wormhole_name(opts[:name]) : 'default'
|
11
|
+
@socket_file = opts[:socket_file] || File.join(::Wormholes.socket_dir, "#{@name}.sock")
|
12
|
+
@output_stream = opts[:output_stream] || $stdout
|
13
|
+
@socket = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def open
|
17
|
+
@socket = UNIXSocket.new(@socket_file)
|
18
|
+
end
|
19
|
+
|
20
|
+
def close
|
21
|
+
@socket.close
|
22
|
+
end
|
23
|
+
|
24
|
+
def listen
|
25
|
+
begin
|
26
|
+
while listen_for_next_line
|
27
|
+
# no-op
|
28
|
+
end
|
29
|
+
rescue Interrupt
|
30
|
+
close
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def listen_for_next_line
|
35
|
+
line = @socket.gets
|
36
|
+
@output_stream.puts line
|
37
|
+
line
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Wormholes
|
2
|
+
|
3
|
+
# Stuff that is needed by both ends of the wormhole
|
4
|
+
module Utilities
|
5
|
+
def test_wormhole_name(name)
|
6
|
+
unless proper_wormhole_name?(name)
|
7
|
+
raise ArgumentError, 'Wormhole name must only contain letters, numbers, hyphens, or underscores'
|
8
|
+
end
|
9
|
+
name
|
10
|
+
end
|
11
|
+
|
12
|
+
def proper_wormhole_name?(name)
|
13
|
+
NAME_REGEX =~ name
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Wormholes
|
5
|
+
class Worm
|
6
|
+
include Utilities
|
7
|
+
|
8
|
+
attr_accessor :server, :sockets, :name
|
9
|
+
|
10
|
+
def initialize(opts={})
|
11
|
+
@name = opts[:name] ? test_wormhole_name(opts[:name]) : 'default'
|
12
|
+
@socket_file = opts[:socket_file] || File.join(::Wormholes.socket_dir, "#{@name}.sock")
|
13
|
+
@sockets = []
|
14
|
+
@server = nil
|
15
|
+
|
16
|
+
@verbose = opts[:verbose] || false
|
17
|
+
@debug = opts[:debug] || false
|
18
|
+
end
|
19
|
+
|
20
|
+
def open
|
21
|
+
socket_dir = File.dirname(@socket_file)
|
22
|
+
FileUtils.mkdir_p(socket_dir) unless File.directory?(socket_dir)
|
23
|
+
|
24
|
+
File.unlink(@socket_file) if File.exist?(@socket_file) && File.socket?(@socket_file)
|
25
|
+
@server = UNIXServer.new(@socket_file)
|
26
|
+
accept_any_connection
|
27
|
+
update_status
|
28
|
+
end
|
29
|
+
|
30
|
+
def close
|
31
|
+
@server.close
|
32
|
+
@server = nil
|
33
|
+
File.unlink(@socket_file) if File.exist?(@socket_file) && File.socket?(@socket_file)
|
34
|
+
$stderr.puts "\n...wormhole closed" if @verbose
|
35
|
+
end
|
36
|
+
|
37
|
+
def send_to_clients(msg)
|
38
|
+
disconnected_sockets = []
|
39
|
+
@sockets.each do |s|
|
40
|
+
begin
|
41
|
+
s.puts msg
|
42
|
+
rescue SystemCallError => e
|
43
|
+
$stderr.puts "Client disconnected: #{s}" if @debug
|
44
|
+
disconnected_sockets << s
|
45
|
+
end
|
46
|
+
end
|
47
|
+
unless disconnected_sockets.empty?
|
48
|
+
@sockets = @sockets - disconnected_sockets
|
49
|
+
debug_sockets
|
50
|
+
update_status
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def send_stream(stream=$stdin)
|
55
|
+
begin
|
56
|
+
while line = stream.gets
|
57
|
+
send_to_clients(line)
|
58
|
+
end
|
59
|
+
rescue Interrupt
|
60
|
+
close
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def accept_any_connection
|
67
|
+
$worm = self
|
68
|
+
Thread.new do
|
69
|
+
while $worm.server
|
70
|
+
$worm.send(:accept_connection)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def accept_connection
|
76
|
+
socket = @server.accept
|
77
|
+
add_client socket
|
78
|
+
update_status
|
79
|
+
end
|
80
|
+
|
81
|
+
def add_client(socket)
|
82
|
+
$stderr.puts "Client connected: #{socket.inspect}" if @debug
|
83
|
+
@sockets << socket
|
84
|
+
debug_sockets
|
85
|
+
end
|
86
|
+
|
87
|
+
def update_status
|
88
|
+
return unless @verbose
|
89
|
+
@previous_status ||= ''
|
90
|
+
@previous_status.length.times { print "\b" }
|
91
|
+
status = "Clients: #{sockets.size}"
|
92
|
+
print status
|
93
|
+
@previous_status = status
|
94
|
+
end
|
95
|
+
|
96
|
+
def debug_sockets
|
97
|
+
return unless @debug
|
98
|
+
$stderr.puts "=== Sockets ==="
|
99
|
+
@sockets.each {|s| $stderr.puts s.inspect}
|
100
|
+
$stderr.puts "==============="
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/test/hole_test.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class HoleTest < MiniTest::Unit::TestCase
|
4
|
+
def test_proper_wormhole_names_are_enforced
|
5
|
+
GOOD_WORMHOLE_NAMES.each {|name| Wormholes::Hole.new :name => name }
|
6
|
+
BAD_WORMHOLE_NAMES.each do |name|
|
7
|
+
assert_raises(ArgumentError) { Wormholes::Hole.new :name => name }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
require 'fileutils'
|
3
|
+
require 'tmpdir'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
# Test wormholes using normal UNIX domain sockets
|
7
|
+
class SocketWormholeTest < MiniTest::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@tmp_dir = File.join( Dir.tmpdir, "wormholes-tests-#{(0...10).map{ ('a'..'z').to_a[rand(26)] }.join}")
|
10
|
+
Dir.mkdir(@tmp_dir)
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
FileUtils.rm_rf @tmp_dir
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_a_line_can_be_sent
|
18
|
+
test_str = "this is what we will send\n"
|
19
|
+
str_io = StringIO.new
|
20
|
+
|
21
|
+
ENV['WORMHOLES_SOCKET_DIR'] = @tmp_dir
|
22
|
+
worm = Wormholes::Worm.new
|
23
|
+
worm.open
|
24
|
+
|
25
|
+
socket_file = File.join(@tmp_dir, 'default.sock')
|
26
|
+
assert File.exist?(socket_file), 'the socket file has been created'
|
27
|
+
assert File.socket?(socket_file), 'the socket file is, indeed, a socket'
|
28
|
+
|
29
|
+
hole = Wormholes::Hole.new(:output_stream => str_io)
|
30
|
+
hole.open
|
31
|
+
|
32
|
+
sleep 0.01 while worm.sockets.size < 1
|
33
|
+
|
34
|
+
thread = Thread.new { hole.listen_for_next_line }
|
35
|
+
worm.send_to_clients test_str
|
36
|
+
thread.join
|
37
|
+
worm.close
|
38
|
+
|
39
|
+
assert_equal test_str, str_io.string
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_can_use_named_wormholes
|
43
|
+
test_str = "we will send this through the named wormhole\n"
|
44
|
+
str_io = StringIO.new
|
45
|
+
wormhole_name = 'deep-space-9'
|
46
|
+
|
47
|
+
ENV['WORMHOLES_SOCKET_DIR'] = @tmp_dir
|
48
|
+
worm = Wormholes::Worm.new(:name => wormhole_name)
|
49
|
+
worm.open
|
50
|
+
|
51
|
+
socket_file = File.join(@tmp_dir, "#{wormhole_name}.sock")
|
52
|
+
assert File.exist?(socket_file), 'the socket file has been created'
|
53
|
+
assert File.socket?(socket_file), 'the socket file is, indeed, a socket'
|
54
|
+
|
55
|
+
hole = Wormholes::Hole.new(:name => wormhole_name, :output_stream => str_io)
|
56
|
+
hole.open
|
57
|
+
|
58
|
+
sleep 0.01 while worm.sockets.size < 1
|
59
|
+
|
60
|
+
thread = Thread.new { hole.listen_for_next_line }
|
61
|
+
worm.send_to_clients test_str
|
62
|
+
thread.join
|
63
|
+
worm.close
|
64
|
+
|
65
|
+
assert_equal test_str, str_io.string
|
66
|
+
end
|
67
|
+
end
|
data/test/test_helper.rb
ADDED
data/test/worm_test.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class WormTest < MiniTest::Unit::TestCase
|
4
|
+
def test_proper_wormhole_names_are_enforced
|
5
|
+
GOOD_WORMHOLE_NAMES.each {|name| Wormholes::Worm.new :name => name }
|
6
|
+
BAD_WORMHOLE_NAMES.each do |name|
|
7
|
+
assert_raises(ArgumentError) { Wormholes::Worm.new :name => name }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
data/wormholes.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'wormholes/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "wormholes"
|
8
|
+
gem.version = Wormholes::VERSION
|
9
|
+
gem.authors = ["Aaron Royer"]
|
10
|
+
gem.email = ["aaronroyer@gmail.com"]
|
11
|
+
gem.description = %q{shell utility that warps text elsewhere}
|
12
|
+
gem.summary = "Pipe text (like log output) in one end and output it anywhere else."
|
13
|
+
gem.homepage = "https://github.com/aaronroyer/wormholes"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wormholes
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Aaron Royer
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-27 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: shell utility that warps text elsewhere
|
15
|
+
email:
|
16
|
+
- aaronroyer@gmail.com
|
17
|
+
executables:
|
18
|
+
- hole
|
19
|
+
- worm
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- .gitignore
|
24
|
+
- Gemfile
|
25
|
+
- LICENSE.txt
|
26
|
+
- README.md
|
27
|
+
- Rakefile
|
28
|
+
- bin/hole
|
29
|
+
- bin/worm
|
30
|
+
- lib/wormholes.rb
|
31
|
+
- lib/wormholes/hole.rb
|
32
|
+
- lib/wormholes/utilities.rb
|
33
|
+
- lib/wormholes/version.rb
|
34
|
+
- lib/wormholes/worm.rb
|
35
|
+
- test/hole_test.rb
|
36
|
+
- test/socket_wormhole_test.rb
|
37
|
+
- test/test_helper.rb
|
38
|
+
- test/worm_test.rb
|
39
|
+
- wormholes.gemspec
|
40
|
+
homepage: https://github.com/aaronroyer/wormholes
|
41
|
+
licenses: []
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.8.23
|
61
|
+
signing_key:
|
62
|
+
specification_version: 3
|
63
|
+
summary: Pipe text (like log output) in one end and output it anywhere else.
|
64
|
+
test_files:
|
65
|
+
- test/hole_test.rb
|
66
|
+
- test/socket_wormhole_test.rb
|
67
|
+
- test/test_helper.rb
|
68
|
+
- test/worm_test.rb
|