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.

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
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,6 @@
1
+ <% WebConsole.config.style.instance_eval do %>
2
+ body { color: <%= colors.foreground %>; background: <%= colors.background %>; margin: 0; padding: 0; }
3
+
4
+ .terminal { float: left; overflow: hidden; font: <%= font %>; }
5
+ .terminal-cursor { color: <%= colors.background %>; background: <%= colors.foreground %>; }
6
+ <% end %>
@@ -0,0 +1,13 @@
1
+ module WebConsole
2
+ class ApplicationController < ActionController::Base
3
+ before_action :prevent_unauthorized_requests!
4
+
5
+ private
6
+
7
+ def prevent_unauthorized_requests!
8
+ unless request.remote_ip.in?(WebConsole.config.whitelisted_ips)
9
+ render nothing: true, status: :unauthorized
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,43 @@
1
+ require_dependency 'web_console/application_controller'
2
+
3
+ module WebConsole
4
+ class ConsoleSessionsController < ApplicationController
5
+ rescue_from ConsoleSession::Unavailable do |exception|
6
+ render json: exception, status: :gone
7
+ end
8
+
9
+ rescue_from ConsoleSession::Invalid do |exception|
10
+ render json: exception, status: :unprocessable_entity
11
+ end
12
+
13
+ def index
14
+ @console_session = ConsoleSession.create
15
+ end
16
+
17
+ def input
18
+ @console_session = ConsoleSession.find(params[:id])
19
+ @console_session.send_input(console_session_params[:input])
20
+
21
+ render nothing: true
22
+ end
23
+
24
+ def configuration
25
+ @console_session = ConsoleSession.find(params[:id])
26
+ @console_session.configure(console_session_params)
27
+
28
+ render nothing: true
29
+ end
30
+
31
+ def pending_output
32
+ @console_session = ConsoleSession.find(params[:id])
33
+
34
+ render json: { output: @console_session.pending_output }
35
+ end
36
+
37
+ private
38
+
39
+ def console_session_params
40
+ params.permit(:id, :input, :width, :height)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,4 @@
1
+ module WebConsole
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module WebConsole
2
+ module ConsoleSessionHelper
3
+ end
4
+ end
@@ -0,0 +1,96 @@
1
+ module WebConsole
2
+ # Manage and persist (in memory) WebConsole::Slave instances.
3
+ class ConsoleSession
4
+ include ActiveModel::Model
5
+
6
+ # In-memory storage for the console sessions. Session preservation is
7
+ # troubled on servers with multiple workers and threads.
8
+ INMEMORY_STORAGE = {}
9
+
10
+ # Base error class for ConsoleSession specific exceptions.
11
+ #
12
+ # Provides #to_json implementation, so all subclasses are JSON
13
+ # serializable.
14
+ class Error < StandardError
15
+ def to_json(*)
16
+ { error: to_s }.to_json
17
+ end
18
+ end
19
+
20
+ # Raised when trying to find a session that is no longer in the in-memory
21
+ # session storage or when the slave process exited.
22
+ Unavailable = Class.new(Error)
23
+
24
+ # Raised when an operation transition to an invalid state.
25
+ Invalid = Class.new(Error)
26
+
27
+ class << self
28
+ # Finds a session by its pid.
29
+ #
30
+ # Raises WebConsole::ConsoleSession::Expired if there is no such session.
31
+ def find(pid)
32
+ INMEMORY_STORAGE[pid.to_i] or raise Unavailable, 'Session unavailable'
33
+ end
34
+
35
+ # Creates an already persisted consolse session.
36
+ #
37
+ # Use this method if you need to persist a session, without providing it
38
+ # any input.
39
+ def create
40
+ new.persist
41
+ end
42
+ end
43
+
44
+ def initialize
45
+ @slave = WebConsole::Slave.new
46
+ end
47
+
48
+ # Explicitly persist the model in the in-memory storage.
49
+ def persist
50
+ INMEMORY_STORAGE[pid] = self
51
+ end
52
+
53
+ # Returns true if the current session is persisted in the in-memory storage.
54
+ def persisted?
55
+ self == INMEMORY_STORAGE[pid]
56
+ end
57
+
58
+ # Returns an Enumerable of all key attributes if any is set, regardless if
59
+ # the object is persisted or not.
60
+ def to_key
61
+ [pid] if persisted?
62
+ end
63
+
64
+ private
65
+
66
+ def delegate_and_call_slave_method(name, *args, &block)
67
+ # Cache the delegated method, so we don't have to hit #method_missing
68
+ # on every call.
69
+ define_singleton_method(name) do |*inner_args, &inner_block|
70
+ begin
71
+ @slave.public_send(name, *inner_args, &inner_block)
72
+ rescue ArgumentError => exc
73
+ raise Invalid, exc
74
+ rescue Slave::Closed => exc
75
+ raise Unavailable, exc
76
+ end
77
+ end
78
+
79
+ # Now call the method, since that's our most common use case. Delegate
80
+ # the method and than call it.
81
+ public_send(name, *args, &block)
82
+ end
83
+
84
+ def method_missing(name, *args, &block)
85
+ if @slave.respond_to?(name)
86
+ delegate_and_call_slave_method(name, *args, &block)
87
+ else
88
+ super
89
+ end
90
+ end
91
+
92
+ def respond_to_missing?(name, include_all = false)
93
+ @slave.respond_to?(name) or super
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>WebConsole</title>
5
+ <%= stylesheet_link_tag "web_console/application", media: "all" %>
6
+ <%= javascript_include_tag "web_console/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,15 @@
1
+ <script>
2
+ var config = {
3
+ terminal: {
4
+ colors: <%= raw WebConsole.config.style.colors.to_json %>
5
+ },
6
+
7
+ transport: {
8
+ url: {
9
+ input: "<%= web_console.input_console_session_path(@console_session) %>",
10
+ pendingOutput: "<%= web_console.pending_output_console_session_path(@console_session) %>",
11
+ configuration: "<%= web_console.configuration_console_session_path(@console_session) %>"
12
+ }
13
+ }
14
+ };
15
+ </script>
data/config/routes.rb ADDED
@@ -0,0 +1,11 @@
1
+ WebConsole::Engine.routes.draw do
2
+ root to: 'console_sessions#index'
3
+
4
+ resources :console_sessions do
5
+ member do
6
+ put :input
7
+ get :pending_output
8
+ put :configuration
9
+ end
10
+ end
11
+ end
@@ -30,86 +30,86 @@ module ActionDispatch
30
30
 
31
31
  private
32
32
 
33
- def update_repl_session(id, input)
34
- console_session = WebConsole::REPLSession.find(id)
35
- response = console_session.save({ input: input })
36
- [200, { "Content-Type" => "text/plain; charset=utf-8" }, [response.to_json]]
37
- end
38
-
39
- def change_stack_trace(id, frame_id)
40
- console_session = WebConsole::REPLSession.find(id)
41
- binding = console_session.binding_stack[frame_id.to_i]
42
- console_session.binding = binding
43
- [200, { "Content-Type" => "text/plain; charset=utf-8" }, [JSON.dump("success")]]
44
- end
33
+ def update_repl_session(id, input)
34
+ console_session = WebConsole::REPLSession.find(id)
35
+ response = console_session.save(input: input)
36
+ [ 200, { "Content-Type" => "text/plain; charset=utf-8" }, [ response.to_json ] ]
37
+ end
45
38
 
46
- def render_exception(env, exception)
47
- if exception.respond_to?(:original_exception) && exception.original_exception
48
- exception = exception.original_exception
39
+ def change_stack_trace(id, frame_id)
40
+ console_session = WebConsole::REPLSession.find(id)
41
+ binding = console_session.binding_stack[frame_id.to_i]
42
+ console_session.binding = binding
43
+ [ 200, { "Content-Type" => "text/plain; charset=utf-8" }, [ JSON.dump("success") ] ]
49
44
  end
50
45
 
51
- wrapper = ExceptionWrapper.new(env, exception)
52
- traces = traces_from_wrapper(wrapper)
53
- extract_sources = wrapper.extract_sources
54
- console_session = WebConsole::REPLSession.create(
55
- binding: binding_from_exception(exception),
56
- binding_stack: exception.__web_console_bindings_stack
57
- )
58
- log_error(env, wrapper)
59
-
60
- if env['action_dispatch.show_detailed_exceptions']
61
- request = Request.new(env)
62
- template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
63
- request: request,
64
- exception: wrapper.exception,
65
- application_trace: traces[:application_trace],
66
- framework_trace: traces[:framework_trace],
67
- full_trace: traces[:full_trace],
68
- routes_inspector: routes_inspector(exception),
69
- extract_sources: extract_sources,
70
- console_session: console_session
71
- )
72
- file = "rescues/#{wrapper.rescue_template}"
46
+ def render_exception(env, exception)
47
+ if exception.respond_to?(:original_exception) && exception.original_exception
48
+ exception = exception.original_exception
49
+ end
73
50
 
74
- if request.xhr?
75
- body = template.render(template: file, layout: false, formats: [:text])
76
- format = "text/plain"
51
+ wrapper = ExceptionWrapper.new(env, exception)
52
+ traces = traces_from_wrapper(wrapper)
53
+ extract_sources = wrapper.extract_sources
54
+ console_session = WebConsole::REPLSession.create(
55
+ binding: binding_from_exception(exception),
56
+ binding_stack: exception.__web_console_bindings_stack
57
+ )
58
+ log_error(env, wrapper)
59
+
60
+ if env['action_dispatch.show_detailed_exceptions']
61
+ request = Request.new(env)
62
+ template = ActionView::Base.new([ RESCUES_TEMPLATE_PATH ],
63
+ request: request,
64
+ exception: wrapper.exception,
65
+ application_trace: traces[:application_trace],
66
+ framework_trace: traces[:framework_trace],
67
+ full_trace: traces[:full_trace],
68
+ routes_inspector: routes_inspector(exception),
69
+ extract_sources: extract_sources,
70
+ console_session: console_session
71
+ )
72
+ file = "rescues/#{wrapper.rescue_template}"
73
+
74
+ if request.xhr?
75
+ body = template.render(template: file, layout: false, formats: [ :text ])
76
+ format = "text/plain"
77
+ else
78
+ body = template.render(template: file, layout: 'rescues/layout')
79
+ format = "text/html"
80
+ end
81
+
82
+ [ wrapper.status_code, { 'Content-Type' => "#{format}; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s }, [ body ] ]
77
83
  else
78
- body = template.render(template: file, layout: 'rescues/layout')
79
- format = "text/html"
84
+ raise exception
80
85
  end
81
-
82
- [wrapper.status_code, {'Content-Type' => "#{format}; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
83
- else
84
- raise exception
85
86
  end
86
- end
87
87
 
88
- # Augment the exception traces by providing ids for all unique stack frame
89
- def traces_from_wrapper(wrapper)
90
- id_counter = 0
88
+ # Augment the exception traces by providing ids for all unique stack frames.
89
+ def traces_from_wrapper(wrapper)
90
+ id_counter = 0
91
91
 
92
- application_trace = wrapper.application_trace.map do |trace|
93
- prev = id_counter
94
- id_counter += 1
95
- { id: prev, trace: trace }
96
- end
92
+ application_trace = wrapper.application_trace.map do |trace|
93
+ prev = id_counter
94
+ id_counter += 1
95
+ { id: prev, trace: trace }
96
+ end
97
97
 
98
- framework_trace = wrapper.framework_trace.map do |trace|
99
- prev = id_counter
100
- id_counter += 1
101
- { id: prev, trace: trace }
102
- end
98
+ framework_trace = wrapper.framework_trace.map do |trace|
99
+ prev = id_counter
100
+ id_counter += 1
101
+ { id: prev, trace: trace }
102
+ end
103
103
 
104
- {
105
- application_trace: application_trace,
106
- framework_trace: framework_trace,
107
- full_trace: application_trace + framework_trace
108
- }
109
- end
104
+ {
105
+ application_trace: application_trace,
106
+ framework_trace: framework_trace,
107
+ full_trace: application_trace + framework_trace
108
+ }
109
+ end
110
110
 
111
- def binding_from_exception(exception)
112
- exception.__web_console_bindings_stack[0]
113
- end
111
+ def binding_from_exception(exception)
112
+ exception.__web_console_bindings_stack[0]
113
+ end
114
114
  end
115
115
  end
@@ -2,8 +2,9 @@ module ActionDispatch
2
2
  class ExceptionWrapper
3
3
  def extract_sources
4
4
  exception.backtrace.map do |trace|
5
- file, line = trace.split(":")
5
+ file, line = trace.split(":")
6
6
  line_number = line.to_i
7
+
7
8
  {
8
9
  code: source_fragment(file, line_number) || {},
9
10
  file: file,
@@ -0,0 +1 @@
1
+ //= require ./web_console
@@ -0,0 +1,41 @@
1
+ //= require term
2
+
3
+ ;(function(BaseTerminal) {
4
+
5
+ // Expose the main WebConsole namespace.
6
+ var WebConsole = this.WebConsole = {};
7
+
8
+ // Follow term.js example and expose inherits and EventEmitter.
9
+ var inherits = WebConsole.inherits = BaseTerminal.inherits;
10
+ var EventEmitter = WebConsole.EventEmitter = BaseTerminal.EventEmitter;
11
+
12
+ var Terminal = WebConsole.Terminal = function(options) {
13
+ if (typeof options === 'number') {
14
+ return BaseTerminal.apply(this, arguments);
15
+ }
16
+
17
+ BaseTerminal.call(this, options || (options = {}));
18
+
19
+ this.open();
20
+
21
+ if (!(options.rows || options.cols) || !options.geometry) {
22
+ this.fitScreen();
23
+ }
24
+ };
25
+
26
+ // Make WebConsole.Terminal inherit from BaseTerminal (term.js).
27
+ inherits(Terminal, BaseTerminal);
28
+
29
+ Terminal.prototype.fitScreen = function() {
30
+ var width = Math.floor(this.element.clientWidth / this.cols);
31
+ var height = Math.floor(this.element.clientHeight / this.rows);
32
+
33
+ var rows = Math.floor(window.innerHeight / height);
34
+ var cols = Math.floor(this.parent.clientWidth / width);
35
+
36
+ this.resize(cols, rows);
37
+
38
+ return [cols, rows];
39
+ };
40
+
41
+ }).call(this, Terminal);