require "open3" module Xray OPEN_PATH = '/_xray/open' UPDATE_CONFIG_PATH = '/_xray/config' # This middleware is responsible for injecting xray.js, xray-backbone.js, and # the Xray bar into the app's pages. It also listens for requests to open files # with the user's editor. class Middleware def initialize(app) @app = app end def call(env) # Request for opening a file path. if env['PATH_INFO'] == OPEN_PATH req, res = Rack::Request.new(env), Rack::Response.new out, err, status = Xray.open_file(req.GET['path']) if status.success? res.status = 200 else res.write out res.status = 500 end res.finish elsif env['PATH_INFO'] == UPDATE_CONFIG_PATH req, res = Rack::Request.new(env), Rack::Response.new if req.post? && Xray.config.editor = req.POST['editor'] res.status = 200 else res.status = 400 end res.finish # Inject xray.js and friends if this is a successful HTML response else status, headers, response = @app.call(env) if html_headers?(status, headers) && body = response_body(response) body = body.sub(/]*>/) { "#{$~}\n#{xray_bar}" } # Inject js script tags if assets are unbundled if Rails.application.config.assets.debug append_js!(body, 'jquery', 'xray') append_js!(body, 'backbone', 'xray-backbone') end content_length = body.bytesize.to_s # Modifying the original response obj maintains compatibility with other middlewares if ActionDispatch::Response === response response.body = [body] response.header['Content-Length'] = content_length response.to_a else headers['Content-Length'] = content_length [status, headers, [body]] end else [status, headers, response] end end end private def xray_bar ActionController::Base.new.render_to_string(:partial => '/xray_bar').html_safe end # Appends the given `script_name` after the `after_script_name`. def append_js!(html, after_script_name, script_name) # Matches: # # # html.sub!(/]+\/#{after_script_name}([-.]{1}[\d\.]+)?([-.]{1}min)?\.js[^>]+><\/script>/) do h = ActionController::Base.helpers "#{$~}\n" + h.javascript_include_tag(script_name) end end def html_headers?(status, headers) status == 200 && headers['Content-Type'] && headers['Content-Type'].include?('text/html') && headers["Content-Transfer-Encoding"] != "binary" end def response_body(response) body = '' response.each { |s| body << s.to_s } body end end end