web-console 2.0.0.beta2 → 2.0.0.beta3
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 +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
|