From 7d1f547fe72178778e144c9855059d8754786e0d Mon Sep 17 00:00:00 2001 From: Hiroyuki Sano Date: Fri, 14 Aug 2015 11:55:52 +0900 Subject: [PATCH 1/4] Move WebConsole::Template::Context into WebConsole::View --- lib/web_console.rb | 1 + lib/web_console/template.rb | 31 ++------------------ lib/web_console/testing/fake_middleware.rb | 15 ++-------- lib/web_console/view.rb | 33 ++++++++++++++++++++++ 4 files changed, 38 insertions(+), 42 deletions(-) create mode 100644 lib/web_console/view.rb diff --git a/lib/web_console.rb b/lib/web_console.rb index 0d2b2fac..f15c6034 100644 --- a/lib/web_console.rb +++ b/lib/web_console.rb @@ -14,6 +14,7 @@ require 'web_console/whitelist' require 'web_console/request' require 'web_console/response' +require 'web_console/view' module WebConsole mattr_accessor :logger diff --git a/lib/web_console/template.rb b/lib/web_console/template.rb index 947d8125..8ff19b30 100644 --- a/lib/web_console/template.rb +++ b/lib/web_console/template.rb @@ -4,33 +4,6 @@ module WebConsole # It introduces template helpers to ease the inclusion of scripts only on # Rails error pages. class Template - class Context < ActionView::Base - # Execute a block only on error pages. - # - # The error pages are special, because they are the only pages that - # currently require multiple bindings. We get those from exceptions. - def only_on_error_page(*args) - yield if @env['web_console.exception'].present? - end - - # Render JavaScript inside a script tag and a closure. - # - # This one lets write JavaScript that will automatically get wrapped in a - # script tag and enclosed in a closure, so you don't have to worry for - # leaking globals, unless you explicitly want to. - def render_javascript(template) - render(template: template, layout: 'layouts/javascript') - end - - # Render inlined string to be used inside of JavaScript code. - # - # The inlined string is returned as an actual JavaScript string. You - # don't need to wrap the result yourself. - def render_inlined_string(template) - render(template: template, layout: 'layouts/inlined_string') - end - end - # Lets you customize the default templates folder location. cattr_accessor :template_paths @@template_paths = [ File.expand_path('../templates', __FILE__) ] @@ -43,8 +16,8 @@ def initialize(env, session) # Render a template (inferred from +template_paths+) as a plain string. def render(template) - context = Context.new(template_paths, instance_values) - context.render(template: template, layout: false) + view = View.new(template_paths, instance_values) + view.render(template: template, layout: false) end end end diff --git a/lib/web_console/testing/fake_middleware.rb b/lib/web_console/testing/fake_middleware.rb index b1a652f8..9f57ff2f 100644 --- a/lib/web_console/testing/fake_middleware.rb +++ b/lib/web_console/testing/fake_middleware.rb @@ -3,6 +3,7 @@ require 'json' require 'web_console/whitelist' require 'web_console/request' +require 'web_console/view' module WebConsole module Testing @@ -20,7 +21,7 @@ def call(env) end def view - @view ||= create_view + @view ||= View.new(@view_path) end private @@ -33,18 +34,6 @@ def req_path(env) def render(template) view.render(template: template, layout: nil) end - - def create_view - lookup_context = ActionView::LookupContext.new(@view_path) - lookup_context.cache = false - FakeView.new(lookup_context) - end - - class FakeView < ActionView::Base - def render_inlined_string(template) - render(template: template, layout: "layouts/inlined_string") - end - end end end end diff --git a/lib/web_console/view.rb b/lib/web_console/view.rb new file mode 100644 index 00000000..abc61632 --- /dev/null +++ b/lib/web_console/view.rb @@ -0,0 +1,33 @@ +module WebConsole + class View < ActionView::Base + # Execute a block only on error pages. + # + # The error pages are special, because they are the only pages that + # currently require multiple bindings. We get those from exceptions. + def only_on_error_page(*args) + yield if @env['web_console.exception'].present? + end + + # Render JavaScript inside a script tag and a closure. + # + # This one lets write JavaScript that will automatically get wrapped in a + # script tag and enclosed in a closure, so you don't have to worry for + # leaking globals, unless you explicitly want to. + def render_javascript(template) + render(template: template, layout: 'layouts/javascript') + end + + # Render inlined string to be used inside of JavaScript code. + # + # The inlined string is returned as an actual JavaScript string. You + # don't need to wrap the result yourself. + def render_inlined_string(template) + render(template: template, layout: 'layouts/inlined_string') + end + + # Escaped alias for "ActionView::Helpers::TranslationHelper.t". + def t(key, options = {}) + super.gsub("\n", "\\n") + end + end +end From 25889be25eec00c4e5cf9681600163cfd2a5f415 Mon Sep 17 00:00:00 2001 From: Hiroyuki Sano Date: Fri, 14 Aug 2015 12:42:24 +0900 Subject: [PATCH 2/4] Use I18n module for system messages instead of hard-coded value --- lib/web_console/locales/en.yml | 15 +++++++++++++++ lib/web_console/middleware.rb | 14 ++------------ lib/web_console/railtie.rb | 4 ++++ lib/web_console/testing/fake_middleware.rb | 1 + 4 files changed, 22 insertions(+), 12 deletions(-) create mode 100644 lib/web_console/locales/en.yml diff --git a/lib/web_console/locales/en.yml b/lib/web_console/locales/en.yml new file mode 100644 index 00000000..143d1f03 --- /dev/null +++ b/lib/web_console/locales/en.yml @@ -0,0 +1,15 @@ +en: + errors: + unavailable_session: | + Session %{id} is is no longer available in memory. + + If you happen to run on a multi-process server (like Unicorn or Puma) the process + this request hit doesn't store %{id} in memory. Consider turning the number of + processes/workers to one (1) or using a different server in development. + + unacceptable_request: | + A supported version is expected in the Accept header. + + connection_refused: | + Oops! Failed to connect to the Web Console middleware. + Please make sure a rails development server is running. diff --git a/lib/web_console/middleware.rb b/lib/web_console/middleware.rb index 2bf18462..197a8efa 100644 --- a/lib/web_console/middleware.rb +++ b/lib/web_console/middleware.rb @@ -4,16 +4,6 @@ module WebConsole class Middleware TEMPLATES_PATH = File.expand_path('../templates', __FILE__) - UNAVAILABLE_SESSION_MESSAGE = <<-END.strip_heredoc - Session %{id} is is no longer available in memory. - - If you happen to run on a multi-process server (like Unicorn or Puma) the process - this request hit doesn't store %{id} in memory. Consider turning the number of - processes/workers to one (1) or using a different server in development. - END - - UNACCEPTABLE_REQUEST_MESSAGE = "A supported version is expected in the Accept header." - cattr_accessor :mount_point @@mount_point = '/__web_console' @@ -115,13 +105,13 @@ def change_stack_trace(id, request) def respond_with_unavailable_session(id) json_response(status: 404) do - { output: format(UNAVAILABLE_SESSION_MESSAGE, id: id)} + { output: format(I18n.t('errors.unavailable_session'), id: id)} end end def respond_with_unacceptable_request json_response(status: 406) do - { error: UNACCEPTABLE_REQUEST_MESSAGE } + { output: I18n.t('errors.unacceptable_request') } end end end diff --git a/lib/web_console/railtie.rb b/lib/web_console/railtie.rb index dcc1aeac..2ba61391 100644 --- a/lib/web_console/railtie.rb +++ b/lib/web_console/railtie.rb @@ -66,5 +66,9 @@ class Railtie < ::Rails::Railtie Middleware.whiny_requests = config.web_console.whiny_requests end end + + initializer 'i18n.load_path' do + config.i18n.load_path.concat(Dir[File.expand_path('../locales/*.yml', __FILE__)]) + end end end diff --git a/lib/web_console/testing/fake_middleware.rb b/lib/web_console/testing/fake_middleware.rb index 9f57ff2f..fb51ed0f 100644 --- a/lib/web_console/testing/fake_middleware.rb +++ b/lib/web_console/testing/fake_middleware.rb @@ -1,5 +1,6 @@ require 'action_view' require 'action_dispatch' +require 'active_support/core_ext/string/access' require 'json' require 'web_console/whitelist' require 'web_console/request' From cd0cc0217d80d3c0a6b0744fcaa145cf7575ef2f Mon Sep 17 00:00:00 2001 From: Hiroyuki Sano Date: Mon, 10 Aug 2015 14:53:06 +0900 Subject: [PATCH 3/4] Show an error message when a response does not have a JSON text --- extensions/chrome/js/background.js | 12 +++++++++++- lib/web_console/templates/console.js.erb | 24 ++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/extensions/chrome/js/background.js b/extensions/chrome/js/background.js index f364bfef..96da3c3b 100644 --- a/extensions/chrome/js/background.js +++ b/extensions/chrome/js/background.js @@ -40,11 +40,21 @@ function initPanelMessage() { function initReqRes() { chrome.runtime.onMessage.addListener(handleMessage); + function extractProps(xhr) { + var props = {}; + for (var key in xhr) { + if (typeof xhr[key] === 'string' || typeof xhr[key] === 'number') { + props[key] = xhr[key]; + } + } + return props; + } + function handleMessage(req, sender, sendResponse) { if (req.type === 'request') { var url = tabInfo[req.tabId].remoteHost + '/' + req.url; REPLConsole.request(req.method, url, req.params, function(xhr) { - sendResponse({ status: xhr.status, responseText: xhr.responseText }); + sendResponse(extractProps(xhr)); }); } return true; diff --git a/lib/web_console/templates/console.js.erb b/lib/web_console/templates/console.js.erb index 5a961217..de55f2a7 100644 --- a/lib/web_console/templates/console.js.erb +++ b/lib/web_console/templates/console.js.erb @@ -80,13 +80,33 @@ REPLConsole.prototype.commandHandle = function(line, callback) { return status >= 200 && status < 300 || status === 304; } + function parseJSON(text) { + try { + return JSON.parse(text); + } catch (e) { + return null; + } + } + + function getErrorText(xhr) { + if (!xhr.status) { + return "<%= t 'errors.connection_refused' %>"; + } else { + return xhr.status + ' ' + xhr.statusText; + } + } + putRequest(self.getUrl(), params, function(xhr) { - var response = JSON.parse(xhr.responseText); + var response = parseJSON(xhr.responseText); var result = isSuccess(xhr.status); if (result) { self.writeOutput(response.output); } else { - self.writeError(response.output); + if (response && response.output) { + self.writeError(response.output); + } else { + self.writeError(getErrorText(xhr)); + } } callback(result, response); }); From 0f47ed9d32d8c912c981bc29df056f2bb545412b Mon Sep 17 00:00:00 2001 From: Hiroyuki Sano Date: Tue, 11 Aug 2015 12:05:38 +0900 Subject: [PATCH 4/4] Add test for the error response without JSON output --- test/templates/config.ru | 6 ++++++ test/templates/spec/repl_console_spec.js | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/test/templates/config.ru b/test/templates/config.ru index 7fcdcb4c..1a09f293 100644 --- a/test/templates/config.ru +++ b/test/templates/config.ru @@ -45,3 +45,9 @@ map "/mock/repl/error" do body = [ { output: 'fake-error-message' }.to_json ] run lambda { |env| [ 400, headers, body ] } end + +map "/mock/repl_sessions/error.txt" do + headers = { 'Content-Type' => 'plain/text' } + body = [ 'error message' ] + run lambda { |env| [ 400, headers, body ] } +end diff --git a/test/templates/spec/repl_console_spec.js b/test/templates/spec/repl_console_spec.js index 1ed7f56e..7047deb4 100644 --- a/test/templates/spec/repl_console_spec.js +++ b/test/templates/spec/repl_console_spec.js @@ -51,6 +51,21 @@ describe("REPLConsole", function() { assert.ok(hasClass(this.message, 'error-message')); }); }); + + context("remotePath: /mock/repl_sessions/error.txt", function() { + beforeEach(function(done) { + var self = this; + var options = { remotePath: '/mock/repl_sessions/error.txt' }; + self.console = REPLConsole.installInto('console', options); + self.console.commandHandle('fake-input', function(result, response) { + self.message = self.elm.getElementsByClassName('console-message')[0]; + done(); + }); + }); + it("should output HTTP status code", function() { + assert.match(this.message.innerHTML, /400 Bad Request/); + }); + }); }); describe(".installInto()", function() {