wormholes 0.0.1
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/.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
|