forward 0.3.3 → 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile +0 -2
- data/README.md +24 -0
- data/Rakefile +3 -1
- data/bin/forward +1 -1
- data/forward.gemspec +17 -11
- data/lib/forward/api/resource.rb +51 -83
- data/lib/forward/api/tunnel.rb +41 -68
- data/lib/forward/api/user.rb +14 -11
- data/lib/forward/api.rb +7 -26
- data/lib/forward/cli.rb +55 -253
- data/lib/forward/command/account.rb +69 -0
- data/lib/forward/command/base.rb +62 -0
- data/lib/forward/command/config.rb +64 -0
- data/lib/forward/command/tunnel.rb +178 -0
- data/lib/forward/common.rb +44 -0
- data/lib/forward/config.rb +75 -118
- data/lib/forward/request.rb +72 -0
- data/lib/forward/socket.rb +125 -0
- data/lib/forward/static/app.rb +157 -0
- data/lib/forward/static/directory.erb +142 -0
- data/lib/forward/tunnel.rb +102 -40
- data/lib/forward/version.rb +1 -1
- data/lib/forward.rb +80 -63
- data/test/api/resource_test.rb +70 -54
- data/test/api/tunnel_test.rb +50 -51
- data/test/api/user_test.rb +33 -20
- data/test/cli_test.rb +0 -126
- data/test/command/account_test.rb +26 -0
- data/test/command/tunnel_test.rb +133 -0
- data/test/config_test.rb +103 -54
- data/test/forward_test.rb +47 -0
- data/test/test_helper.rb +35 -26
- data/test/tunnel_test.rb +50 -22
- metadata +210 -169
- data/forwardhq.crt +0 -112
- data/lib/forward/api/client_log.rb +0 -20
- data/lib/forward/api/tunnel_key.rb +0 -18
- data/lib/forward/client.rb +0 -110
- data/lib/forward/error.rb +0 -12
- data/test/api/tunnel_key_test.rb +0 -28
- data/test/api_test.rb +0 -0
- data/test/client_test.rb +0 -8
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Forward
|
4
|
+
module Static
|
5
|
+
|
6
|
+
class TemplateContext
|
7
|
+
FILESIZE_FORMAT = [
|
8
|
+
['%.1fT', 1 << 40],
|
9
|
+
['%.1fG', 1 << 30],
|
10
|
+
['%.1fM', 1 << 20],
|
11
|
+
['%.1fK', 1 << 10],
|
12
|
+
]
|
13
|
+
|
14
|
+
|
15
|
+
def initialize(root, path_info)
|
16
|
+
@root = root
|
17
|
+
@project_name = File.basename(root)
|
18
|
+
@path_info = path_info
|
19
|
+
@path = File.join(root, path_info)
|
20
|
+
end
|
21
|
+
|
22
|
+
def path_components
|
23
|
+
@path_info.split('/')[1..-1]
|
24
|
+
end
|
25
|
+
|
26
|
+
def files
|
27
|
+
_files = []
|
28
|
+
|
29
|
+
Dir["#{@path}*"].sort.each do |path|
|
30
|
+
_stat = stat(path)
|
31
|
+
next if _stat.nil?
|
32
|
+
|
33
|
+
basename = File.basename(path)
|
34
|
+
url = path.sub(@root, '')
|
35
|
+
ext = File.extname(path)
|
36
|
+
size = _stat.size
|
37
|
+
type = _stat.directory? ? 'directory' : Rack::Mime.mime_type(ext)
|
38
|
+
size = _stat.directory? ? '-' : filesize_format(size)
|
39
|
+
|
40
|
+
if _stat.directory?
|
41
|
+
url << '/'
|
42
|
+
basename << '/'
|
43
|
+
end
|
44
|
+
|
45
|
+
_files << {
|
46
|
+
url: url,
|
47
|
+
basename: basename,
|
48
|
+
size: size,
|
49
|
+
type: type,
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
_files
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_binding
|
57
|
+
binding
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def stat(path)
|
63
|
+
File.stat(path)
|
64
|
+
rescue Errno::ENOENT, Errno::ELOOP
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
|
68
|
+
def filesize_format(int)
|
69
|
+
FILESIZE_FORMAT.each do |format, size|
|
70
|
+
return format % (int.to_f / size) if int >= size
|
71
|
+
end
|
72
|
+
|
73
|
+
int.to_s + 'B'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class App
|
78
|
+
attr_reader :files
|
79
|
+
attr_accessor :root
|
80
|
+
attr_accessor :path
|
81
|
+
|
82
|
+
def initialize(root, app = nil)
|
83
|
+
@root = File.expand_path(root)
|
84
|
+
@app = app || Rack::File.new(@root)
|
85
|
+
@listing_view = File.read(File.expand_path('../directory.erb', __FILE__))
|
86
|
+
end
|
87
|
+
|
88
|
+
def call(env)
|
89
|
+
dup._call(env)
|
90
|
+
end
|
91
|
+
|
92
|
+
def _call(env)
|
93
|
+
@env = env
|
94
|
+
@path_info = Rack::Utils.unescape(env['PATH_INFO'])
|
95
|
+
|
96
|
+
if forbidden?
|
97
|
+
render_404
|
98
|
+
else
|
99
|
+
@path = File.join(@root, @path_info)
|
100
|
+
process
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def forbidden?
|
107
|
+
@path_info =~ /\.\./
|
108
|
+
end
|
109
|
+
|
110
|
+
def render_directory_listing
|
111
|
+
context = TemplateContext.new(@root, @path_info)
|
112
|
+
body = ERB.new(@listing_view).result(context.get_binding)
|
113
|
+
|
114
|
+
[ 200, {'Content-Type' => 'text/html; charset=utf-8'}, [body] ]
|
115
|
+
end
|
116
|
+
|
117
|
+
def has_index?
|
118
|
+
File.exist?(File.join(@path, 'index.html'))
|
119
|
+
end
|
120
|
+
|
121
|
+
def process
|
122
|
+
return render_404 unless File.exist?(@path)
|
123
|
+
|
124
|
+
@stat = File.stat(@path)
|
125
|
+
|
126
|
+
raise Errno::ENOENT, 'No such file or directory' unless @stat.readable?
|
127
|
+
|
128
|
+
if @stat.directory?
|
129
|
+
return render_404 unless @path.end_with?('/')
|
130
|
+
return render_directory_listing unless has_index?
|
131
|
+
|
132
|
+
@env['PATH_INFO'] << 'index.html'
|
133
|
+
end
|
134
|
+
|
135
|
+
Rack::File.new(@root).call(@env)
|
136
|
+
|
137
|
+
rescue Errno::ENOENT, Errno::ELOOP => e
|
138
|
+
Forward.logger.debug e
|
139
|
+
Forward.logger.debug e.message
|
140
|
+
render_404
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
def render_404
|
145
|
+
body = "Not Found: #{@path_info}\n"
|
146
|
+
headers = {
|
147
|
+
"Content-Type" => "text/plain",
|
148
|
+
"Content-Length" => Rack::Utils.bytesize(body).to_s,
|
149
|
+
"X-Cascade" => "pass"
|
150
|
+
}
|
151
|
+
|
152
|
+
[404, headers, [body]]
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Directory Listing: <%= @project_name%><%= @path_info %></title>
|
5
|
+
<meta http-equiv="content-kind" content="text/html; charset=utf-8">
|
6
|
+
<link rel="stylesheet" href="https://assets.50east.co/v1.0/css/mark.css">
|
7
|
+
<link rel="stylesheet" href="https://assets.50east.co/v1.0/css/picons.css">
|
8
|
+
<link rel="stylesheet" href="https://assets.50east.co/v1.0/css/core.css">
|
9
|
+
<style kind="text/css">
|
10
|
+
h1 {
|
11
|
+
padding-left: 3rem;
|
12
|
+
text-indent: -1rem;
|
13
|
+
font-size: 1rem;
|
14
|
+
color: #999;
|
15
|
+
cursor: default;
|
16
|
+
}
|
17
|
+
|
18
|
+
h1 .root {
|
19
|
+
color: #222;
|
20
|
+
font-weight: 600;
|
21
|
+
}
|
22
|
+
|
23
|
+
h1 a {
|
24
|
+
color: inherit;
|
25
|
+
white-space: nowrap;
|
26
|
+
}
|
27
|
+
|
28
|
+
a {
|
29
|
+
color: #222;
|
30
|
+
}
|
31
|
+
|
32
|
+
a:hover {
|
33
|
+
color: #27c166 !important;
|
34
|
+
}
|
35
|
+
|
36
|
+
table {
|
37
|
+
max-width: 35rem;
|
38
|
+
width: 100%;
|
39
|
+
}
|
40
|
+
|
41
|
+
td,
|
42
|
+
th {
|
43
|
+
padding: 0.25rem 0 0.5rem;
|
44
|
+
}
|
45
|
+
|
46
|
+
th {
|
47
|
+
text-align: left;
|
48
|
+
padding-bottom: 1rem;
|
49
|
+
color: #ccc;
|
50
|
+
}
|
51
|
+
|
52
|
+
td.kind,
|
53
|
+
td.size {
|
54
|
+
color: #555;
|
55
|
+
}
|
56
|
+
|
57
|
+
.size {
|
58
|
+
text-align: right;
|
59
|
+
padding-right: 1rem;
|
60
|
+
}
|
61
|
+
|
62
|
+
td.name {
|
63
|
+
position: relative;
|
64
|
+
font-weight: 500;
|
65
|
+
}
|
66
|
+
|
67
|
+
.file-icon {
|
68
|
+
position: absolute;
|
69
|
+
top: 8px; left: -33px;
|
70
|
+
width: 20px;
|
71
|
+
height: 20px;
|
72
|
+
}
|
73
|
+
|
74
|
+
*[data-type="file"] .file-icon {
|
75
|
+
background-position: 0% -0.5%;
|
76
|
+
}
|
77
|
+
|
78
|
+
*[data-type="directory"] .file-icon {
|
79
|
+
top: 10px;
|
80
|
+
left: -34px;
|
81
|
+
}
|
82
|
+
|
83
|
+
footer p {
|
84
|
+
text-align: left !important;
|
85
|
+
}
|
86
|
+
|
87
|
+
footer a {
|
88
|
+
color: #27c166 !important;
|
89
|
+
}
|
90
|
+
|
91
|
+
footer a:hover {
|
92
|
+
border-bottom: 1px solid #ccc;
|
93
|
+
}
|
94
|
+
</style>
|
95
|
+
</head>
|
96
|
+
|
97
|
+
<body>
|
98
|
+
<div class="page">
|
99
|
+
<h1>
|
100
|
+
<a href="/" class="root"><%= @project_name %></a>
|
101
|
+
<% if path_components%>
|
102
|
+
<% path_components.each_with_index do |part, i| %>
|
103
|
+
/ <a href="/<%= path_components[0..i].join('/') %>/"><%= part %></a>
|
104
|
+
<% end %>
|
105
|
+
<% end %>
|
106
|
+
</h1>
|
107
|
+
|
108
|
+
<section>
|
109
|
+
<table>
|
110
|
+
<thead>
|
111
|
+
<tr>
|
112
|
+
<th class="name">Name</th>
|
113
|
+
<th class="size">Size</th>
|
114
|
+
<th class="kind">Kind</th>
|
115
|
+
</tr>
|
116
|
+
</thead>
|
117
|
+
<tbody>
|
118
|
+
<% files.each do |file| %>
|
119
|
+
<tr>
|
120
|
+
<td class="name"><div class="file-icon"></div><a href="<%= file[:url] %>"><%= file[:basename] %></a></td><td class="size"><%= file[:size] %></td><td class="kind"><%= file[:type] %></td>
|
121
|
+
</tr>
|
122
|
+
<% end %>
|
123
|
+
</tbody>
|
124
|
+
</table>
|
125
|
+
</section>
|
126
|
+
<footer>
|
127
|
+
<p>
|
128
|
+
Brought to you by <a href="https://forwardhq.com/">Forward</a>
|
129
|
+
</p>
|
130
|
+
</footer>
|
131
|
+
</div>
|
132
|
+
<script src="https://assets.50east.co/v1.0/js/core.js"></script>
|
133
|
+
<script>
|
134
|
+
$('td.name').each(function(){
|
135
|
+
var filename = $.trim($(this).text());
|
136
|
+
var row = $(this).closest('tr');
|
137
|
+
|
138
|
+
row.attr('data-type', FileTypes.getTypeForFilename(filename));
|
139
|
+
});
|
140
|
+
</script>
|
141
|
+
</body>
|
142
|
+
</html>
|
data/lib/forward/tunnel.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Forward
|
2
2
|
class Tunnel
|
3
|
-
|
3
|
+
include Common
|
4
4
|
|
5
5
|
# The Tunnel resource ID
|
6
6
|
attr_reader :id
|
@@ -8,60 +8,122 @@ module Forward
|
|
8
8
|
attr_reader :subdomain
|
9
9
|
# The CNAME for the Tunnel
|
10
10
|
attr_reader :cname
|
11
|
-
# The host
|
11
|
+
# The host to forward requests to
|
12
12
|
attr_reader :host
|
13
|
-
# The
|
14
|
-
attr_reader :vhost
|
15
|
-
# The hostport (local port)
|
16
|
-
attr_reader :hostport
|
17
|
-
# The remote port
|
13
|
+
# The port to forward requests to
|
18
14
|
attr_reader :port
|
15
|
+
# Authentication for tunnel
|
16
|
+
attr_reader :username
|
17
|
+
attr_reader :password
|
18
|
+
attr_reader :no_auth
|
19
|
+
# The public hostname/subdomain url for the tunnel
|
20
|
+
attr_reader :url
|
19
21
|
# The tunneler host
|
20
22
|
attr_reader :tunneler
|
21
23
|
# The timeout
|
22
24
|
attr_reader :timeout
|
23
|
-
|
24
|
-
|
25
|
+
|
26
|
+
attr_reader :socket
|
27
|
+
attr_reader :socket_url
|
28
|
+
attr_reader :requests
|
29
|
+
|
30
|
+
attr_accessor :last_active_at
|
25
31
|
|
26
32
|
# Initializes a Tunnel instance for the Client and requests a tunnel from
|
27
33
|
# API.
|
28
34
|
#
|
29
35
|
# client - The Client instance.
|
30
|
-
def initialize(
|
31
|
-
|
32
|
-
@
|
33
|
-
@id
|
34
|
-
@
|
35
|
-
@
|
36
|
-
@
|
37
|
-
@
|
38
|
-
@
|
39
|
-
@
|
40
|
-
@
|
41
|
-
@
|
36
|
+
def initialize(attributes = {})
|
37
|
+
logger.debug "[tunnel] initializing with: #{attributes.inspect}"
|
38
|
+
@attributes = attributes
|
39
|
+
@id = attributes[:id]
|
40
|
+
@host = attributes[:vhost]
|
41
|
+
@port = attributes[:hostport]
|
42
|
+
@subdomain_prefix = attributes[:subdomain_prefix]
|
43
|
+
@username = attributes[:username]
|
44
|
+
@password = attributes[:password]
|
45
|
+
@no_auth = attributes[:no_auth]
|
46
|
+
@static_path = attributes[:static_path]
|
47
|
+
@cname = attributes[:cname]
|
48
|
+
@timeout = attributes[:timeout]
|
49
|
+
@tunneler = attributes[:tunneler]
|
50
|
+
@url = attributes[:url]
|
51
|
+
@requests = {}
|
52
|
+
@open = false
|
53
|
+
@socket = Socket.new(self)
|
54
|
+
end
|
55
|
+
|
56
|
+
def ready!
|
57
|
+
logger.debug '[tunnel] opened successfully'
|
58
|
+
@open = true
|
59
|
+
|
60
|
+
copy_url_to_clipboard
|
61
|
+
open_url_in_browser
|
62
|
+
display_ready_message
|
63
|
+
end
|
64
|
+
|
65
|
+
def open?
|
66
|
+
@open
|
42
67
|
end
|
43
68
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
69
|
+
def destroy(&block)
|
70
|
+
API::Tunnel.destroy(id, &block)
|
71
|
+
end
|
72
|
+
|
73
|
+
def authority
|
74
|
+
@authority ||= begin
|
75
|
+
_authority = "#{host}"
|
76
|
+
_authority << ":#{port}" unless port == 80
|
77
|
+
|
78
|
+
_authority
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def track_activity!
|
83
|
+
self.last_active_at = Time.now.to_i
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def display_ready_message
|
89
|
+
source = static? ? "Directory `#{File.expand_path(@static_path).split('/').last}'" : authority
|
90
|
+
message = "#{source} is now available at: #{HighLine.color(url, :underline)}"
|
91
|
+
|
92
|
+
message << " and #{HighLine.color("http://#{cname}", :underline)}" if cname
|
93
|
+
|
94
|
+
puts "#{message}\n\nCtrl-C to stop forwarding"
|
95
|
+
end
|
96
|
+
|
97
|
+
def copy_url_to_clipboard
|
98
|
+
return unless config.auto_copy?
|
99
|
+
|
100
|
+
if windows?
|
101
|
+
begin
|
102
|
+
require 'ffi'
|
103
|
+
rescue LoadError
|
104
|
+
puts "The FFI gem is required to copy your url to the clipboard, you can install it with `gem install ffi'"
|
105
|
+
return
|
59
106
|
end
|
60
|
-
|
107
|
+
end
|
108
|
+
|
109
|
+
Clipboard.copy(url)
|
110
|
+
end
|
111
|
+
|
112
|
+
def open_url_in_browser
|
113
|
+
return unless config.auto_open?
|
114
|
+
|
115
|
+
case os
|
116
|
+
when :windows
|
117
|
+
%x[start #{url}]
|
118
|
+
when :osx
|
119
|
+
%x[open #{url}]
|
120
|
+
when :unix
|
121
|
+
%x[xdg-open #{url}]
|
122
|
+
end
|
61
123
|
end
|
62
|
-
|
63
|
-
def
|
64
|
-
|
124
|
+
|
125
|
+
def static?
|
126
|
+
!@static_path.nil?
|
65
127
|
end
|
66
128
|
|
67
129
|
end
|
data/lib/forward/version.rb
CHANGED
data/lib/forward.rb
CHANGED
@@ -1,108 +1,125 @@
|
|
1
1
|
require 'base64'
|
2
2
|
require 'json'
|
3
3
|
require 'logger'
|
4
|
-
require 'openssl'
|
5
|
-
require 'optparse'
|
6
4
|
require 'rbconfig'
|
7
|
-
require '
|
8
|
-
require '
|
5
|
+
require 'fileutils'
|
6
|
+
require 'yaml'
|
9
7
|
|
8
|
+
require 'slop'
|
10
9
|
require 'highline/import'
|
11
|
-
require '
|
10
|
+
require 'eventmachine'
|
11
|
+
require 'em-http-request'
|
12
|
+
require 'faye/websocket'
|
13
|
+
require 'clipboard'
|
12
14
|
|
13
15
|
require 'forward/core_extensions'
|
14
16
|
|
15
|
-
require 'forward/
|
17
|
+
require 'forward/common'
|
16
18
|
require 'forward/api'
|
17
19
|
require 'forward/config'
|
20
|
+
require 'forward/request'
|
21
|
+
require 'forward/socket'
|
18
22
|
require 'forward/tunnel'
|
19
|
-
require 'forward/client'
|
20
23
|
require 'forward/cli'
|
21
24
|
require 'forward/version'
|
22
25
|
|
23
26
|
module Forward
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
27
|
+
class Error < StandardError; end
|
28
|
+
# An error occurred with the CLI
|
29
|
+
class CLIError < Error; end
|
30
|
+
# An error occurred with the Client
|
31
|
+
class ClientError < Error; end
|
32
|
+
# An error occurred with the Config
|
33
|
+
class ConfigError < Error; end
|
34
|
+
# An error occurred with the Tunnel
|
35
|
+
class TunnelError < Error; end
|
36
|
+
|
37
|
+
SUPPORT_EMAIL = 'support@forwardhq.com'.freeze
|
38
|
+
|
39
|
+
# Helper for determining the host OS
|
29
40
|
#
|
30
|
-
# Returns
|
31
|
-
def self.
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
41
|
+
# Returns simplified and symbolized host os name
|
42
|
+
def self.os
|
43
|
+
@os ||= begin
|
44
|
+
case RbConfig::CONFIG['host_os']
|
45
|
+
when /mswin|mingw|cygwin/
|
46
|
+
:windows
|
47
|
+
when /darwin/
|
48
|
+
:osx
|
49
|
+
when /linux|bsd/
|
50
|
+
:unix
|
51
|
+
else
|
52
|
+
:unknown
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Helper to determine if host OS is windows
|
36
58
|
#
|
37
|
-
# Returns
|
38
|
-
def self.ssh_port
|
39
|
-
ENV['FORWARD_SSH_PORT'] || DEFAULT_SSH_PORT
|
40
|
-
end
|
41
|
-
|
59
|
+
# Returns Boolean
|
42
60
|
def self.windows?
|
43
|
-
|
61
|
+
@windows ||= os == :windows
|
44
62
|
end
|
45
63
|
|
46
|
-
|
47
|
-
|
64
|
+
# Setter for the current Client
|
65
|
+
#
|
66
|
+
# Returns the new Forawrd::Client instance
|
67
|
+
def self.tunnel=(tunnel)
|
68
|
+
@tunnel = tunnel
|
48
69
|
end
|
49
70
|
|
50
|
-
|
51
|
-
|
71
|
+
# Getter for the current Client
|
72
|
+
#
|
73
|
+
# Returns a Forward::Client instance
|
74
|
+
def self.tunnel
|
75
|
+
@tunnel
|
52
76
|
end
|
53
77
|
|
54
|
-
|
55
|
-
|
78
|
+
# Helper method for making forward quiet (silence most output)
|
79
|
+
def self.quiet!
|
80
|
+
@quiet = true
|
56
81
|
end
|
57
82
|
|
58
|
-
|
59
|
-
|
83
|
+
# Helper method for making forward quiet (silence most output)
|
84
|
+
def self.quiet?
|
85
|
+
@quiet == true
|
60
86
|
end
|
61
87
|
|
88
|
+
# Helper method for setting the log level to debug
|
62
89
|
def self.debug!
|
63
|
-
|
64
|
-
@debug = true
|
65
|
-
log.level = Logger::DEBUG
|
66
|
-
end
|
67
|
-
|
68
|
-
def self.debug?
|
69
|
-
@debug
|
90
|
+
logger.level = Logger::DEBUG
|
70
91
|
end
|
71
92
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
@logdev = stringio_log
|
78
|
-
@debug = true
|
79
|
-
@debug_remotely = true
|
80
|
-
log.level = Logger::DEBUG
|
81
|
-
end
|
82
|
-
|
83
|
-
def self.debug_remotely?
|
84
|
-
@debug_remotely ||= false
|
85
|
-
end
|
86
|
-
|
87
|
-
def self.logdev
|
88
|
-
@logdev ||= (windows? ? 'NUL:' : '/dev/null')
|
93
|
+
# Setter for the logger
|
94
|
+
#
|
95
|
+
# Returns the new Logger instance
|
96
|
+
def self.logger=(logger)
|
97
|
+
@logger ||= logger
|
89
98
|
end
|
90
99
|
|
100
|
+
# Getter for the logger
|
101
|
+
#
|
102
|
+
# Returns the new or cached Logger instance
|
91
103
|
def self.logger
|
92
|
-
@
|
93
|
-
|
104
|
+
@logger ||= begin
|
105
|
+
_logger = Logger.new(STDOUT)
|
106
|
+
_logger.level = Logger::WARN
|
107
|
+
_logger.formatter = proc do |severity, datetime, progname, msg|
|
108
|
+
"#{severity} - [#{datetime.strftime('%H:%M:%S')}] #{msg}\n"
|
109
|
+
end
|
94
110
|
|
95
|
-
|
96
|
-
|
111
|
+
_logger
|
112
|
+
end
|
97
113
|
end
|
98
114
|
|
99
|
-
# Returns a string representing a detailed client version
|
115
|
+
# Returns a string representing a detailed client version
|
100
116
|
#
|
101
|
-
# Returns a String representing the client
|
117
|
+
# Returns a String representing the client
|
102
118
|
def self.client_string
|
103
119
|
os = RbConfig::CONFIG['host_os']
|
104
120
|
engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
|
105
121
|
|
106
|
-
|
122
|
+
# TODO: make os version more friendly
|
123
|
+
"(#{engine}-#{RUBY_VERSION})(#{os})(v#{Forward::VERSION})"
|
107
124
|
end
|
108
125
|
end
|