rails_spotlight 0.1.7 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -0
- data/Guardfile +1 -0
- data/README.md +35 -13
- data/chrome_ext_private_policy.md +39 -0
- data/docker-compose.yml +0 -4
- data/fake_spec_res/rails_spotlight_spec.rb +8 -8
- data/lib/rails_spotlight/app_notifications.rb +88 -0
- data/lib/rails_spotlight/app_request.rb +20 -0
- data/lib/rails_spotlight/channels/live_console_channel.rb +91 -0
- data/lib/rails_spotlight/channels/request_completed_channel.rb +15 -0
- data/lib/rails_spotlight/channels.rb +8 -0
- data/lib/rails_spotlight/configuration.rb +96 -0
- data/lib/rails_spotlight/event.rb +93 -0
- data/lib/rails_spotlight/log_interceptor.rb +47 -0
- data/lib/rails_spotlight/middlewares/concerns/skip_request_paths.rb +35 -0
- data/lib/rails_spotlight/middlewares/handlers/file_action_handler.rb +13 -6
- data/lib/rails_spotlight/middlewares/handlers/meta_action_handler.rb +28 -0
- data/lib/rails_spotlight/middlewares/handlers/sql_action_handler.rb +3 -5
- data/lib/rails_spotlight/middlewares/handlers/verify_action_handler.rb +4 -1
- data/lib/rails_spotlight/middlewares/header_marker.rb +33 -0
- data/lib/rails_spotlight/middlewares/main_request_handler.rb +35 -0
- data/lib/rails_spotlight/middlewares/request_completed.rb +71 -0
- data/lib/rails_spotlight/middlewares/request_handler.rb +2 -0
- data/lib/rails_spotlight/middlewares.rb +10 -0
- data/lib/rails_spotlight/railtie.rb +41 -12
- data/lib/rails_spotlight/storage.rb +47 -0
- data/lib/rails_spotlight/utils.rb +28 -0
- data/lib/rails_spotlight/version.rb +1 -1
- data/lib/rails_spotlight.rb +22 -3
- data/lib/tasks/init.rake +36 -0
- metadata +83 -10
- data/lib/rails_spotlight/support/project.rb +0 -21
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RailsSpotlight
         | 
| 4 | 
            +
              module Middlewares
         | 
| 5 | 
            +
                module SkipRequestPaths
         | 
| 6 | 
            +
                  PATHS_TO_SKIP = %w[/__better_errors /__rails_spotlight /__meta_request].freeze
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  private
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def skip?(path)
         | 
| 11 | 
            +
                    skip_paths.any? { |skip_path| path.start_with?(skip_path) } || asset?(path)
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def default_skip_paths
         | 
| 15 | 
            +
                    PATHS_TO_SKIP
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def additional_skip_paths
         | 
| 19 | 
            +
                    []
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  def skip_paths
         | 
| 23 | 
            +
                    additional_skip_paths + default_skip_paths + ::RailsSpotlight.config.middleware_skipped_paths
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def asset?(path)
         | 
| 27 | 
            +
                    app_config.respond_to?(:assets) && path.start_with?(assets_prefix)
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  def assets_prefix
         | 
| 31 | 
            +
                    "/#{app_config.assets.prefix[%r{\A/?(.*?)/?\z}, 1]}/"
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
| @@ -1,7 +1,5 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require_relative '../../support/project'
         | 
| 4 | 
            -
             | 
| 5 3 | 
             
            module RailsSpotlight
         | 
| 6 4 | 
             
              module Middlewares
         | 
| 7 5 | 
             
                module Handlers
         | 
| @@ -9,7 +7,7 @@ module RailsSpotlight | |
| 9 7 | 
             
                    def execute
         | 
| 10 8 | 
             
                      raise NotFound, 'File not found' unless path_valid?
         | 
| 11 9 |  | 
| 12 | 
            -
                      File.write(file_path,  | 
| 10 | 
            +
                      File.write(file_path, new_content) if write_mode?
         | 
| 13 11 | 
             
                    rescue => e # rubocop:disable Style/RescueStandardError
         | 
| 14 12 | 
             
                      raise UnprocessableEntity, e.message
         | 
| 15 13 | 
             
                    end
         | 
| @@ -20,11 +18,16 @@ module RailsSpotlight | |
| 20 18 | 
             
                      File.read(file_path)
         | 
| 21 19 | 
             
                    end
         | 
| 22 20 |  | 
| 21 | 
            +
                    def new_content
         | 
| 22 | 
            +
                      json_request_body.fetch('content')
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
             | 
| 23 25 | 
             
                    def json_response_body
         | 
| 24 26 | 
             
                      {
         | 
| 25 27 | 
             
                        source: text_response_body,
         | 
| 26 | 
            -
                         | 
| 27 | 
            -
             | 
| 28 | 
            +
                        changed: write_mode?,
         | 
| 29 | 
            +
                        project: ::RailsSpotlight.config.project_name
         | 
| 30 | 
            +
                      }.merge(write_mode? ? { new_content: new_content } : {})
         | 
| 28 31 | 
             
                    end
         | 
| 29 32 |  | 
| 30 33 | 
             
                    def write_mode?
         | 
| @@ -40,7 +43,11 @@ module RailsSpotlight | |
| 40 43 | 
             
                    end
         | 
| 41 44 |  | 
| 42 45 | 
             
                    def file_path
         | 
| 43 | 
            -
                      @file_path ||=  | 
| 46 | 
            +
                      @file_path ||= if json_request_body.fetch('file').start_with?(::RailsSpotlight.config.rails_root)
         | 
| 47 | 
            +
                                       json_request_body.fetch('file')
         | 
| 48 | 
            +
                                     else
         | 
| 49 | 
            +
                                       File.join(::RailsSpotlight.config.rails_root, json_request_body.fetch('file'))
         | 
| 50 | 
            +
                                     end
         | 
| 44 51 | 
             
                    end
         | 
| 45 52 | 
             
                  end
         | 
| 46 53 | 
             
                end
         | 
| @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RailsSpotlight
         | 
| 4 | 
            +
              module Middlewares
         | 
| 5 | 
            +
                module Handlers
         | 
| 6 | 
            +
                  class MetaActionHandler < BaseActionHandler
         | 
| 7 | 
            +
                    def execute; end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                    private
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    def json_response_body
         | 
| 12 | 
            +
                      {
         | 
| 13 | 
            +
                        events: events,
         | 
| 14 | 
            +
                        project: ::RailsSpotlight.config.project_name
         | 
| 15 | 
            +
                      }
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    def id
         | 
| 19 | 
            +
                      @id ||= request.params['id']
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    def events
         | 
| 23 | 
            +
                      @events ||= Storage.new(id).read || []
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
            end
         | 
| @@ -1,7 +1,5 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require_relative '../../support/project'
         | 
| 4 | 
            -
             | 
| 5 3 | 
             
            module RailsSpotlight
         | 
| 6 4 | 
             
              module Middlewares
         | 
| 7 5 | 
             
                module Handlers
         | 
| @@ -20,9 +18,9 @@ module RailsSpotlight | |
| 20 18 | 
             
                    def validate_project!
         | 
| 21 19 | 
             
                      Rails.logger.warn required_projects
         | 
| 22 20 | 
             
                      return if required_projects.blank?
         | 
| 23 | 
            -
                      return if required_projects.include?(::RailsSpotlight | 
| 21 | 
            +
                      return if required_projects.include?(::RailsSpotlight.config.project_name)
         | 
| 24 22 |  | 
| 25 | 
            -
                      raise UnprocessableEntity, "Check your connetction settings the current query is not allowed to be executed on the #{::RailsSpotlight | 
| 23 | 
            +
                      raise UnprocessableEntity, "Check your connetction settings the current query is not allowed to be executed on the #{::RailsSpotlight.config.project_name} project"
         | 
| 26 24 | 
             
                    end
         | 
| 27 25 |  | 
| 28 26 | 
             
                    def transaction
         | 
| @@ -59,7 +57,7 @@ module RailsSpotlight | |
| 59 57 | 
             
                        logs: logs,
         | 
| 60 58 | 
             
                        error: error.present? ? error.inspect : nil,
         | 
| 61 59 | 
             
                        query_mode: force_execution? ? 'force' : 'default',
         | 
| 62 | 
            -
                        project: ::RailsSpotlight | 
| 60 | 
            +
                        project: ::RailsSpotlight.config.project_name
         | 
| 63 61 | 
             
                      }
         | 
| 64 62 | 
             
                    end
         | 
| 65 63 |  | 
| @@ -18,7 +18,10 @@ module RailsSpotlight | |
| 18 18 | 
             
                        body: request.body.read,
         | 
| 19 19 | 
             
                        content_type: request.content_type,
         | 
| 20 20 | 
             
                        request_method: request.request_method,
         | 
| 21 | 
            -
                        version: request.get_header('HTTP_X_RAILS_SPOTLIGHT')
         | 
| 21 | 
            +
                        version: request.get_header('HTTP_X_RAILS_SPOTLIGHT'),
         | 
| 22 | 
            +
                        current_gem_version: ::RailsSpotlight::VERSION,
         | 
| 23 | 
            +
                        project: ::RailsSpotlight.config.project_name,
         | 
| 24 | 
            +
                        action_cable_path: defined?(ActionCable) ? ActionCable&.server&.config&.mount_path : nil
         | 
| 22 25 | 
             
                      }
         | 
| 23 26 | 
             
                    end
         | 
| 24 27 | 
             
                  end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'rack/contrib/response_headers'
         | 
| 4 | 
            +
            require_relative 'concerns/skip_request_paths'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module RailsSpotlight
         | 
| 7 | 
            +
              module Middlewares
         | 
| 8 | 
            +
                class HeaderMarker
         | 
| 9 | 
            +
                  include ::RailsSpotlight::Middlewares::SkipRequestPaths
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  def initialize(app, app_config)
         | 
| 12 | 
            +
                    @app = app
         | 
| 13 | 
            +
                    @app_config = app_config
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def call(env)
         | 
| 17 | 
            +
                    request_path = env['PATH_INFO']
         | 
| 18 | 
            +
                    middleware = Rack::ResponseHeaders.new(app) do |headers|
         | 
| 19 | 
            +
                      headers['X-Rails-Spotlight-Version'] = RailsSpotlight::VERSION unless skip?(request_path)
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                    middleware.call(env)
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  private
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  attr_reader :app, :app_config
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def default_skip_paths
         | 
| 29 | 
            +
                    %w[/__better_errors]
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'json'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module RailsSpotlight
         | 
| 6 | 
            +
              module Middlewares
         | 
| 7 | 
            +
                class MainRequestHandler
         | 
| 8 | 
            +
                  def initialize(app)
         | 
| 9 | 
            +
                    @app = app
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  attr_reader :app
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def call(env)
         | 
| 15 | 
            +
                    app_request = AppRequest.new env['action_dispatch.request_id']
         | 
| 16 | 
            +
                    app_request.current!
         | 
| 17 | 
            +
                    app.call(env)
         | 
| 18 | 
            +
                  rescue StandardError => e
         | 
| 19 | 
            +
                    if defined?(ActionDispatch::ExceptionWrapper)
         | 
| 20 | 
            +
                      wrapper = if ActionDispatch::ExceptionWrapper.method_defined? :env
         | 
| 21 | 
            +
                                  ActionDispatch::ExceptionWrapper.new(env, e)
         | 
| 22 | 
            +
                                else
         | 
| 23 | 
            +
                                  ActionDispatch::ExceptionWrapper.new(env['action_dispatch.backtrace_cleaner'], e)
         | 
| 24 | 
            +
                                end
         | 
| 25 | 
            +
                      app_request.events.push(*Event.events_for_exception(wrapper))
         | 
| 26 | 
            +
                    else
         | 
| 27 | 
            +
                      app_request.events.push(*Event.events_for_exception(e))
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
                    raise
         | 
| 30 | 
            +
                  ensure
         | 
| 31 | 
            +
                    Storage.new(app_request.id).write(app_request.events.to_json) unless app_request.events.empty?
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
| @@ -0,0 +1,71 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative 'concerns/skip_request_paths'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module RailsSpotlight
         | 
| 6 | 
            +
              module Middlewares
         | 
| 7 | 
            +
                class RequestCompleted
         | 
| 8 | 
            +
                  include ::RailsSpotlight::Middlewares::SkipRequestPaths
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def initialize(app, app_config)
         | 
| 11 | 
            +
                    @app = app
         | 
| 12 | 
            +
                    @app_config = app_config
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def call(env)
         | 
| 16 | 
            +
                    if skip?(env['PATH_INFO']) || (env['HTTP_CONNECTION'] == 'Upgrade' && env['HTTP_UPGRADE'] == 'websocket')
         | 
| 17 | 
            +
                      app.call(env)
         | 
| 18 | 
            +
                    else
         | 
| 19 | 
            +
                      status, headers, body = app.call(env)
         | 
| 20 | 
            +
                      publish_event(status, headers, env)
         | 
| 21 | 
            +
                      [status, headers, body]
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
                  rescue => e # rubocop:disable Style/RescueStandardError
         | 
| 24 | 
            +
                    ::RailsSpotlight.config.logger.error "Error in RailsSpotlight::Middlewares::RequestCompletedHandler instrumentation: #{e.message}"
         | 
| 25 | 
            +
                    app.call(env)
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  private
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  attr_reader :app, :app_config
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def rails_spotlight_request_id
         | 
| 33 | 
            +
                    Thread.current[:rails_spotlight_request_id]&.id
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  def publish_event(status, _headers, env)
         | 
| 37 | 
            +
                    return if status < 100
         | 
| 38 | 
            +
                    return unless rails_spotlight_request_id
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    request = ActionDispatch::Request.new(env)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    host, url = host_and_url(env)
         | 
| 43 | 
            +
                    ActionCable.server.broadcast(
         | 
| 44 | 
            +
                      'rails_spotlight_request_completed_channel',
         | 
| 45 | 
            +
                      {
         | 
| 46 | 
            +
                        rails_spotlight_version: RailsSpotlight::VERSION,
         | 
| 47 | 
            +
                        id: rails_spotlight_request_id,
         | 
| 48 | 
            +
                        http_method: env['REQUEST_METHOD'],
         | 
| 49 | 
            +
                        host: host,
         | 
| 50 | 
            +
                        url: url,
         | 
| 51 | 
            +
                        format: request.format.symbol,
         | 
| 52 | 
            +
                        controller: request.path_parameters[:controller],
         | 
| 53 | 
            +
                        action: request.path_parameters[:action]
         | 
| 54 | 
            +
                      }
         | 
| 55 | 
            +
                    )
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  def host_and_url(env)
         | 
| 59 | 
            +
                    scheme = env['rack.url_scheme']
         | 
| 60 | 
            +
                    host = env['HTTP_HOST']
         | 
| 61 | 
            +
                    path = env['PATH_INFO']
         | 
| 62 | 
            +
                    query_string = env['QUERY_STRING']
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    host_url = "#{scheme}://#{host}"
         | 
| 65 | 
            +
                    full_url = "#{host_url}#{path}"
         | 
| 66 | 
            +
                    full_url += "?#{query_string}" unless query_string.empty?
         | 
| 67 | 
            +
                    [host_url, full_url]
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
              end
         | 
| 71 | 
            +
            end
         | 
| @@ -5,6 +5,7 @@ require_relative 'handlers/file_action_handler' | |
| 5 5 | 
             
            require_relative 'handlers/sql_action_handler'
         | 
| 6 6 | 
             
            require_relative 'handlers/verify_action_handler'
         | 
| 7 7 | 
             
            require_relative 'handlers/not_found_action_handler'
         | 
| 8 | 
            +
            require_relative 'handlers/meta_action_handler'
         | 
| 8 9 |  | 
| 9 10 | 
             
            module RailsSpotlight
         | 
| 10 11 | 
             
              module Middlewares
         | 
| @@ -31,6 +32,7 @@ module RailsSpotlight | |
| 31 32 | 
             
                    when 'file' then Handlers::FileActionHandler.new(*args).call
         | 
| 32 33 | 
             
                    when 'sql' then Handlers::SqlActionHandler.new(*args).call
         | 
| 33 34 | 
             
                    when 'verify' then Handlers::VerifyActionHandler.new(*args).call
         | 
| 35 | 
            +
                    when 'meta' then Handlers::MetaActionHandler.new(*args).call
         | 
| 34 36 | 
             
                    else
         | 
| 35 37 | 
             
                      Handlers::NotFoundActionHandler.new(*args).call
         | 
| 36 38 | 
             
                    end
         | 
| @@ -0,0 +1,10 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RailsSpotlight
         | 
| 4 | 
            +
              module Middlewares
         | 
| 5 | 
            +
                autoload :RequestHandler,     'rails_spotlight/middlewares/request_handler'
         | 
| 6 | 
            +
                autoload :RequestCompleted,   'rails_spotlight/middlewares/request_completed'
         | 
| 7 | 
            +
                autoload :HeaderMarker,       'rails_spotlight/middlewares/header_marker'
         | 
| 8 | 
            +
                autoload :MainRequestHandler, 'rails_spotlight/middlewares/main_request_handler'
         | 
| 9 | 
            +
              end
         | 
| 10 | 
            +
            end
         | 
| @@ -1,28 +1,57 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            require 'rails/railtie'
         | 
| 4 | 
            -
            require 'meta_request'
         | 
| 5 4 |  | 
| 6 5 | 
             
            module RailsSpotlight
         | 
| 7 6 | 
             
              class Railtie < ::Rails::Railtie
         | 
| 8 7 | 
             
                initializer 'rails_spotlight.inject_middlewares' do
         | 
| 9 | 
            -
                   | 
| 8 | 
            +
                  insert_base_middlewares unless Rails.env.production?
         | 
| 10 9 | 
             
                end
         | 
| 11 10 |  | 
| 12 | 
            -
                 | 
| 13 | 
            -
             | 
| 14 | 
            -
                 | 
| 15 | 
            -
             | 
| 16 | 
            -
                 | 
| 17 | 
            -
             | 
| 18 | 
            -
                 | 
| 11 | 
            +
                initializer 'rails_spotlight.log_interceptor' do
         | 
| 12 | 
            +
                  Rails.logger&.extend(LogInterceptor) unless Rails.env.production?
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                initializer 'rails_spotlight.subscribe_to_notifications' do
         | 
| 16 | 
            +
                  AppNotifications.subscribe unless Rails.env.production?
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                initializer 'rails_spotlight.action_cable_setup' do
         | 
| 20 | 
            +
                  insert_action_cable_helpers unless Rails.env.production?
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def insert_action_cable_helpers
         | 
| 24 | 
            +
                  return unless ::RailsSpotlight.config.action_cable_present?
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  app.config.after_initialize do
         | 
| 27 | 
            +
                    update_actioncable_allowed_request_origins!
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    require 'rails_spotlight/channels/request_completed_channel' if ::RailsSpotlight.config.request_completed_broadcast_enabled?
         | 
| 30 | 
            +
                    require 'rails_spotlight/channels/live_console_channel' if ::RailsSpotlight.config.live_console_enabled?
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    app.routes.draw { mount ActionCable.server => '/cable' } if ::RailsSpotlight.config.auto_mount_action_cable?
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                def update_actioncable_allowed_request_origins!
         | 
| 37 | 
            +
                  existing_origins = Array(app.config.action_cable.allowed_request_origins)
         | 
| 38 | 
            +
                  app.config.action_cable.allowed_request_origins = existing_origins | [%r{\Achrome-extension://.*\z}]
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def insert_base_middlewares
         | 
| 42 | 
            +
                  app.middleware.use ::RailsSpotlight::Middlewares::RequestHandler
         | 
| 19 43 |  | 
| 20 | 
            -
                def insert_middleware
         | 
| 21 44 | 
             
                  if defined? ActionDispatch::DebugExceptions
         | 
| 22 | 
            -
                    app.middleware.insert_before ActionDispatch::DebugExceptions, RailsSpotlight::Middlewares:: | 
| 45 | 
            +
                    app.middleware.insert_before ActionDispatch::DebugExceptions, ::RailsSpotlight::Middlewares::HeaderMarker, app.config
         | 
| 23 46 | 
             
                  else
         | 
| 24 | 
            -
                    app.middleware.use RailsSpotlight::Middlewares:: | 
| 47 | 
            +
                    app.middleware.use ::RailsSpotlight::Middlewares::HeaderMarker, app.config
         | 
| 25 48 | 
             
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  app.middleware.use ::RailsSpotlight::Middlewares::MainRequestHandler
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  return unless ::RailsSpotlight.config.request_completed_broadcast_enabled?
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  app.middleware.insert_after ::RailsSpotlight::Middlewares::HeaderMarker, RailsSpotlight::Middlewares::RequestCompleted, app.config
         | 
| 26 55 | 
             
                end
         | 
| 27 56 |  | 
| 28 57 | 
             
                def app
         | 
| @@ -0,0 +1,47 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RailsSpotlight
         | 
| 4 | 
            +
              class Storage
         | 
| 5 | 
            +
                attr_reader :key
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(key)
         | 
| 8 | 
            +
                  @key = key
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def write(value)
         | 
| 12 | 
            +
                  FileUtils.mkdir_p dir_path
         | 
| 13 | 
            +
                  # Use File.binwrite instead File.open(json_file, 'wb') { |file| file.write(value) }
         | 
| 14 | 
            +
                  File.binwrite(json_file, value)
         | 
| 15 | 
            +
                  maintain_file_pool(RailsSpotlight.config.storage_pool_size)
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def read
         | 
| 19 | 
            +
                  # avoid FileNotFound error
         | 
| 20 | 
            +
                  File.exist?(json_file) ? File.read(json_file) : '[]'
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                private
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def maintain_file_pool(size)
         | 
| 26 | 
            +
                  files = Dir["#{dir_path}/*.json"]
         | 
| 27 | 
            +
                  files = files.sort_by { |f| -file_ctime(f) }
         | 
| 28 | 
            +
                  (files[size..] || []).each do |file|
         | 
| 29 | 
            +
                    FileUtils.rm_f(file)
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def file_ctime(file)
         | 
| 34 | 
            +
                  File.stat(file).ctime.to_i
         | 
| 35 | 
            +
                rescue Errno::ENOENT
         | 
| 36 | 
            +
                  0
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                def json_file
         | 
| 40 | 
            +
                  File.join(dir_path, "#{key}.json")
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                def dir_path
         | 
| 44 | 
            +
                  @dir_path ||= RailsSpotlight.config.storage_path
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
            end
         | 
| @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RailsSpotlight
         | 
| 4 | 
            +
              module Utils
         | 
| 5 | 
            +
                module_function
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def dev_callsite(caller)
         | 
| 8 | 
            +
                  app_line = caller.detect { |c| c.start_with? RailsSpotlight.config.rails_root }
         | 
| 9 | 
            +
                  return nil unless app_line
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  _, filename, _, line, _, method = app_line.split(/^(.*?)(:(\d+))(:in `(.*)')?$/)
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  {
         | 
| 14 | 
            +
                    filename: sub_source_path(filename),
         | 
| 15 | 
            +
                    line: line.to_i,
         | 
| 16 | 
            +
                    method: method
         | 
| 17 | 
            +
                  }
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def sub_source_path(path)
         | 
| 21 | 
            +
                  rails_root = RailsSpotlight.config.rails_root
         | 
| 22 | 
            +
                  source_path = RailsSpotlight.config.source_path
         | 
| 23 | 
            +
                  return path if rails_root == source_path
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  path.sub(rails_root, source_path)
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
            end
         | 
    
        data/lib/rails_spotlight.rb
    CHANGED
    
    | @@ -1,9 +1,28 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require_relative 'rails_spotlight/version'
         | 
| 4 | 
            -
            require_relative 'rails_spotlight/middlewares/request_handler'
         | 
| 5 | 
            -
             | 
| 6 3 | 
             
            module RailsSpotlight
         | 
| 4 | 
            +
              autoload :VERSION,          'rails_spotlight/version'
         | 
| 5 | 
            +
              autoload :Configuration,    'rails_spotlight/configuration'
         | 
| 6 | 
            +
              autoload :Storage,          'rails_spotlight/storage'
         | 
| 7 | 
            +
              autoload :Event,            'rails_spotlight/event'
         | 
| 8 | 
            +
              autoload :AppRequest,       'rails_spotlight/app_request'
         | 
| 9 | 
            +
              autoload :Middlewares,      'rails_spotlight/middlewares'
         | 
| 10 | 
            +
              autoload :LogInterceptor,   'rails_spotlight/log_interceptor'
         | 
| 11 | 
            +
              autoload :AppNotifications, 'rails_spotlight/app_notifications'
         | 
| 12 | 
            +
              autoload :Utils,            'rails_spotlight/utils'
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              class << self
         | 
| 15 | 
            +
                def config
         | 
| 16 | 
            +
                  @config ||= Configuration.load_config
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              autoload :Channels, 'rails_spotlight/channels'
         | 
| 7 21 | 
             
            end
         | 
| 8 22 |  | 
| 9 23 | 
             
            require_relative 'rails_spotlight/railtie'
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            if defined?(Rake)
         | 
| 26 | 
            +
              spec = Gem::Specification.find_by_name 'rails_spotlight'
         | 
| 27 | 
            +
              load "#{spec.gem_dir}/lib/tasks/init.rake"
         | 
| 28 | 
            +
            end
         | 
    
        data/lib/tasks/init.rake
    ADDED
    
    | @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'rake'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            namespace :rails_spotlight do
         | 
| 6 | 
            +
              desc 'Generate rails_spotlight configuration file'
         | 
| 7 | 
            +
              task generate_config: :environment do
         | 
| 8 | 
            +
                require 'fileutils'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                config_path = Rails.root.join('config', 'rails_spotlight.yml')
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                default_config = <<~YAML
         | 
| 13 | 
            +
                  # Default configuration for RailsSpotlight
         | 
| 14 | 
            +
                  PROJECT_NAME: <%=Rails.application.class.respond_to?(:module_parent_name) ? Rails.application.class.module_parent_name : Rails.application.class.parent_name%>
         | 
| 15 | 
            +
                  SOURCE_PATH: <%=Rails.root%>
         | 
| 16 | 
            +
                  STORAGE_PATH: <%=Rails.root.join('tmp', 'data', 'rails_spotlight')%>
         | 
| 17 | 
            +
                  STORAGE_POOL_SIZE: 20
         | 
| 18 | 
            +
                  LOGGER: <%=Logger.new(Rails.root.join('log', 'rails_spotlight.log'))%>
         | 
| 19 | 
            +
                  MIDDLEWARE_SKIPPED_PATHS: []
         | 
| 20 | 
            +
                  NOT_ENCODABLE_EVENT_VALUES:
         | 
| 21 | 
            +
                  # Rest of the configuration is required for ActionCable. It will be disabled automatically in when ActionCable is not available.
         | 
| 22 | 
            +
                  LIVE_CONSOLE_ENABLED: true
         | 
| 23 | 
            +
                  REQUEST_COMPLETED_BROADCAST_ENABLED: false
         | 
| 24 | 
            +
                  AUTO_MOUNT_ACTION_CABLE: true
         | 
| 25 | 
            +
                  ACTION_CABLE_MOUNT_PATH: /cable
         | 
| 26 | 
            +
                YAML
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                if File.exist?(config_path)
         | 
| 29 | 
            +
                  puts 'Config file already exists: config/rails_spotlight.yml'
         | 
| 30 | 
            +
                else
         | 
| 31 | 
            +
                  FileUtils.mkdir_p(File.dirname(config_path))
         | 
| 32 | 
            +
                  File.write(config_path, default_config)
         | 
| 33 | 
            +
                  puts 'Created config file: config/rails_spotlight.yml'
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
            end
         |