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.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.markdown +304 -19
  4. data/app/assets/javascripts/web_console/application.js +1 -0
  5. data/app/assets/javascripts/web_console/console_sessions.js +172 -0
  6. data/app/assets/stylesheets/web_console/application.css +13 -0
  7. data/app/assets/stylesheets/web_console/console_sessions.css.erb +6 -0
  8. data/app/controllers/web_console/application_controller.rb +13 -0
  9. data/app/controllers/web_console/console_sessions_controller.rb +43 -0
  10. data/app/helpers/web_console/application_helper.rb +4 -0
  11. data/app/helpers/web_console/console_session_helper.rb +4 -0
  12. data/app/models/web_console/console_session.rb +96 -0
  13. data/app/views/layouts/web_console/application.html.erb +14 -0
  14. data/app/views/web_console/console_sessions/index.html.erb +15 -0
  15. data/config/routes.rb +11 -0
  16. data/lib/action_dispatch/debug_exceptions.rb +69 -69
  17. data/lib/action_dispatch/exception_wrapper.rb +2 -1
  18. data/lib/assets/javascripts/web-console.js +1 -0
  19. data/lib/assets/javascripts/web_console.js +41 -0
  20. data/lib/web_console.rb +14 -15
  21. data/lib/web_console/colors.rb +87 -0
  22. data/lib/web_console/colors/light.rb +24 -0
  23. data/lib/web_console/colors/monokai.rb +24 -0
  24. data/lib/web_console/colors/solarized.rb +47 -0
  25. data/lib/web_console/colors/tango.rb +24 -0
  26. data/lib/web_console/colors/xterm.rb +24 -0
  27. data/lib/web_console/engine.rb +95 -0
  28. data/lib/web_console/exception_extension.rb +10 -11
  29. data/lib/web_console/repl_session.rb +5 -3
  30. data/lib/web_console/slave.rb +139 -0
  31. data/lib/web_console/version.rb +1 -1
  32. data/lib/web_console/view_helpers.rb +3 -7
  33. data/test/controllers/web_console/console_sessions_controller_test.rb +95 -0
  34. data/test/dummy/app/controllers/exception_test_controller.rb +4 -0
  35. data/test/dummy/app/views/exception_test/xhr.html.erb +1 -0
  36. data/test/dummy/config/application.rb +37 -1
  37. data/test/dummy/config/environments/test.rb +2 -0
  38. data/test/dummy/config/routes.rb +1 -0
  39. data/test/dummy/db/development.sqlite3 +0 -0
  40. data/test/dummy/log/development.log +61814 -0
  41. data/test/dummy/log/test.log +8621 -3
  42. data/test/dummy/tmp/cache/assets/development/sprockets/038461854af2e8bccdb29768efd4768f +0 -0
  43. data/test/dummy/tmp/cache/assets/development/sprockets/0ec396634a5f6808b026257fd107c355 +0 -0
  44. data/test/dummy/tmp/cache/assets/development/sprockets/127a54171eea8d294e4673599861787d +0 -0
  45. data/test/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  46. data/test/dummy/tmp/cache/assets/development/sprockets/17c571144b4e44da39bddb2d2c412414 +0 -0
  47. data/test/dummy/tmp/cache/assets/development/sprockets/1cb77d8cf661ccbc9de08f347c89b9f1 +0 -0
  48. data/test/dummy/tmp/cache/assets/development/sprockets/204edd12a29660722d4e0d8de9bd6652 +0 -0
  49. data/test/dummy/tmp/cache/assets/development/sprockets/2b96b037f3dfeccfe27113eb95b06ea1 +0 -0
  50. data/test/dummy/tmp/cache/assets/development/sprockets/2c853768baf811357d81d41bdfd05dcf +0 -0
  51. data/test/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  52. data/test/dummy/tmp/cache/assets/development/sprockets/314d48e543146f617c4d3439a4d8d40d +0 -0
  53. data/test/dummy/tmp/cache/assets/development/sprockets/34f21019a876722b8c24a6da4f0ef50b +0 -0
  54. data/test/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  55. data/test/dummy/tmp/cache/assets/development/sprockets/36341e42f23669574fa1027d0958ff3e +0 -0
  56. data/test/dummy/tmp/cache/assets/development/sprockets/44117154e909436e7eeaf10cdb18d2b4 +0 -0
  57. data/test/dummy/tmp/cache/assets/development/sprockets/496864a905d53afd8e176f29500f96a8 +0 -0
  58. data/test/dummy/tmp/cache/assets/development/sprockets/55b7b76605fdffe31d737d4ac1f1ef7b +0 -0
  59. data/test/dummy/tmp/cache/assets/development/sprockets/5ac98782fe3dfd0a766f75ce1801f0a0 +0 -0
  60. data/test/dummy/tmp/cache/assets/development/sprockets/6088d6f344b38303cc8028057d69e0f9 +0 -0
  61. data/test/dummy/tmp/cache/assets/development/sprockets/676dcf9b2d01b9dc7bd3183d8da88463 +0 -0
  62. data/test/dummy/tmp/cache/assets/development/sprockets/680381170dc160e358fc28076ea6886c +0 -0
  63. data/test/dummy/tmp/cache/assets/development/sprockets/6ad7acc9a22fe2a67ec24a1fc866c20e +0 -0
  64. data/test/dummy/tmp/cache/assets/development/sprockets/6bdb0d0c602e0e1bc304dc697e2cc6de +0 -0
  65. data/test/dummy/tmp/cache/assets/development/sprockets/6dc8d7aa69668fce85683aaad6615432 +0 -0
  66. data/test/dummy/tmp/cache/assets/development/sprockets/6e4d5b32cc444226f6597198994ccd5e +0 -0
  67. data/test/dummy/tmp/cache/assets/development/sprockets/74db0ca5cb8c8c347c9131a3ff516748 +0 -0
  68. data/test/dummy/tmp/cache/assets/development/sprockets/7999e525c88173c1beb785f002effc1d +0 -0
  69. data/test/dummy/tmp/cache/assets/development/sprockets/7a50a9e605754e99783de95715b976b0 +0 -0
  70. data/test/dummy/tmp/cache/assets/development/sprockets/806b0e33a2fe8e1245534345fa27c30a +0 -0
  71. data/test/dummy/tmp/cache/assets/development/sprockets/8aa4c7aabff23c8089d41e9e54193483 +0 -0
  72. data/test/dummy/tmp/cache/assets/development/sprockets/90396626cba6cbec37e32038e6c54e76 +0 -0
  73. data/test/dummy/tmp/cache/assets/development/sprockets/976b28910aa72c90a3b30c6e940f51df +0 -0
  74. data/test/dummy/tmp/cache/assets/development/sprockets/99e1bd7cbc437505bc8f07bc528c721c +0 -0
  75. data/test/dummy/tmp/cache/assets/development/sprockets/aaccf2c9ae2add0863c9a49e0042a097 +0 -0
  76. data/test/dummy/tmp/cache/assets/development/sprockets/ae4677d24a79d9411f2fced5011d5807 +0 -0
  77. data/test/dummy/tmp/cache/assets/development/sprockets/b2401118729720034b6f3eda0b4c5025 +0 -0
  78. data/test/dummy/tmp/cache/assets/development/sprockets/c649837df826fc310cb80f1adafd6b8d +0 -0
  79. data/test/dummy/tmp/cache/assets/development/sprockets/cac185d59612fae451a12df3fc21bb51 +0 -0
  80. data/test/dummy/tmp/cache/assets/development/sprockets/cb0065359d3b5b296f71d673f4b276e9 +0 -0
  81. data/test/dummy/tmp/cache/assets/development/sprockets/cee8c6b09c33d2b276753e959712724e +0 -0
  82. data/test/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  83. data/test/dummy/tmp/cache/assets/development/sprockets/d1f6e06bc2f112c4ec3a4c3f68351878 +0 -0
  84. data/test/dummy/tmp/cache/assets/development/sprockets/d20d83fd7ffa378b1b2b901786d640f3 +0 -0
  85. data/test/dummy/tmp/cache/assets/development/sprockets/d38c7c3aa1e72b55769ccb3607641ef4 +0 -0
  86. data/test/dummy/tmp/cache/assets/development/sprockets/d6b85d8b0b5c569388b89e56e9f6fed7 +0 -0
  87. data/test/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  88. data/test/dummy/tmp/cache/assets/development/sprockets/d982412def520c434e2240eae6d29cf2 +0 -0
  89. data/test/dummy/tmp/cache/assets/development/sprockets/df048a8b0897b9c04acdf59c8f95b18f +0 -0
  90. data/test/dummy/tmp/cache/assets/development/sprockets/df600f50f002512c95d93bcfbab891ed +0 -0
  91. data/test/dummy/tmp/cache/assets/development/sprockets/e6d6b8bde546349764be7b44ffcf5807 +0 -0
  92. data/test/dummy/tmp/cache/assets/development/sprockets/eb25265794d2f7afd1684779d84efdac +0 -0
  93. data/test/dummy/tmp/cache/assets/development/sprockets/ee8826b12b7d9bfd717df950b58f82ab +0 -0
  94. data/test/dummy/tmp/cache/assets/development/sprockets/ef9824789c6ed3483590e0564a12e1d1 +0 -0
  95. data/test/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  96. data/test/dummy/tmp/cache/assets/development/sprockets/fc7201c6cbef32453aa4175c520c8eae +0 -0
  97. data/test/dummy/tmp/cache/assets/test/sprockets/17c571144b4e44da39bddb2d2c412414 +0 -0
  98. data/test/dummy/tmp/cache/assets/test/sprockets/36341e42f23669574fa1027d0958ff3e +0 -0
  99. data/test/dummy/tmp/cache/assets/test/sprockets/55b7b76605fdffe31d737d4ac1f1ef7b +0 -0
  100. data/test/dummy/tmp/cache/assets/test/sprockets/5ac98782fe3dfd0a766f75ce1801f0a0 +0 -0
  101. data/test/dummy/tmp/cache/assets/test/sprockets/680381170dc160e358fc28076ea6886c +0 -0
  102. data/test/dummy/tmp/cache/assets/test/sprockets/6ad7acc9a22fe2a67ec24a1fc866c20e +0 -0
  103. data/test/dummy/tmp/cache/assets/test/sprockets/6e4d5b32cc444226f6597198994ccd5e +0 -0
  104. data/test/dummy/tmp/cache/assets/test/sprockets/7a50a9e605754e99783de95715b976b0 +0 -0
  105. data/test/dummy/tmp/cache/assets/test/sprockets/8aa4c7aabff23c8089d41e9e54193483 +0 -0
  106. data/test/dummy/tmp/cache/assets/test/sprockets/b2401118729720034b6f3eda0b4c5025 +0 -0
  107. data/test/dummy/tmp/cache/assets/test/sprockets/cb0065359d3b5b296f71d673f4b276e9 +0 -0
  108. data/test/dummy/tmp/cache/assets/test/sprockets/d1f6e06bc2f112c4ec3a4c3f68351878 +0 -0
  109. data/test/dummy/tmp/cache/assets/test/sprockets/d6b85d8b0b5c569388b89e56e9f6fed7 +0 -0
  110. data/test/dummy/tmp/cache/assets/test/sprockets/d982412def520c434e2240eae6d29cf2 +0 -0
  111. data/test/dummy/tmp/cache/assets/test/sprockets/df048a8b0897b9c04acdf59c8f95b18f +0 -0
  112. data/test/dummy/tmp/cache/assets/test/sprockets/e6d6b8bde546349764be7b44ffcf5807 +0 -0
  113. data/test/models/console_session_test.rb +58 -0
  114. data/test/web_console/colors_test.rb +58 -0
  115. data/test/web_console/engine_test.rb +136 -0
  116. data/test/web_console/slave_test.rb +71 -0
  117. data/vendor/assets/javascripts/term.js +5726 -0
  118. metadata +188 -7
  119. 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
- module WebConsole
8
- class << self
9
- attr_accessor :binding_of_caller_available
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
- alias_method :binding_of_caller_available?, :binding_of_caller_available
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
- begin
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
- if WebConsole.binding_of_caller_available?
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
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.new('Session unavailable')
20
+ INMEMORY_STORAGE[id.to_i] or raise NotFound, 'Session unavailable'
21
21
  end
22
22
 
23
- # Creates an already persisted consolse session.
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 binding
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