lib/yard/server/rack_adapter.rb
# frozen_string_literal: true module YARD module Server begin require 'rackup' # @private RackServer = Rackup::Server rescue LoadError require 'rack' # @private RackServer = Rack::Server end # This class wraps the {RackAdapter} into a Rack-compatible middleware. # See {#initialize} for a list of options to pass via Rack's +#use+ method. # # @note You must pass a +:libraries+ option to the RackMiddleware via +#use+. To # read about how to return a list of libraries, see {LibraryVersion} or look # at the example below. # @example Using the RackMiddleware in a Rack application # libraries = {:mylib => [YARD::Server::LibraryVersion.new('mylib', nil, '/path/to/.yardoc')]} # use YARD::Server::RackMiddleware, :libraries => libraries # class RackMiddleware # Creates a new Rack-based middleware for serving YARD documentation. # # @param app the next Rack middleware in the stack # @option opts [Hash{String=>Array<LibraryVersion>}] :libraries ({}) # the map of libraries to serve through the adapter. This option is *required*. # @option opts [Hash] :options ({}) a list of options to pass to the adapter. # See {Adapter#options} for a list. # @option opts [Hash] :server_options ({}) a list of options to pass to the server. # See {Adapter#server_options} for a list. def initialize(app, opts = {}) args = [opts[:libraries] || {}, opts[:options] || {}, opts[:server_options] || {}] @app = app @adapter = RackAdapter.new(*args) end def call(env) status, headers, body = *@adapter.call(env) if status == 404 @app.call(env) else [status, headers, body] end end end # A server adapter to respond to requests using the Rack server infrastructure. class RackAdapter < Adapter include YARD::Server::HTTPUtils # Responds to Rack requests and builds a response with the {Router}. # @return [Array(Numeric,Hash,Array)] the Rack-style response def call(env) request = Rack::Request.new(env) request.path_info = unescape(request.path_info) # unescape things like %3F router.call(request) rescue StandardError => ex log.backtrace(ex) [500, {'Content-Type' => 'text/plain'}, [ex.message + "\n" + ex.backtrace.join("\n")]] end # Starts the Rack server. This method will pass control to the server and # block. # @return [void] def start server = RackServer.new(server_options) server.instance_variable_set("@app", self) print_start_message(server) server.start end private def print_start_message(server) opts = server.default_options.merge(server.options) log.puts ">> YARD #{YARD::VERSION} documentation server at http://#{opts[:Host]}:#{opts[:Port]}" # Only happens for Mongrel return unless server.server.to_s == "Rack::Handler::Mongrel" log.puts ">> #{server.server.class_name} web server (running on Rack)" log.puts ">> Listening on #{opts[:Host]}:#{opts[:Port]}, CTRL+C to stop" end end end end # @private class Rack::Request attr_accessor :version_supplied alias query params def xhr?; (env['HTTP_X_REQUESTED_WITH'] || "").casecmp("xmlhttprequest") == 0 end end