Hello community, here is the log from the commit of package rubygem-web-console for openSUSE:Factory checked in at 2016-03-17 16:36:00 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-web-console (Old) and /work/SRC/openSUSE:Factory/.rubygem-web-console.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-web-console" Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-web-console/rubygem-web-console.changes 2015-08-05 06:49:52.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.rubygem-web-console.new/rubygem-web-console.changes 2016-03-17 16:49:24.000000000 +0100 @@ -1,0 +2,20 @@ +Wed Mar 2 22:46:02 UTC 2016 - dims...@opensuse.org + +- Fix spec: this rubygem is only compatible with ruby 2.2. Add: + + %%define rb_build_versions ruby22 + + %%define rb_default_ruby_abi ruby:2.2.0 + +------------------------------------------------------------------- +Mon Jan 25 05:55:49 UTC 2016 - co...@suse.com + +- updated to version 3.0.0 + see installed CHANGELOG.markdown + + ## 3.0.0 + + * [#173](https://github.com/rails/web-console/pull/173) Revert "Change config.development_only default until 4.2.4 is released" ([@gsamokovarov]) + * [#171](https://github.com/rails/web-console/pull/171) Fixed blocked IP logging ([@gsamokovarov]) + * [#162](https://github.com/rails/web-console/pull/162) Render the console inside the body tag ([@gsamokovarov]) + * [#165](https://github.com/rails/web-console/pull/165) Revamped integrations for CRuby and Rubinius ([@gsamokovarov]) + +------------------------------------------------------------------- Old: ---- web-console-2.2.1.gem New: ---- web-console-3.0.0.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-web-console.spec ++++++ --- /var/tmp/diff_new_pack.rZPino/_old 2016-03-17 16:49:25.000000000 +0100 +++ /var/tmp/diff_new_pack.rZPino/_new 2016-03-17 16:49:25.000000000 +0100 @@ -1,7 +1,7 @@ # # spec file for package rubygem-web-console # -# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -24,13 +24,17 @@ # Name: rubygem-web-console -Version: 2.2.1 +Version: 3.0.0 Release: 0 %define mod_name web-console %define mod_full_name %{mod_name}-%{version} +# MANUAL +%define rb_build_versions ruby22 +%define rb_default_ruby_abi ruby:2.2.0 +# /MANUAL BuildRoot: %{_tmppath}/%{name}-%{version}-build +BuildRequires: %{ruby >= 2.2.2} BuildRequires: %{rubygem gem2rpm} -BuildRequires: %{ruby} BuildRequires: ruby-macros >= 5 Url: https://github.com/rails/web-console Source: http://rubygems.org/gems/%{mod_full_name}.gem @@ -48,7 +52,7 @@ %install %gem_install \ - --doc-files="CHANGELOG.markdown MIT-LICENSE README.markdown" \ + --doc-files="MIT-LICENSE" \ -f %gem_packages ++++++ gem2rpm.yml ++++++ --- /var/tmp/diff_new_pack.rZPino/_old 2016-03-17 16:49:25.000000000 +0100 +++ /var/tmp/diff_new_pack.rZPino/_new 2016-03-17 16:49:25.000000000 +0100 @@ -68,3 +68,6 @@ # :post: |- # /bin/echo foo # +:preamble: |- + %define rb_build_versions ruby22 + %define rb_default_ruby_abi ruby:2.2.0 ++++++ web-console-2.2.1.gem -> web-console-3.0.0.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CHANGELOG.markdown new/CHANGELOG.markdown --- old/CHANGELOG.markdown 2015-07-10 22:13:19.000000000 +0200 +++ new/CHANGELOG.markdown 2015-12-13 17:58:49.000000000 +0100 @@ -2,9 +2,16 @@ ## master (unreleased) +## 3.0.0 + +* [#173](https://github.com/rails/web-console/pull/173) Revert "Change config.development_only default until 4.2.4 is released" ([@gsamokovarov]) +* [#171](https://github.com/rails/web-console/pull/171) Fixed blocked IP logging ([@gsamokovarov]) +* [#162](https://github.com/rails/web-console/pull/162) Render the console inside the body tag ([@gsamokovarov]) +* [#165](https://github.com/rails/web-console/pull/165) Revamped integrations for CRuby and Rubinius ([@gsamokovarov]) + ## 2.2.1 -* [#150](https://github.com/rails/web-console/pull/150) Change config.development_only default until 4.2.4 is released. +* [#150](https://github.com/rails/web-console/pull/150) Change config.development_only default until 4.2.4 is released ([@gsamokovarov]) ## 2.2.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/README.markdown new/README.markdown --- old/README.markdown 2015-07-10 22:13:19.000000000 +0200 +++ new/README.markdown 2015-12-13 17:58:49.000000000 +0100 @@ -1,9 +1,6 @@ <p align=right> Documentation for: - <a href=https://github.com/rails/web-console/tree/v2.0.0>v2.0.0</a> - <a href=https://github.com/rails/web-console/tree/v2.1.0>v2.1.0</a> - <a href=https://github.com/rails/web-console/tree/v2.1.1>v2.1.1</a> - <a href=https://github.com/rails/web-console/tree/v2.1.2>v2.1.2</a> + <a href=https://github.com/rails/web-console/tree/v1.0.4>v1.0.4</a> <a href=https://github.com/rails/web-console/tree/v2.1.3>v2.1.3</a> </p> @@ -28,7 +25,7 @@ ```ruby group :development do - gem 'web-console', '~> 2.0' + gem 'web-console', '~> 3.0' end ``` @@ -37,35 +34,9 @@ ## Runtime -_Web Console_ uses [John Mair]'s [binding_of_caller] to spawn a console in a -specific binding. This comes at the price of limited Ruby runtime support. - ### CRuby -CRuby 1.9.2 and below is **not** supported. - -### JRuby - -JRuby needs to run in interpreted mode. You can enable it by: - -```bash -export JRUBY_OPTS=-J-Djruby.compile.mode=OFF - -# If you run JRuby 1.7.12 and above, you can use: -export JRUBY_OPTS=--dev -``` - -An unstable version of [binding_of_caller] is needed as the latest stable one -won't compile on _JRuby_. To install it, put the following in your application -`Gemfile`: - -```ruby -group :development do - gem 'binding_of_caller', '0.7.3.pre1' -end -``` - -Only _JRuby_ 1.7, is supported (no JRuby 9K support at the moment). +CRuby 2.2 and below is **not** supported. ### Rubinius @@ -75,7 +46,7 @@ The web console allows you to create an interactive Ruby session in your browser. Those sessions are launched automatically in case on an error, but -they can also be launched manually in in any page. +they can also be launched manually in any page. For example, calling `console` in a view will display a console in the current page in the context of the view binding. @@ -161,6 +132,18 @@ You may wanna check the [templates] folder at the source tree for the files you may override. +### config.web_console.mount_point + +Usually the middleware of _Web Console_ is mounted at `/__web_console`. +If you wanna change the path for some reasons, you can specify it +by `config.web_console.mount_point`: + +```ruby +Rails.application.configure do + config.web_console.mount_point = '/path/to/web_console' +end +``` + ## FAQ ### Where did /console go? @@ -203,17 +186,16 @@ ## Credits -* Shoutout to [Charlie Somerville] for [better_errors] and [this] code. -* Kudos to [John Mair] for [binding_of_caller]. +* Shoutout to [Charlie Somerville] for [better_errors]. +* Kudos to [John Mair] for [debug_inspector]. * Thanks to [Charles Oliver Nutter] for all the _JRuby_ feedback. -* Hugs and kisses to all of our [contributors]. +* Hugs and kisses to all of our [contributors]! [better_errors]: https://github.com/charliesome/better_errors -[binding_of_caller]: https://github.com/banister/binding_of_caller +[debug_inspector]: https://github.com/banister/debug_inspector [Charlie Somerville]: https://github.com/charliesome [John Mair]: https://github.com/banister [Charles Oliver Nutter]: https://github.com/headius [templates]: https://github.com/rails/web-console/tree/master/lib/web_console/templates -[this]: https://github.com/rails/web-console/blob/master/lib/web_console/integration/cruby.rb#L20-L32 [rvt]: https://github.com/gsamokovarov/rvt [contributors]: https://github.com/rails/web-console/graphs/contributors diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Rakefile new/Rakefile --- old/Rakefile 2015-07-10 22:13:19.000000000 +0200 +++ new/Rakefile 2015-12-13 17:58:49.000000000 +0100 @@ -8,6 +8,8 @@ require 'rake/testtask' require 'tmpdir' require 'securerandom' +require 'json' +require 'web_console/testing/erb_precompiler' EXPANDED_CWD = File.expand_path(File.dirname(__FILE__)) @@ -18,40 +20,7 @@ t.verbose = false end -namespace :test do - desc "Run tests for templates" - task :templates => "templates:all" - - namespace :templates do - task :all => [:daemonize, :npm, :rackup, :mocha, :kill] - task :serve => [:npm, :rackup] - - work_dir = Pathname(__FILE__).dirname.join("test/templates") - pid_file = Pathname(Dir.tmpdir).join("web_console.#{SecureRandom.uuid}.pid") - server_port = 29292 - rackup_opts = "-p #{server_port}" - - task :daemonize do - rackup_opts += " -D -P #{pid_file}" - end - - task :npm do - Dir.chdir(work_dir) { system "npm install --silent" } - end - - task :rackup do - Dir.chdir(work_dir) { system "bundle exec rackup #{rackup_opts}" } - end - - task :mocha do - Dir.chdir(work_dir) { system "$(npm bin)/mocha-phantomjs http://localhost:#{server_port}/html/spec_runner.html" } - end - - task :kill do - system "kill #{File.read pid_file}" - end - end -end +Dir['lib/web_console/tasks/**/*.rake'].each { |task| load task } Bundler::GemHelper.install_tasks Files old/checksums.yaml.gz and new/checksums.yaml.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/extensions.rb new/lib/web_console/extensions.rb --- old/lib/web_console/extensions.rb 2015-07-10 22:13:19.000000000 +0200 +++ new/lib/web_console/extensions.rb 2015-12-13 17:58:49.000000000 +0100 @@ -1,7 +1,11 @@ ActionDispatch::DebugExceptions.class_eval do - def render_exception_with_web_console(env, exception) - render_exception_without_web_console(env, exception).tap do - error = ActionDispatch::ExceptionWrapper.new(env, exception).exception + def render_exception_with_web_console(request, exception) + render_exception_without_web_console(request, exception).tap do + # Retain superficial Rails 4.2 compatibility. + env = Hash === request ? request : request.env + + backtrace_cleaner = env['action_dispatch.backtrace_cleaner'] + error = ActionDispatch::ExceptionWrapper.new(backtrace_cleaner, exception).exception # Get the original exception if ExceptionWrapper decides to follow it. env['web_console.exception'] = error diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/helper.rb new/lib/web_console/helper.rb --- old/lib/web_console/helper.rb 2015-07-10 22:13:19.000000000 +0200 +++ new/lib/web_console/helper.rb 2015-12-13 17:58:49.000000000 +0100 @@ -11,7 +11,7 @@ def console(binding = nil) raise DoubleRenderError if request.env['web_console.binding'] - request.env['web_console.binding'] = binding || ::Kernel.binding.of_caller(1) + request.env['web_console.binding'] = binding || ::WebConsole.caller_bindings.first # Make sure nothing is rendered from the view helper. Otherwise # you're gonna see unexpected #<Binding:0x007fee4302b078> in the diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/integration/cruby.rb new/lib/web_console/integration/cruby.rb --- old/lib/web_console/integration/cruby.rb 2015-07-10 22:13:19.000000000 +0200 +++ new/lib/web_console/integration/cruby.rb 2015-12-13 17:58:49.000000000 +0100 @@ -1,40 +1,23 @@ -class Exception - begin - # We share the same exception binding extraction mechanism as better_errors, - # so try to use it if it is already available. It also solves problems like - # charliesome/better_errors#272, caused by an infinite recursion. - require 'better_errors' +require 'debug_inspector' - # The bindings in which the exception originated in. - def bindings - @bindings || __better_errors_bindings_stack - end - rescue LoadError - # The bindings in which the exception originated in. - def bindings - @bindings || [] - end - - # CRuby calls #set_backtrace every time it raises an exception. Overriding - # it to assign the #bindings. - def set_backtrace_with_binding_of_caller(*args) - # Thanks to @charliesome who wrote this bit for better_errors. - unless Thread.current[:__web_console_exception_lock] - Thread.current[:__web_console_exception_lock] = true - begin - # Raising an exception here will cause all of the rubies to go into a - # stack overflow. Some rubies may even segfault. See - # https://bugs.ruby-lang.org/issues/10164 for details. - @bindings = binding.callers.drop(1) - ensure - Thread.current[:__web_console_exception_lock] = false - end - end +def WebConsole.caller_bindings + bindings = RubyVM::DebugInspector.open do |context| + context.backtrace_locations.each_index.map { |i| context.frame_binding(i) } + end - set_backtrace_without_binding_of_caller(*args) - end + # For C functions, we can't extract a binding. In this case, + # DebugInspector#frame_binding would have returned us nil. That's why we need + # to compact the bindings. + # + # Dropping two bindings, removes the current Ruby one in this exact method, + # and the one in the caller method. The caller method binding can be obtained + # by Kernel#binding, if needed. + bindings.compact.drop(2) +end - alias_method :set_backtrace_without_binding_of_caller, :set_backtrace - alias_method :set_backtrace, :set_backtrace_with_binding_of_caller +TracePoint.trace(:raise) do |context| + exc = context.raised_exception + if exc.bindings.empty? + exc.instance_variable_set(:@bindings, WebConsole.caller_bindings) end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/integration/jruby.rb new/lib/web_console/integration/jruby.rb --- old/lib/web_console/integration/jruby.rb 2015-07-10 22:13:19.000000000 +0200 +++ new/lib/web_console/integration/jruby.rb 1970-01-01 01:00:00.000000000 +0100 @@ -1,111 +0,0 @@ -require 'English' -require 'active_support/core_ext/string/strip' - -java_import org.jruby.RubyInstanceConfig - -module WebConsole - module JRuby - class << self - # Returns whether JRuby is ran in interpreted mode. - def interpreted_mode? - compile_mode = ::JRuby.runtime.instance_config.compile_mode - interpreted_mode = RubyInstanceConfig::CompileMode::OFF - - compile_mode == interpreted_mode - end - - # A proc to be used in Kernel#set_trace_func. - # - # It sets Exception#bindings for an error with all the bindings the - # current ThreadContext contains. - def set_exception_bindings_trace_func - proc do |event, file, line, id, binding, classname| - case event - when 'raise' - if $ERROR_INFO.bindings.empty? - # binding_of_caller will generate an improperly built binding at - # caller[1]. Every call to a non existent method, constant or a - # local variable will result in a Java NullPointerException. - # - # The binding that Kernel#set_trace_func is giving us is properly - # built, so we can use in place of the broken one. - bindings = ::Kernel.binding.callers.drop(2).unshift(binding) - $ERROR_INFO.instance_variable_set(:@bindings, bindings) - end - end - end - end - end - - # A fake binding for JRuby in non interpreted mode. - # - # It won't actually evaluate any code, rather it will tell the user how to - # enable interpreted mode. - class FakeJRubyBinding - TURN_ON_INTERPRETED_MODE_TEXT = <<-END.strip_heredoc - JRuby needs to run in interpreted mode for introspection support. - - To turn on interpreted mode, put -J-Djruby.compile.mode=OFF in the - JRUBY_OPTS environment variable. - END - - def TURN_ON_INTERPRETED_MODE_TEXT.inspect - self - end - - TURN_ON_INTERPRETED_MODE_TEXT.freeze - - def eval(*) - TURN_ON_INTERPRETED_MODE_TEXT - end - end - - # A fake array of FakeJRubyBinding objects. - # - # It is used in Exception#bindings to make sure that when users switch - # bindings on the UI, they get a FakeJRubyBinding notifying them what to do - # if they want introspection. - class FakeJRubyBindingsArray < Array - def [](*) - FakeJRubyBinding.new - end - - # For Kernel#Array and other implicit conversion API. JRuby expects it to - # return an object that is_a? an Array. This is the reasoning of - # FakeJRubyBindingsArray being a subclass of Array. - def to_ary - self - end - end - end -end - -if WebConsole::JRuby.interpreted_mode? - ::Exception.class_eval do - def bindings - @bindings || [] - end - end - - # Kernel#set_trace_func will complain about not being able to capture all the - # events without the JRuby --debug flag. - silence_warnings do - set_trace_func WebConsole::JRuby.set_exception_bindings_trace_func - end -else - ::Exception.class_eval do - def bindings - WebConsole::JRuby::FakeJRubyBindingsArray.new - end - end - - ::Binding.class_eval do - def of_caller(*) - WebConsole::JRuby::FakeJRubyBinding.new - end - - def callers - WebConsole::JRuby::FakeJRubyBindingsArray.new - end - end -end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/integration/rubinius.rb new/lib/web_console/integration/rubinius.rb --- old/lib/web_console/integration/rubinius.rb 2015-07-10 22:13:19.000000000 +0200 +++ new/lib/web_console/integration/rubinius.rb 2015-12-13 17:58:49.000000000 +0100 @@ -1,62 +1,34 @@ -module WebConsole - module Rubinius - # Filters internal Rubinius locations. - # - # There are a couple of reasons why we wanna filter out the locations. - # - # * ::Kernel.raise, is implemented in Ruby for Rubinius. We don't wanna - # have the frame for it to align with the CRuby and JRuby implementations. - # - # * For internal methods location variables can be nil. We can't create a - # bindings for them. - # - # * Bindings from the current file are considered internal and ignored. - # - # We do that all that so we can align the bindings with the backtraces - # entries. - class InternalLocationFilter - def initialize(locations) - @locations = locations - end +def WebConsole.caller_bindings + locations = ::Rubinius::VM.backtrace(1, true) - def filter - @locations.reject do |location| - location.file.start_with?('kernel/delta/kernel.rb') || - location.file == __FILE__ || - location.variables.nil? - end - end - end - - # Gets the current bindings for all available Ruby frames. - # - # Filters the internal Rubinius and WebConsole frames. - def self.current_bindings - locations = ::Rubinius::VM.backtrace(1, true) - - InternalLocationFilter.new(locations).filter.map do |location| - Binding.setup( - location.variables, - location.variables.method, - location.constant_scope, - location.variables.self, - location - ) - end - end + # Kernel.raise, is implemented in Ruby for Rubinius. We don't wanna have + # the frame for it to align with the CRuby and JRuby implementations. + # + # For internal methods location variables can be nil. We can't create a + # bindings for them. + locations.reject! do |location| + location.file.start_with?('kernel/delta/kernel.rb') || location.variables.nil? end -end -::Exception.class_eval do - def bindings - @bindings || [] + bindings = locations.map do |location| + Binding.setup( + location.variables, + location.variables.method, + location.constant_scope, + location.variables.self, + location + ) end + + # Drop the binding of the direct caller. That one can be created by + # Kernel#binding. + bindings.drop(1) end ::Rubinius.singleton_class.class_eval do def raise_exception_with_current_bindings(exc) if exc.bindings.empty? - exc.instance_variable_set(:@bindings, WebConsole::Rubinius.current_bindings) + exc.instance_variable_set(:@bindings, WebConsole.caller_bindings) end raise_exception_without_current_bindings(exc) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/integration.rb new/lib/web_console/integration.rb --- old/lib/web_console/integration.rb 2015-07-10 22:13:19.000000000 +0200 +++ new/lib/web_console/integration.rb 2015-12-13 17:58:49.000000000 +0100 @@ -1,8 +1,31 @@ +module WebConsole + # Returns the Ruby bindings of Kernel#callers locations. + # + # The list of bindings here doesn't map 1 to 1 with Kernel#callers, as we + # can't build Ruby bindings for C functions or the equivalent native + # implementations in JRuby and Rubinius. + # + # This method needs to be overridden by every integration. + def self.caller_bindings + raise NotImplementedError + end +end + +class Exception + # Returns an array of the exception backtrace locations bindings. + # + # The list won't map to the traces in #backtrace 1 to 1, because we can't + # build bindings for every trace (C functions, for example). + # + # Every integration should the instance variable. + def bindings + @bindings || [] + end +end + case RUBY_ENGINE when 'rbx' require 'web_console/integration/rubinius' -when 'jruby' - require 'web_console/integration/jruby' when 'ruby' require 'web_console/integration/cruby' end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/locales/en.yml new/lib/web_console/locales/en.yml --- old/lib/web_console/locales/en.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/lib/web_console/locales/en.yml 2015-12-13 17:58:49.000000000 +0100 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/middleware.rb new/lib/web_console/middleware.rb --- old/lib/web_console/middleware.rb 2015-07-10 22:13:19.000000000 +0200 +++ new/lib/web_console/middleware.rb 2015-12-13 17:58:49.000000000 +0100 @@ -4,31 +4,19 @@ class Middleware TEMPLATES_PATH = File.expand_path('../templates', __FILE__) - DEFAULT_OPTIONS = { - update_re: %r{/repl_sessions/(?<id>.+?)\z}, - binding_change_re: %r{/repl_sessions/(?<id>.+?)/trace\z} - } - - 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) the process - this request hit doesn't store %{id} in memory. - END - - UNACCEPTABLE_REQUEST_MESSAGE = "A supported version is expected in the Accept header." + cattr_accessor :mount_point + @@mount_point = '/__web_console' cattr_accessor :whiny_requests @@whiny_requests = true - def initialize(app, options = {}) - @app = app - @options = DEFAULT_OPTIONS.merge(options) + def initialize(app) + @app = app end def call(env) request = create_regular_or_whiny_request(env) - return @app.call(env) unless request.from_whitelited_ip? + return @app.call(env) unless request.from_whitelisted_ip? if id = id_for_repl_session_update(request) return update_repl_session(id, request) @@ -44,11 +32,12 @@ session = Session.from_binding(binding) end - if session && request.acceptable_content_type? - headers["X-Web-Console-Session-Id"] = session.id - response = Rack::Response.new(body, status, headers) + if session && acceptable_content_type?(headers) + response = Response.new(body, status, headers) template = Template.new(env, session) + response.headers["X-Web-Console-Session-Id"] = session.id + response.headers["X-Web-Console-Mount-Point"] = mount_point response.write(template.render('index')) response.finish else @@ -58,6 +47,10 @@ private + def acceptable_content_type?(headers) + Mime::Type.parse(headers['Content-Type']).first == Mime[:html] + end + def json_response(opts = {}) status = opts.fetch(:status, 200) headers = { 'Content-Type' => 'application/json; charset = utf-8' } @@ -67,17 +60,10 @@ end def json_response_with_session(id, request, opts = {}) - json_response(opts) do - unless request.acceptable? - return respond_with_unacceptable_request - end - - unless session = Session.find(id) - return respond_with_unavailable_session(id) - end + return respond_with_unacceptable_request unless request.acceptable? + return respond_with_unavailable_session(id) unless session = Session.find(id) - yield session - end + json_response(opts) { yield session } end def create_regular_or_whiny_request(env) @@ -85,23 +71,27 @@ whiny_requests ? WhinyRequest.new(request) : request end + def repl_sessions_re + @_repl_sessions_re ||= %r{#{mount_point}/repl_sessions/(?<id>[^/]+)} + end + def update_re - @options[:update_re] + @_update_re ||= %r{#{repl_sessions_re}\z} end def binding_change_re - @options[:binding_change_re] + @_binding_change_re ||= %r{#{repl_sessions_re}/trace\z} end def id_for_repl_session_update(request) if request.xhr? && request.put? - update_re.match(request.path_info) { |m| m[:id] } + update_re.match(request.path) { |m| m[:id] } end end def id_for_repl_session_stack_frame_change(request) if request.xhr? && request.post? - binding_change_re.match(request.path_info) { |m| m[:id] } + binding_change_re.match(request.path) { |m| m[:id] } end end @@ -121,13 +111,13 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/railtie.rb new/lib/web_console/railtie.rb --- old/lib/web_console/railtie.rb 2015-07-10 22:13:19.000000000 +0200 +++ new/lib/web_console/railtie.rb 2015-12-13 17:58:49.000000000 +0100 @@ -5,10 +5,6 @@ config.web_console = ActiveSupport::OrderedOptions.new config.web_console.whitelisted_ips = %w( 127.0.0.1 ::1 ) - # See rails/web-console#150 and rails/rails#20319. Revert when Ruby on - # Rails 4.2.4 is released. - config.web_console.development_only = false - initializer 'web_console.initialize' do require 'web_console/extensions' @@ -24,7 +20,7 @@ initializer 'web_console.development_only' do unless (config.web_console.development_only == false) || Rails.env.development? abort <<-END.strip_heredoc - Web Console is activated in the #{Rails.env} environment, which is + Web Console is activated in the #{Rails.env} environment. This is usually a mistake. To ensure it's only activated in development mode, move it to the development group of your Gemfile: @@ -43,6 +39,12 @@ app.middleware.insert_before ActionDispatch::DebugExceptions, Middleware end + initializer 'web_console.mount_point' do + if mount_point = config.web_console.mount_point + Middleware.mount_point = mount_point.chomp('/') + end + end + initializer 'web_console.template_paths' do if template_paths = config.web_console.template_paths Template.template_paths.unshift(*Array(template_paths)) @@ -61,12 +63,8 @@ end end - # Leave this undocumented so we treat such content type misses as bugs, - # while still being able to help the affected users in the meantime. - initializer 'web_console.acceptable_content_types' do - if acceptable_content_types = config.web_console.acceptable_content_types - Request.acceptable_content_types.concat(Array(acceptable_content_types)) - end + initializer 'i18n.load_path' do + config.i18n.load_path.concat(Dir[File.expand_path('../locales/*.yml', __FILE__)]) end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/request.rb new/lib/web_console/request.rb --- old/lib/web_console/request.rb 2015-07-10 22:13:19.000000000 +0200 +++ new/lib/web_console/request.rb 2015-12-13 17:58:49.000000000 +0100 @@ -1,57 +1,50 @@ module WebConsole # Web Console tailored request object. class Request < ActionDispatch::Request - # While most of the servers will return blank content type if none given, - # Puma will return text/plain. - cattr_accessor :acceptable_content_types - @@acceptable_content_types = [Mime::HTML, Mime::TEXT, Mime::URL_ENCODED_FORM] - # Configurable set of whitelisted networks. cattr_accessor :whitelisted_ips @@whitelisted_ips = Whitelist.new - # Define a vendor MIME type. We can call it using Mime::WEB_CONSOLE_V2 constant. + # Define a vendor MIME type. We can call it using Mime[:web_console_v2]. Mime::Type.register 'application/vnd.web-console.v2', :web_console_v2 # Returns whether a request came from a whitelisted IP. # # For a request to hit Web Console features, it needs to come from a white # listed IP. - def from_whitelited_ip? + def from_whitelisted_ip? whitelisted_ips.include?(strict_remote_ip) end # Determines the remote IP using our much stricter whitelist. def strict_remote_ip - GetSecureIp.new(env, whitelisted_ips).to_s - end - - # Returns whether the request is from an acceptable content type. - # - # We can render a console for HTML and TEXT by default. If a client didn't - # specified any content type and the server returned it as blank, we'll - # render it as well. - def acceptable_content_type? - content_type.blank? || content_type.in?(acceptable_content_types) + GetSecureIp.new(self, whitelisted_ips).to_s end # Returns whether the request is acceptable. def acceptable? - xhr? && accepts.any? { |mime| Mime::WEB_CONSOLE_V2 == mime } + xhr? && accepts.any? { |mime| Mime[:web_console_v2] == mime } end - class GetSecureIp < ActionDispatch::RemoteIp::GetIp - def initialize(env, proxies) - @env = env - @check_ip = true - @proxies = proxies - end + private + + class GetSecureIp < ActionDispatch::RemoteIp::GetIp + def initialize(req, proxies) + # After rails/rails@07b2ff0 ActionDispatch::RemoteIp::GetIp initializes + # with a ActionDispatch::Request object instead of plain Rack + # environment hash. Keep both @req and @env here, so we don't if/else + # on Rails versions. + @req = req + @env = req.env + @check_ip = true + @proxies = proxies + end - def filter_proxies(ips) - ips.reject do |ip| - @proxies.include?(ip) + def filter_proxies(ips) + ips.reject do |ip| + @proxies.include?(ip) + end end end - end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/response.rb new/lib/web_console/response.rb --- old/lib/web_console/response.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/lib/web_console/response.rb 2015-12-13 17:58:49.000000000 +0100 @@ -0,0 +1,23 @@ +module WebConsole + # A response object that writes content before the closing </body> tag, if + # possible. + # + # The object quacks like Rack::Response. + class Response < Struct.new(:body, :status, :headers) + def write(content) + raw_body = Array(body).first.to_s + + if position = raw_body.rindex('</body>') + raw_body.insert(position, content) + else + raw_body << content + end + + self.body = raw_body + end + + def finish + Rack::Response.new(body, status, headers).finish + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/tasks/extensions.rake new/lib/web_console/tasks/extensions.rake --- old/lib/web_console/tasks/extensions.rake 1970-01-01 01:00:00.000000000 +0100 +++ new/lib/web_console/tasks/extensions.rake 2015-12-13 17:58:49.000000000 +0100 @@ -0,0 +1,60 @@ +namespace :ext do + rootdir = Pathname('extensions') + + desc 'Build Chrome Extension' + task chrome: 'chrome:build' + + namespace :chrome do + dist = Pathname('dist/crx') + extdir = rootdir.join(dist) + manifest_json = rootdir.join('chrome/manifest.json') + + directory extdir + + task build: [ extdir, 'lib:templates' ] do + cd rootdir do + cp_r [ 'img/', 'tmp/lib/' ], dist + `cd chrome && git ls-files`.split("\n").each do |src| + dest = dist.join(src) + mkdir_p dest.dirname + cp Pathname('chrome').join(src), dest + end + end + end + + # Generate a .crx file. + task crx: [ :build, :npm ] do + out = "crx-web-console-#{JSON.parse(File.read(manifest_json))["version"]}.crx" + cd(extdir) { sh "node \"$(npm bin)/crx\" pack ./ -p ../crx-web-console.pem -o ../#{out}" } + end + + # Generate a .zip file for Chrome Web Store. + task zip: [ :build ] do + version = JSON.parse(File.read(manifest_json))["version"] + cd(extdir) { sh "zip -r ../crx-web-console-#{version}.zip ./" } + end + + desc 'Launch a browser with the chrome extension.' + task run: [ :build ] do + cd(rootdir) { sh "sh ./script/run_chrome.sh --load-extension=#{dist}" } + end + end + + task :npm do + cd(rootdir) { sh "npm install --silent" } + end + + namespace :lib do + templates = Pathname('lib/web_console/templates') + tmplib = rootdir.join('tmp/lib/') + js_erb = FileList.new(templates.join('**/*.js.erb')) + dirs = js_erb.pathmap("%{^#{templates},#{tmplib}}d") + + task templates: dirs + js_erb.pathmap("%{^#{templates},#{tmplib}}X") + + dirs.each { |d| directory d } + rule '.js' => [ "%{^#{tmplib},#{templates}}X.js.erb" ] do |t| + File.write(t.name, WebConsole::Testing::ERBPrecompiler.new(t.source).build) + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/tasks/test_templates.rake new/lib/web_console/tasks/test_templates.rake --- old/lib/web_console/tasks/test_templates.rake 1970-01-01 01:00:00.000000000 +0100 +++ new/lib/web_console/tasks/test_templates.rake 2015-12-13 17:58:49.000000000 +0100 @@ -0,0 +1,50 @@ +namespace :test do + desc "Run tests for templates" + task templates: "templates:all" + + namespace :templates do + task all: [ :daemonize, :npm, :rackup, :wait, :mocha, :kill, :exit ] + task serve: [ :npm, :rackup ] + + work_dir = Pathname(EXPANDED_CWD).join("test/templates") + pid_file = Pathname(Dir.tmpdir).join("web_console.#{SecureRandom.uuid}.pid") + runner_uri = URI.parse("http://localhost:29292/html/spec_runner.html") + rackup_opts = "-p #{runner_uri.port}" + test_result = nil + + def need_to_wait?(uri) + Net::HTTP.start(uri.host, uri.port) { |http| http.get(uri.path) } + rescue Errno::ECONNREFUSED + retry if yield + end + + task :daemonize do + rackup_opts += " -D -P #{pid_file}" + end + + task :npm do + Dir.chdir(work_dir) { system "npm install --silent" } + end + + task :rackup do + Dir.chdir(work_dir) { system "bundle exec rackup #{rackup_opts}" } + end + + task :wait do + cnt = 0 + need_to_wait?(runner_uri) { sleep 1; cnt += 1; cnt < 5 } + end + + task :mocha do + Dir.chdir(work_dir) { test_result = system("$(npm bin)/mocha-phantomjs #{runner_uri}") } + end + + task :kill do + system "kill #{File.read pid_file}" + end + + task :exit do + exit test_result + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/template.rb new/lib/web_console/template.rb --- old/lib/web_console/template.rb 2015-07-10 22:13:19.000000000 +0200 +++ new/lib/web_console/template.rb 2015-12-13 17:58:49.000000000 +0100 @@ -4,33 +4,6 @@ # 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__) ] @@ -38,12 +11,13 @@ def initialize(env, session) @env = env @session = session + @mount_point = Middleware.mount_point end # 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/templates/_markup.html.erb new/lib/web_console/templates/_markup.html.erb --- old/lib/web_console/templates/_markup.html.erb 2015-07-10 22:13:19.000000000 +0200 +++ new/lib/web_console/templates/_markup.html.erb 2015-12-13 17:58:49.000000000 +0100 @@ -1,4 +1,5 @@ <div id="console" - data-remote-path='<%= "console/repl_sessions/#{@session.id}" %>' - data-initial-prompt='>> '> + data-mount-point='<%= @mount_point %>' + data-session-id='<%= @session.id %>' + data-prompt-label='>> '> </div> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/templates/console.js.erb new/lib/web_console/templates/console.js.erb --- old/lib/web_console/templates/console.js.erb 2015-07-10 22:13:19.000000000 +0200 +++ new/lib/web_console/templates/console.js.erb 2015-12-13 17:58:49.000000000 +0100 @@ -54,11 +54,69 @@ // REPLConsole Constructor function REPLConsole(config) { + function getConfig(key, defaultValue) { + return config && config[key] || defaultValue; + } + this.commandStorage = new CommandStorage(); - this.prompt = config && config.promptLabel ? config.promptLabel : ' >>'; - this.commandHandle = config && config.commandHandle ? config.commandHandle : function() { return this; } + this.prompt = getConfig('promptLabel', ' >>'); + this.mountPoint = getConfig('mountPoint'); + this.sessionId = getConfig('sessionId'); } +REPLConsole.prototype.getSessionUrl = function(path) { + var parts = [ this.mountPoint, 'repl_sessions', this.sessionId ]; + if (path) { + parts.push(path); + } + // Join and remove duplicate slashes. + return parts.join('/').replace(/([^:]\/)\/+/g, '$1'); +}; + +REPLConsole.prototype.commandHandle = function(line, callback) { + var self = this; + var params = 'input=' + encodeURIComponent(line); + callback = callback || function() {}; + + function isSuccess(status) { + 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.getSessionUrl(), params, function(xhr) { + var response = parseJSON(xhr.responseText); + var result = isSuccess(xhr.status); + if (result) { + self.writeOutput(response.output); + } else { + if (response && response.output) { + self.writeError(response.output); + } else { + self.writeError(getErrorText(xhr)); + } + } + callback(result, response); + }); +}; + +REPLConsole.prototype.uninstall = function() { + this.container.parentNode.removeChild(this.container); +}; REPLConsole.prototype.install = function(container) { var _this = this; @@ -99,8 +157,8 @@ // Make the console resizable. function resizeContainer(ev) { - var startY = ev.clientY; - var startHeight = parseInt(document.defaultView.getComputedStyle(container).height, 10); + var startY = ev.clientY; + var startHeight = parseInt(document.defaultView.getComputedStyle(container).height, 10); var scrollTopStart = consoleOuter.scrollTop; var clientHeightStart = consoleOuter.clientHeight; @@ -137,10 +195,10 @@ } // Initialize + this.container = container; this.outer = consoleOuter; this.inner = findChild(this.outer, 'console-inner'); this.clipboard = findChild(container, 'clipboard'); - this.remotePath = container.dataset.remotePath; this.newPromptBox(); this.insertCss(); @@ -263,6 +321,13 @@ consoleMessage.innerHTML = escapeHTML(output); this.inner.appendChild(consoleMessage); this.newPromptBox(); + return consoleMessage; +}; + +REPLConsole.prototype.writeError = function(output) { + var consoleMessage = this.writeOutput(output); + addClass(consoleMessage, "error-message"); + return consoleMessage; }; REPLConsole.prototype.onEnterKey = function() { @@ -385,7 +450,7 @@ // Change the binding of the console REPLConsole.prototype.switchBindingTo = function(frameId, callback) { - var url = this.remotePath + "/trace"; + var url = this.getSessionUrl('trace'); var params = "frame_id=" + encodeURIComponent(frameId); postRequest(url, params, callback); }; @@ -394,22 +459,16 @@ * Install the console into the element with a specific ID. * Example: REPLConsole.installInto("target-id") */ -REPLConsole.installInto = function(id) { +REPLConsole.installInto = function(id, options) { var consoleElement = document.getElementById(id); - var remotePath = consoleElement.dataset.remotePath; - var replConsole = new REPLConsole({ - promptLabel: consoleElement.dataset.initialPrompt, - commandHandle: function(line) { - var _this = this; - var url = remotePath; - var params = "input=" + encodeURIComponent(line); - putRequest(url, params, function(xhr) { - var response = JSON.parse(xhr.responseText); - _this.writeOutput(response.output); - }); - } - }); + options = options || {}; + + for (var prop in consoleElement.dataset) { + options[prop] = options[prop] || consoleElement.dataset[prop]; + } + + var replConsole = new REPLConsole(options); replConsole.install(consoleElement); return replConsole; }; @@ -419,6 +478,26 @@ // It allows to operate the current session from the other scripts. REPLConsole.currentSession = null; +// This line is for the Firefox Add-on, because it doesn't have XMLHttpRequest as default. +// And so we need to require a module compatible with XMLHttpRequest from SDK. +REPLConsole.XMLHttpRequest = typeof XMLHttpRequest === 'undefined' ? null : XMLHttpRequest; + +REPLConsole.request = function request(method, url, params, callback) { + var xhr = new REPLConsole.XMLHttpRequest(); + + xhr.open(method, url, true); + xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + xhr.setRequestHeader("Accept", "<%= Mime[:web_console_v2] %>"); + xhr.send(params); + + xhr.onreadystatechange = function() { + if (xhr.readyState === 4) { + callback(xhr); + } + }; +}; + // DOM helpers function hasClass(el, className) { var regex = new RegExp('(?:^|\\s)' + className + '(?!\\S)', 'g'); @@ -470,28 +549,16 @@ } // XHR helpers -function request(method, url, params, callback) { - var xhr = new XMLHttpRequest(); - - xhr.open(method, url, true); - xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); - xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); - xhr.setRequestHeader("Accept", "<%= Mime::WEB_CONSOLE_V2 %>"); - xhr.send(params); - - xhr.onreadystatechange = function() { - if (xhr.readyState === 4) { - callback(xhr); - } - } +function postRequest() { + REPLConsole.request.apply(this, ["POST"].concat([].slice.call(arguments))); } -function postRequest(url, params, callback) { - request("POST", url, params, callback); +function putRequest() { + REPLConsole.request.apply(this, ["PUT"].concat([].slice.call(arguments))); } -function putRequest(url, params, callback) { - request("PUT", url, params, callback); +if (typeof exports === 'object') { + exports.REPLConsole = REPLConsole; +} else { + window.REPLConsole = REPLConsole; } - -window.REPLConsole = REPLConsole; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/templates/style.css.erb new/lib/web_console/templates/style.css.erb --- old/lib/web_console/templates/style.css.erb 2015-07-10 22:13:19.000000000 +0200 +++ new/lib/web_console/templates/style.css.erb 2015-12-13 17:58:49.000000000 +0100 @@ -10,6 +10,7 @@ .console .console-inner { font-family: monospace; font-size: 11px; width: 100%; height: 100%; overflow: none; background: #333; } .console .console-prompt-box { color: #FFF; } .console .console-message { color: #1AD027; margin: 0; border: 0; white-space: pre-wrap; background-color: #333; padding: 0; } +.console .console-message.error-message { color: #fc9; } .console .console-focus .console-cursor { background: #FEFEFE; color: #333; font-weight: bold; } .console .resizer { background: #333; width: 100%; height: 4px; cursor: ns-resize; } .console .console-actions { padding-right: 3px; } @@ -20,3 +21,7 @@ .console .clipboard { height: 0px; padding: 0px; margin: 0px; width: 0px; margin-left: -1000px; } .console .console-prompt-label { display: inline; color: #FFF; background: none repeat scroll 0% 0% #333; border: 0; padding: 0; } .console .console-prompt-display { display: inline; color: #FFF; background: none repeat scroll 0% 0% #333; border: 0; padding: 0; } +.console.full-screen { height: 100%; } +.console.full-screen .console-outer { padding-top: 3px; } +.console.full-screen .resizer { display: none; } +.console.full-screen .close-button { display: none; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/testing/erb_precompiler.rb new/lib/web_console/testing/erb_precompiler.rb --- old/lib/web_console/testing/erb_precompiler.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/lib/web_console/testing/erb_precompiler.rb 2015-12-13 17:58:49.000000000 +0100 @@ -0,0 +1,25 @@ +require 'web_console/testing/helper' +require 'web_console/testing/fake_middleware' + +module WebConsole + module Testing + # This class is to pre-compile 'templates/*.erb'. + class ERBPrecompiler + def initialize(path) + @erb = ERB.new(File.read(path)) + @view = FakeMiddleware.new( + view_path: Helper.gem_root.join('lib/web_console/templates'), + ).view + end + + def build + @erb.result(binding) + end + + def method_missing(name, *args, &block) + return super unless @view.respond_to?(name) + @view.send(name, *args, &block) + end + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/testing/fake_middleware.rb new/lib/web_console/testing/fake_middleware.rb --- old/lib/web_console/testing/fake_middleware.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/lib/web_console/testing/fake_middleware.rb 2015-12-13 17:58:49.000000000 +0100 @@ -0,0 +1,43 @@ +require 'action_view' +require 'action_dispatch' +require 'active_support/core_ext/string/access' +require 'json' +require 'web_console/whitelist' +require 'web_console/request' +require 'web_console/view' +require 'web_console/testing/helper' + +module WebConsole + module Testing + class FakeMiddleware + I18n.load_path.concat(Dir[Helper.gem_root.join('lib/web_console/locales/*.yml')]) + + DEFAULT_HEADERS = { "Content-Type" => "application/javascript" } + + def initialize(opts) + @headers = opts.fetch(:headers, DEFAULT_HEADERS) + @req_path_regex = opts[:req_path_regex] + @view_path = opts[:view_path] + end + + def call(env) + [ 200, @headers, [ render(req_path(env)) ] ] + end + + def view + @view ||= View.new(@view_path) + end + + private + + # extract target path from REQUEST_PATH + def req_path(env) + env["REQUEST_PATH"].match(@req_path_regex)[1] + end + + def render(template) + view.render(template: template, layout: nil) + end + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/testing/helper.rb new/lib/web_console/testing/helper.rb --- old/lib/web_console/testing/helper.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/lib/web_console/testing/helper.rb 2015-12-13 17:58:49.000000000 +0100 @@ -0,0 +1,9 @@ +module WebConsole + module Testing + module Helper + def self.gem_root + Pathname(File.expand_path('../../../../', __FILE__)) + end + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/version.rb new/lib/web_console/version.rb --- old/lib/web_console/version.rb 2015-07-10 22:13:19.000000000 +0200 +++ new/lib/web_console/version.rb 2015-12-13 17:58:49.000000000 +0100 @@ -1,3 +1,3 @@ module WebConsole - VERSION = '2.2.1' + VERSION = '3.0.0' end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/view.rb new/lib/web_console/view.rb --- old/lib/web_console/view.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/lib/web_console/view.rb 2015-12-13 17:58:49.000000000 +0100 @@ -0,0 +1,37 @@ +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 + + # Override method for ActionView::Helpers::TranslationHelper#t. + # + # This method escapes the original return value for JavaScript, since the + # method returns a HTML tag with some attributes when the key is not found, + # so it could cause a syntax error if we use the value in the string literals. + def t(key, options = {}) + j super + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console/whiny_request.rb new/lib/web_console/whiny_request.rb --- old/lib/web_console/whiny_request.rb 2015-07-10 22:13:19.000000000 +0200 +++ new/lib/web_console/whiny_request.rb 2015-12-13 17:58:49.000000000 +0100 @@ -4,20 +4,13 @@ # If any calls to +from_whitelisted_ip?+ and +acceptable_content_type?+ # return false, an info log message will be displayed in users' logs. class WhinyRequest < SimpleDelegator - def from_whitelited_ip? - whine_unless request.from_whitelited_ip? do - "Cannot render console from #{request.remote_ip}! " \ + def from_whitelisted_ip? + whine_unless request.from_whitelisted_ip? do + "Cannot render console from #{request.strict_remote_ip}! " \ "Allowed networks: #{request.whitelisted_ips}" end end - def acceptable_content_type? - whine_unless request.acceptable_content_type? do - "Cannot render console with content type #{request.content_type}" \ - "Allowed content types: #{request.acceptable_content_types}" - end - end - private def whine_unless(condition) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/web_console.rb new/lib/web_console.rb --- old/lib/web_console.rb 2015-07-10 22:13:19.000000000 +0200 +++ new/lib/web_console.rb 2015-12-13 17:58:49.000000000 +0100 @@ -1,5 +1,3 @@ -require 'binding_of_caller' - require 'active_support/lazy_load_hooks' require 'active_support/logger' @@ -13,6 +11,8 @@ require 'web_console/middleware' require 'web_console/whitelist' require 'web_console/request' +require 'web_console/response' +require 'web_console/view' require 'web_console/whiny_request' module WebConsole diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 2015-07-10 22:13:19.000000000 +0200 +++ new/metadata 2015-12-13 17:58:49.000000000 +0100 @@ -1,7 +1,7 @@ --- !ruby/object:Gem::Specification name: web-console version: !ruby/object:Gem::Version - version: 2.2.1 + version: 3.0.0 platform: ruby authors: - Charlie Somerville @@ -11,7 +11,7 @@ autorequire: bindir: bin cert_chain: [] -date: 2015-07-10 00:00:00.000000000 Z +date: 2015-12-13 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: railties @@ -19,90 +19,42 @@ requirements: - - ">=" - !ruby/object:Gem::Version - version: '4.0' + version: '4.2' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version - version: '4.0' + version: '4.2' - !ruby/object:Gem::Dependency name: activemodel requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version - version: '4.0' + version: '4.2' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version - version: '4.0' + version: '4.2' - !ruby/object:Gem::Dependency - name: sprockets-rails + name: debug_inspector requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version - version: '2.0' - - - "<" - - !ruby/object:Gem::Version - version: '4.0' + version: '0' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version - version: '2.0' - - - "<" - - !ruby/object:Gem::Version - version: '4.0' -- !ruby/object:Gem::Dependency - name: binding_of_caller - requirement: !ruby/object:Gem::Requirement - requirements: - - - ">=" - - !ruby/object:Gem::Version - version: 0.7.2 - type: :runtime - prerelease: false - version_requirements: !ruby/object:Gem::Requirement - requirements: - - - ">=" - - !ruby/object:Gem::Version - version: 0.7.2 -- !ruby/object:Gem::Dependency - name: actionmailer - requirement: !ruby/object:Gem::Requirement - requirements: - - - ">=" - - !ruby/object:Gem::Version - version: '4.0' - type: :development - prerelease: false - version_requirements: !ruby/object:Gem::Requirement - requirements: - - - ">=" - - !ruby/object:Gem::Version - version: '4.0' -- !ruby/object:Gem::Dependency - name: activerecord - requirement: !ruby/object:Gem::Requirement - requirements: - - - ">=" - - !ruby/object:Gem::Version - version: '4.0' - type: :development - prerelease: false - version_requirements: !ruby/object:Gem::Requirement - requirements: - - - ">=" - - !ruby/object:Gem::Version - version: '4.0' + version: '0' description: email: - char...@charliesomerville.com @@ -125,12 +77,15 @@ - lib/web_console/helper.rb - lib/web_console/integration.rb - lib/web_console/integration/cruby.rb -- lib/web_console/integration/jruby.rb - lib/web_console/integration/rubinius.rb +- lib/web_console/locales/en.yml - lib/web_console/middleware.rb - lib/web_console/railtie.rb - lib/web_console/request.rb +- lib/web_console/response.rb - lib/web_console/session.rb +- lib/web_console/tasks/extensions.rake +- lib/web_console/tasks/test_templates.rake - lib/web_console/template.rb - lib/web_console/templates/_inner_console_markup.html.erb - lib/web_console/templates/_markup.html.erb @@ -142,8 +97,12 @@ - lib/web_console/templates/layouts/javascript.erb - lib/web_console/templates/main.js.erb - lib/web_console/templates/style.css.erb +- lib/web_console/testing/erb_precompiler.rb +- lib/web_console/testing/fake_middleware.rb +- lib/web_console/testing/helper.rb - lib/web_console/tracer.rb - lib/web_console/version.rb +- lib/web_console/view.rb - lib/web_console/whiny_request.rb - lib/web_console/whitelist.rb homepage: https://github.com/rails/web-console @@ -158,7 +117,7 @@ requirements: - - ">=" - !ruby/object:Gem::Version - version: '0' + version: 2.2.2 required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">="