web-console 2.0.0.beta2 → 2.0.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of web-console might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.markdown +304 -19
- data/app/assets/javascripts/web_console/application.js +1 -0
- data/app/assets/javascripts/web_console/console_sessions.js +172 -0
- data/app/assets/stylesheets/web_console/application.css +13 -0
- data/app/assets/stylesheets/web_console/console_sessions.css.erb +6 -0
- data/app/controllers/web_console/application_controller.rb +13 -0
- data/app/controllers/web_console/console_sessions_controller.rb +43 -0
- data/app/helpers/web_console/application_helper.rb +4 -0
- data/app/helpers/web_console/console_session_helper.rb +4 -0
- data/app/models/web_console/console_session.rb +96 -0
- data/app/views/layouts/web_console/application.html.erb +14 -0
- data/app/views/web_console/console_sessions/index.html.erb +15 -0
- data/config/routes.rb +11 -0
- data/lib/action_dispatch/debug_exceptions.rb +69 -69
- data/lib/action_dispatch/exception_wrapper.rb +2 -1
- data/lib/assets/javascripts/web-console.js +1 -0
- data/lib/assets/javascripts/web_console.js +41 -0
- data/lib/web_console.rb +14 -15
- data/lib/web_console/colors.rb +87 -0
- data/lib/web_console/colors/light.rb +24 -0
- data/lib/web_console/colors/monokai.rb +24 -0
- data/lib/web_console/colors/solarized.rb +47 -0
- data/lib/web_console/colors/tango.rb +24 -0
- data/lib/web_console/colors/xterm.rb +24 -0
- data/lib/web_console/engine.rb +95 -0
- data/lib/web_console/exception_extension.rb +10 -11
- data/lib/web_console/repl_session.rb +5 -3
- data/lib/web_console/slave.rb +139 -0
- data/lib/web_console/version.rb +1 -1
- data/lib/web_console/view_helpers.rb +3 -7
- data/test/controllers/web_console/console_sessions_controller_test.rb +95 -0
- data/test/dummy/app/controllers/exception_test_controller.rb +4 -0
- data/test/dummy/app/views/exception_test/xhr.html.erb +1 -0
- data/test/dummy/config/application.rb +37 -1
- data/test/dummy/config/environments/test.rb +2 -0
- data/test/dummy/config/routes.rb +1 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/log/development.log +61814 -0
- data/test/dummy/log/test.log +8621 -3
- data/test/dummy/tmp/cache/assets/development/sprockets/038461854af2e8bccdb29768efd4768f +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/0ec396634a5f6808b026257fd107c355 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/127a54171eea8d294e4673599861787d +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/17c571144b4e44da39bddb2d2c412414 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/1cb77d8cf661ccbc9de08f347c89b9f1 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/204edd12a29660722d4e0d8de9bd6652 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/2b96b037f3dfeccfe27113eb95b06ea1 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/2c853768baf811357d81d41bdfd05dcf +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/314d48e543146f617c4d3439a4d8d40d +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/34f21019a876722b8c24a6da4f0ef50b +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/36341e42f23669574fa1027d0958ff3e +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/44117154e909436e7eeaf10cdb18d2b4 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/496864a905d53afd8e176f29500f96a8 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/55b7b76605fdffe31d737d4ac1f1ef7b +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/5ac98782fe3dfd0a766f75ce1801f0a0 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/6088d6f344b38303cc8028057d69e0f9 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/676dcf9b2d01b9dc7bd3183d8da88463 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/680381170dc160e358fc28076ea6886c +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/6ad7acc9a22fe2a67ec24a1fc866c20e +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/6bdb0d0c602e0e1bc304dc697e2cc6de +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/6dc8d7aa69668fce85683aaad6615432 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/6e4d5b32cc444226f6597198994ccd5e +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/74db0ca5cb8c8c347c9131a3ff516748 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/7999e525c88173c1beb785f002effc1d +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/7a50a9e605754e99783de95715b976b0 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/806b0e33a2fe8e1245534345fa27c30a +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/8aa4c7aabff23c8089d41e9e54193483 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/90396626cba6cbec37e32038e6c54e76 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/976b28910aa72c90a3b30c6e940f51df +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/99e1bd7cbc437505bc8f07bc528c721c +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/aaccf2c9ae2add0863c9a49e0042a097 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/ae4677d24a79d9411f2fced5011d5807 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/b2401118729720034b6f3eda0b4c5025 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/c649837df826fc310cb80f1adafd6b8d +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/cac185d59612fae451a12df3fc21bb51 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/cb0065359d3b5b296f71d673f4b276e9 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/cee8c6b09c33d2b276753e959712724e +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/d1f6e06bc2f112c4ec3a4c3f68351878 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/d20d83fd7ffa378b1b2b901786d640f3 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/d38c7c3aa1e72b55769ccb3607641ef4 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/d6b85d8b0b5c569388b89e56e9f6fed7 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/d982412def520c434e2240eae6d29cf2 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/df048a8b0897b9c04acdf59c8f95b18f +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/df600f50f002512c95d93bcfbab891ed +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/e6d6b8bde546349764be7b44ffcf5807 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/eb25265794d2f7afd1684779d84efdac +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/ee8826b12b7d9bfd717df950b58f82ab +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/ef9824789c6ed3483590e0564a12e1d1 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/fc7201c6cbef32453aa4175c520c8eae +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/17c571144b4e44da39bddb2d2c412414 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/36341e42f23669574fa1027d0958ff3e +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/55b7b76605fdffe31d737d4ac1f1ef7b +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/5ac98782fe3dfd0a766f75ce1801f0a0 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/680381170dc160e358fc28076ea6886c +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/6ad7acc9a22fe2a67ec24a1fc866c20e +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/6e4d5b32cc444226f6597198994ccd5e +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/7a50a9e605754e99783de95715b976b0 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/8aa4c7aabff23c8089d41e9e54193483 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/b2401118729720034b6f3eda0b4c5025 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/cb0065359d3b5b296f71d673f4b276e9 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/d1f6e06bc2f112c4ec3a4c3f68351878 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/d6b85d8b0b5c569388b89e56e9f6fed7 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/d982412def520c434e2240eae6d29cf2 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/df048a8b0897b9c04acdf59c8f95b18f +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/e6d6b8bde546349764be7b44ffcf5807 +0 -0
- data/test/models/console_session_test.rb +58 -0
- data/test/web_console/colors_test.rb +58 -0
- data/test/web_console/engine_test.rb +136 -0
- data/test/web_console/slave_test.rb +71 -0
- data/vendor/assets/javascripts/term.js +5726 -0
- metadata +188 -7
- data/lib/web_console/railtie.rb +0 -15
data/lib/web_console.rb
CHANGED
@@ -1,23 +1,22 @@
|
|
1
|
+
require 'binding_of_caller'
|
2
|
+
|
1
3
|
require 'active_support/lazy_load_hooks'
|
2
|
-
require 'web_console/repl'
|
3
|
-
require 'web_console/repl_session'
|
4
4
|
require 'action_dispatch/exception_wrapper'
|
5
5
|
require 'action_dispatch/debug_exceptions'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
require 'web_console/exception_extension'
|
8
|
+
require "web_console/view_helpers"
|
9
|
+
require 'web_console/colors'
|
10
|
+
require 'web_console/engine'
|
11
|
+
require 'web_console/repl'
|
12
|
+
require 'web_console/repl_session'
|
13
|
+
require 'web_console/slave'
|
10
14
|
|
11
|
-
|
15
|
+
module WebConsole
|
16
|
+
# Shortcut the +WebConsole::Engine.config.web_console+.
|
17
|
+
def self.config
|
18
|
+
Engine.config.web_console
|
12
19
|
end
|
13
|
-
end
|
14
20
|
|
15
|
-
|
16
|
-
require 'binding_of_caller'
|
17
|
-
WebConsole.binding_of_caller_available = true
|
18
|
-
rescue LoadError => e
|
19
|
-
WebConsole.binding_of_caller_available = false
|
21
|
+
ActiveSupport.run_load_hooks(:web_console, self)
|
20
22
|
end
|
21
|
-
|
22
|
-
require 'web_console/exception_extension'
|
23
|
-
require 'web_console/railtie'
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
2
|
+
|
3
|
+
module WebConsole
|
4
|
+
# = Colors
|
5
|
+
#
|
6
|
+
# Manages the creation and serialization of terminal color themes.
|
7
|
+
#
|
8
|
+
# Colors is a subclass of +Array+ and it stores a collection of CSS color
|
9
|
+
# values, to be used from the client-side terminal.
|
10
|
+
#
|
11
|
+
# You can specify 8 or 16 colors and additional +background+ and +foreground+
|
12
|
+
# colors. If not explicitly specified, +background+ and +foreground+ are
|
13
|
+
# considered to be the first and the last of the given colors.
|
14
|
+
class Colors < Array
|
15
|
+
class << self
|
16
|
+
# Registry of color themes mapped to a name.
|
17
|
+
#
|
18
|
+
# Don't manually alter the registry. Use WebConsole::Colors.register_theme
|
19
|
+
# for adding entries.
|
20
|
+
def themes
|
21
|
+
@@themes ||= {}.with_indifferent_access
|
22
|
+
end
|
23
|
+
|
24
|
+
# Register a color theme into the color themes registry.
|
25
|
+
#
|
26
|
+
# Registration maps a name and Colors instance.
|
27
|
+
#
|
28
|
+
# If a block is given, it would be yielded with a new Colors instance to
|
29
|
+
# populate the theme colors in.
|
30
|
+
#
|
31
|
+
# If a Colors instance is already instantiated it can be passed directly
|
32
|
+
# as the second (_colors_) argument. In this case, if a block is given,
|
33
|
+
# it won't be executed.
|
34
|
+
def register_theme(name, colors = nil)
|
35
|
+
themes[name] = colors || new.tap { |c| yield c }
|
36
|
+
end
|
37
|
+
|
38
|
+
# The default colors theme.
|
39
|
+
def default
|
40
|
+
self[:light]
|
41
|
+
end
|
42
|
+
|
43
|
+
# Shortcut for WebConsole::Colors.themes#[].
|
44
|
+
def [](name)
|
45
|
+
themes[name]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
alias :add :<<
|
50
|
+
|
51
|
+
# Background color getter and setter.
|
52
|
+
#
|
53
|
+
# If called without arguments it acts like a getter. Otherwise it acts like
|
54
|
+
# a setter.
|
55
|
+
#
|
56
|
+
# The default background color will be the first entry in the colors theme.
|
57
|
+
def background(value = nil)
|
58
|
+
@background = value unless value.nil?
|
59
|
+
@background ||= self.first
|
60
|
+
end
|
61
|
+
|
62
|
+
alias :background= :background
|
63
|
+
|
64
|
+
# Foreground color getter and setter.
|
65
|
+
#
|
66
|
+
# If called without arguments it acts like a getter. Otherwise it acts like
|
67
|
+
# a setter.
|
68
|
+
#
|
69
|
+
# The default foreground color will be the last entry in the colors theme.
|
70
|
+
def foreground(value = nil)
|
71
|
+
@foreground = value unless value.nil?
|
72
|
+
@foreground ||= self.last
|
73
|
+
end
|
74
|
+
|
75
|
+
alias :foreground= :foreground
|
76
|
+
|
77
|
+
def to_json
|
78
|
+
(dup << background << foreground).to_a.to_json
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
require 'web_console/colors/light'
|
84
|
+
require 'web_console/colors/monokai'
|
85
|
+
require 'web_console/colors/solarized'
|
86
|
+
require 'web_console/colors/tango'
|
87
|
+
require 'web_console/colors/xterm'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module WebConsole
|
2
|
+
Colors.register_theme(:light) do |c|
|
3
|
+
c.add '#000000'
|
4
|
+
c.add '#cd0000'
|
5
|
+
c.add '#00cd00'
|
6
|
+
c.add '#cdcd00'
|
7
|
+
c.add '#0000ee'
|
8
|
+
c.add '#cd00cd'
|
9
|
+
c.add '#00cdcd'
|
10
|
+
c.add '#e5e5e5'
|
11
|
+
|
12
|
+
c.add '#7f7f7f'
|
13
|
+
c.add '#ff0000'
|
14
|
+
c.add '#00ff00'
|
15
|
+
c.add '#ffff00'
|
16
|
+
c.add '#5c5cff'
|
17
|
+
c.add '#ff00ff'
|
18
|
+
c.add '#00ffff'
|
19
|
+
c.add '#ffffff'
|
20
|
+
|
21
|
+
c.background '#ffffff'
|
22
|
+
c.foreground '#000000'
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module WebConsole
|
2
|
+
Colors.register_theme(:monokai) do |c|
|
3
|
+
c.add '#1c1d19'
|
4
|
+
c.add '#d01b24'
|
5
|
+
c.add '#a7d32C'
|
6
|
+
c.add '#d8cf67'
|
7
|
+
c.add '#61b8d0'
|
8
|
+
c.add '#695abb'
|
9
|
+
c.add '#d53864'
|
10
|
+
c.add '#fefffe'
|
11
|
+
|
12
|
+
c.add '#1c1d19'
|
13
|
+
c.add '#d12a24'
|
14
|
+
c.add '#a7d32c'
|
15
|
+
c.add '#d8cf67'
|
16
|
+
c.add '#61b8d0'
|
17
|
+
c.add '#695abb'
|
18
|
+
c.add '#d53864'
|
19
|
+
c.add '#fefffe'
|
20
|
+
|
21
|
+
c.background '#1c1d19'
|
22
|
+
c.foreground '#fefffe'
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module WebConsole
|
2
|
+
Colors.register_theme(:solarized_dark) do |c|
|
3
|
+
c.add '#073642'
|
4
|
+
c.add '#dc322f'
|
5
|
+
c.add '#859900'
|
6
|
+
c.add '#b58900'
|
7
|
+
c.add '#268bd2'
|
8
|
+
c.add '#d33682'
|
9
|
+
c.add '#2aa198'
|
10
|
+
c.add '#eee8d5'
|
11
|
+
|
12
|
+
c.add '#002b36'
|
13
|
+
c.add '#cb4b16'
|
14
|
+
c.add '#586e75'
|
15
|
+
c.add '#657b83'
|
16
|
+
c.add '#839496'
|
17
|
+
c.add '#6c71c4'
|
18
|
+
c.add '#93a1a1'
|
19
|
+
c.add '#fdf6e3'
|
20
|
+
|
21
|
+
c.background '#002b36'
|
22
|
+
c.foreground '#657b83'
|
23
|
+
end
|
24
|
+
|
25
|
+
Colors.register_theme(:solarized_light) do |c|
|
26
|
+
c.add '#073642'
|
27
|
+
c.add '#dc322f'
|
28
|
+
c.add '#859900'
|
29
|
+
c.add '#b58900'
|
30
|
+
c.add '#268bd2'
|
31
|
+
c.add '#d33682'
|
32
|
+
c.add '#2aa198'
|
33
|
+
c.add '#eee8d5'
|
34
|
+
|
35
|
+
c.add '#002b36'
|
36
|
+
c.add '#cb4b16'
|
37
|
+
c.add '#586e75'
|
38
|
+
c.add '#657b83'
|
39
|
+
c.add '#839496'
|
40
|
+
c.add '#6c71c4'
|
41
|
+
c.add '#93a1a1'
|
42
|
+
c.add '#fdf6e3'
|
43
|
+
|
44
|
+
c.background '#fdf6e3'
|
45
|
+
c.foreground '#657b83'
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module WebConsole
|
2
|
+
Colors.register_theme(:tango) do |c|
|
3
|
+
c.add '#2e3436'
|
4
|
+
c.add '#cc0000'
|
5
|
+
c.add '#4e9a06'
|
6
|
+
c.add '#c4a000'
|
7
|
+
c.add '#3465a4'
|
8
|
+
c.add '#75507b'
|
9
|
+
c.add '#06989a'
|
10
|
+
c.add '#d3d7cf'
|
11
|
+
|
12
|
+
c.add '#555753'
|
13
|
+
c.add '#ef2929'
|
14
|
+
c.add '#8ae234'
|
15
|
+
c.add '#fce94f'
|
16
|
+
c.add '#729fcf'
|
17
|
+
c.add '#ad7fa8'
|
18
|
+
c.add '#34e2e2'
|
19
|
+
c.add '#eeeeec'
|
20
|
+
|
21
|
+
c.background '#2e3436'
|
22
|
+
c.foreground '#eeeeec'
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module WebConsole
|
2
|
+
Colors.register_theme(:xterm) do |c|
|
3
|
+
c.add '#000000'
|
4
|
+
c.add '#cd0000'
|
5
|
+
c.add '#00cd00'
|
6
|
+
c.add '#cdcd00'
|
7
|
+
c.add '#0000ee'
|
8
|
+
c.add '#cd00cd'
|
9
|
+
c.add '#00cdcd'
|
10
|
+
c.add '#e5e5e5'
|
11
|
+
|
12
|
+
c.add '#7f7f7f'
|
13
|
+
c.add '#ff0000'
|
14
|
+
c.add '#00ff00'
|
15
|
+
c.add '#ffff00'
|
16
|
+
c.add '#5c5cff'
|
17
|
+
c.add '#ff00ff'
|
18
|
+
c.add '#00ffff'
|
19
|
+
c.add '#ffffff'
|
20
|
+
|
21
|
+
c.background '#000000'
|
22
|
+
c.foreground '#ffffff'
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
require 'active_support/core_ext/numeric/time'
|
3
|
+
require 'rails/engine'
|
4
|
+
|
5
|
+
require 'active_model'
|
6
|
+
require 'sprockets/rails'
|
7
|
+
|
8
|
+
module WebConsole
|
9
|
+
class Engine < ::Rails::Engine
|
10
|
+
isolate_namespace WebConsole
|
11
|
+
|
12
|
+
config.web_console = ActiveSupport::OrderedOptions.new.tap do |c|
|
13
|
+
c.automount = false
|
14
|
+
c.command = nil
|
15
|
+
c.default_mount_path = '/console'
|
16
|
+
c.timeout = 0.seconds
|
17
|
+
c.term = 'xterm-color'
|
18
|
+
c.whitelisted_ips = '127.0.0.1'
|
19
|
+
|
20
|
+
c.style = ActiveSupport::OrderedOptions.new.tap do |s|
|
21
|
+
s.colors = 'light'
|
22
|
+
s.font = 'large DejaVu Sans Mono, Liberation Mono, monospace'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
initializer "web_console.initialize_view_helpers" do
|
27
|
+
ActiveSupport.on_load :action_view do
|
28
|
+
include WebConsole::ViewHelpers
|
29
|
+
end
|
30
|
+
|
31
|
+
ActiveSupport.on_load :action_controller do
|
32
|
+
prepend_view_path File.dirname(__FILE__) + '/../action_dispatch/templates'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
initializer 'web_console.add_default_route' do |app|
|
37
|
+
# While we don't need the route in the test environment, we define it
|
38
|
+
# there as well, so we can easily test it.
|
39
|
+
if config.web_console.automount && (Rails.env.development? || Rails.env.test?)
|
40
|
+
app.routes.append do
|
41
|
+
mount WebConsole::Engine => app.config.web_console.default_mount_path
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
initializer 'web_console.process_whitelisted_ips' do
|
47
|
+
config.web_console.tap do |c|
|
48
|
+
# Ensure that it is an array of IPAddr instances and it is defaulted to
|
49
|
+
# 127.0.0.1 if not precent. Only unique entries are left in the end.
|
50
|
+
c.whitelisted_ips = Array(c.whitelisted_ips).map { |ip|
|
51
|
+
if ip.is_a?(IPAddr)
|
52
|
+
ip
|
53
|
+
else
|
54
|
+
IPAddr.new(ip.presence || '127.0.0.1')
|
55
|
+
end
|
56
|
+
}.uniq
|
57
|
+
|
58
|
+
# IPAddr instances can cover whole networks, so simplify the #include?
|
59
|
+
# check for the most common case.
|
60
|
+
def (c.whitelisted_ips).include?(ip)
|
61
|
+
if ip.is_a?(IPAddr)
|
62
|
+
super
|
63
|
+
else
|
64
|
+
any? { |net| net.include?(ip.to_s) }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
initializer 'web_console.process_command' do
|
71
|
+
config.web_console.tap do |c|
|
72
|
+
# +Rails.root+ is not available while we set the default values of the
|
73
|
+
# other options. Default it during initialization.
|
74
|
+
|
75
|
+
# Not all people created their Rails 4 applications with the Rails 4
|
76
|
+
# generator, so bin/rails may not be available.
|
77
|
+
if c.command.blank?
|
78
|
+
local_rails = Rails.root.join('bin/rails')
|
79
|
+
c.command = "#{local_rails.executable? ? local_rails : 'rails'} console"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
initializer 'web_console.process_colors' do
|
85
|
+
config.web_console.style.tap do |c|
|
86
|
+
case colors = c.colors
|
87
|
+
when Symbol, String
|
88
|
+
c.colors = Colors[colors] || Colors.default
|
89
|
+
else
|
90
|
+
c.colors = Colors.new(colors)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -1,19 +1,18 @@
|
|
1
|
+
# Thanks to @charliesome who wrote this bit for better_errors.
|
1
2
|
class Exception
|
2
3
|
original_set_backtrace = instance_method(:set_backtrace)
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
Thread.current[:__web_console_exception_lock] = false
|
12
|
-
end
|
5
|
+
define_method :set_backtrace do |*args|
|
6
|
+
unless Thread.current[:__web_console_exception_lock]
|
7
|
+
Thread.current[:__web_console_exception_lock] = true
|
8
|
+
begin
|
9
|
+
@__web_console_bindings_stack = binding.callers.drop(1)
|
10
|
+
ensure
|
11
|
+
Thread.current[:__web_console_exception_lock] = false
|
13
12
|
end
|
14
|
-
|
15
|
-
original_set_backtrace.bind(self).call(*args)
|
16
13
|
end
|
14
|
+
|
15
|
+
original_set_backtrace.bind(self).call(*args)
|
17
16
|
end
|
18
17
|
|
19
18
|
def __web_console_bindings_stack
|
@@ -17,10 +17,10 @@ module WebConsole
|
|
17
17
|
class << self
|
18
18
|
# Finds a session by its id.
|
19
19
|
def find(id)
|
20
|
-
INMEMORY_STORAGE[id.to_i] or raise NotFound
|
20
|
+
INMEMORY_STORAGE[id.to_i] or raise NotFound, 'Session unavailable'
|
21
21
|
end
|
22
22
|
|
23
|
-
# Creates an already persisted
|
23
|
+
# Creates an already persisted console session.
|
24
24
|
#
|
25
25
|
# Use this method if you need to persist a session, without providing it
|
26
26
|
# any input.
|
@@ -37,7 +37,7 @@ module WebConsole
|
|
37
37
|
|
38
38
|
def binding=(binding)
|
39
39
|
@binding = binding
|
40
|
-
@repl = WebConsole::REPL.new
|
40
|
+
@repl = WebConsole::REPL.new(binding)
|
41
41
|
end
|
42
42
|
|
43
43
|
# Saves the model into the in-memory storage.
|
@@ -55,6 +55,7 @@ module WebConsole
|
|
55
55
|
end
|
56
56
|
|
57
57
|
protected
|
58
|
+
|
58
59
|
# Returns a hash of the attributes and their values.
|
59
60
|
def attributes
|
60
61
|
return Hash[ATTRIBUTES.zip([nil])]
|
@@ -69,6 +70,7 @@ module WebConsole
|
|
69
70
|
end
|
70
71
|
|
71
72
|
private
|
73
|
+
|
72
74
|
def ensure_consequential_id!
|
73
75
|
self.id = begin
|
74
76
|
@@counter ||= 0
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'pty'
|
2
|
+
require 'io/console'
|
3
|
+
|
4
|
+
module WebConsole
|
5
|
+
# = Slave\ Process\ Wrapper
|
6
|
+
#
|
7
|
+
# Creates and communicates with slave processes.
|
8
|
+
#
|
9
|
+
# The communication happens through an input with attached psuedo-terminal.
|
10
|
+
# All of the communication is done in asynchronous way, meaning that when you
|
11
|
+
# send input to the process, you have get the output by polling for it.
|
12
|
+
class Slave
|
13
|
+
# Different OS' and platforms raises different errors when trying to read
|
14
|
+
# on output end of a closed process.
|
15
|
+
READING_ON_CLOSED_END_ERRORS = [ Errno::EIO, EOFError ]
|
16
|
+
|
17
|
+
# Raised when trying to read from a closed (exited) process.
|
18
|
+
Closed = Class.new(IOError)
|
19
|
+
|
20
|
+
# The slave process id.
|
21
|
+
attr_reader :pid
|
22
|
+
|
23
|
+
def initialize(command = WebConsole.config.command, options = {})
|
24
|
+
using_term(options[:term] || WebConsole.config.term) do
|
25
|
+
@output, @input, @pid = PTY.spawn(command.to_s)
|
26
|
+
end
|
27
|
+
configure(options)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Configure the psuedo terminal properties.
|
31
|
+
#
|
32
|
+
# Options:
|
33
|
+
# :width The width of the terminal in number of columns.
|
34
|
+
# :height The height of the terminal in number of rows.
|
35
|
+
#
|
36
|
+
# If any of the width or height is missing (or zero), the terminal size
|
37
|
+
# won't be set.
|
38
|
+
def configure(options = {})
|
39
|
+
dimentions = options.values_at(:height, :width).collect(&:to_i)
|
40
|
+
@input.winsize = dimentions unless dimentions.any?(&:zero?)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Sends input to the slave process STDIN.
|
44
|
+
#
|
45
|
+
# Returns immediately.
|
46
|
+
def send_input(input)
|
47
|
+
raise ArgumentError if input.nil? or input.try(:empty?)
|
48
|
+
input.each_char { |char| @input.putc(char) }
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns whether the slave process has any pending output in +wait+
|
52
|
+
# seconds.
|
53
|
+
#
|
54
|
+
# By default, the +timeout+ follows +config.web_console.timeout+. Usually,
|
55
|
+
# it is zero, making the response immediate.
|
56
|
+
def pending_output?(timeout = WebConsole.config.timeout)
|
57
|
+
# JRuby's select won't automatically coerce ActiveSupport::Duration.
|
58
|
+
!!IO.select([@output], [], [], timeout.to_i)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Gets the pending output of the process.
|
62
|
+
#
|
63
|
+
# The pending output is read in an non blocking way by chunks, in the size
|
64
|
+
# of +chunk_len+. By default, +chunk_len+ is 49152 bytes.
|
65
|
+
#
|
66
|
+
# Returns +nil+, if there is no pending output at the moment. Otherwise,
|
67
|
+
# returns the output that hasn't been read since the last invocation.
|
68
|
+
#
|
69
|
+
# Raises Errno:EIO on closed output stream. This can happen if the
|
70
|
+
# underlying process exits.
|
71
|
+
def pending_output(chunk_len = 49152)
|
72
|
+
# Returns nil if there is no pending output.
|
73
|
+
return unless pending_output?
|
74
|
+
|
75
|
+
pending = String.new
|
76
|
+
while chunk = @output.read_nonblock(chunk_len)
|
77
|
+
pending << chunk
|
78
|
+
end
|
79
|
+
pending.force_encoding('UTF-8')
|
80
|
+
rescue IO::WaitReadable
|
81
|
+
pending.force_encoding('UTF-8')
|
82
|
+
rescue
|
83
|
+
raise Closed if READING_ON_CLOSED_END_ERRORS.any? { |exc| $!.is_a?(exc) }
|
84
|
+
end
|
85
|
+
|
86
|
+
# Dispose the underlying process, sending +SIGTERM+.
|
87
|
+
#
|
88
|
+
# After the process is disposed, it is detached from the parent to prevent
|
89
|
+
# zombies.
|
90
|
+
#
|
91
|
+
# If the process is already disposed an Errno::ESRCH will be raised and
|
92
|
+
# handled internally. If you want to handle Errno::ESRCH yourself, pass
|
93
|
+
# +{raise: true}+ as options.
|
94
|
+
#
|
95
|
+
# Returns a thread, which can be used to wait for the process termination.
|
96
|
+
def dispose(options = {})
|
97
|
+
dispose_with(:SIGTERM, options)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Dispose the underlying process, sending +SIGKILL+.
|
101
|
+
#
|
102
|
+
# After the process is disposed, it is detached from the parent to prevent
|
103
|
+
# zombies.
|
104
|
+
#
|
105
|
+
# If the process is already disposed an Errno::ESRCH will be raised and
|
106
|
+
# handled internally. If you want to handle Errno::ESRCH yourself, pass
|
107
|
+
# +{raise: true}+ as options.
|
108
|
+
#
|
109
|
+
# Returns a thread, which can be used to wait for the process termination.
|
110
|
+
def dispose!(options = {})
|
111
|
+
dispose_with(:SIGKILL, options)
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
LOCK = Mutex.new
|
117
|
+
|
118
|
+
def using_term(term)
|
119
|
+
if term.nil?
|
120
|
+
yield
|
121
|
+
else
|
122
|
+
LOCK.synchronize do
|
123
|
+
begin
|
124
|
+
(previous_term, ENV['TERM'] = ENV['TERM'], term) and yield
|
125
|
+
ensure
|
126
|
+
ENV['TERM'] = previous_term
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def dispose_with(signal, options = {})
|
133
|
+
Process.kill(signal, @pid)
|
134
|
+
Process.detach(@pid)
|
135
|
+
rescue Errno::ESRCH
|
136
|
+
raise if options[:raise]
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|