class YARD::Server::Commands::Base

This is the base command class used to implement custom commands for a server. A command will be routed to by the {Router} class and return a Rack-style response.

Attribute Initializers

All attributes can be initialized via options passed into the {#initialize} method. When creating a custom command, the {Adapter#options} will automatically be mapped to attributes by the same name on your class.

class MyCommand < Base
  attr_accessor :myattr
end

Adapter.new(libs, {:myattr => 'foo'}).start

# when a request comes in, cmd.myattr == 'foo'

Subclassing Notes

To implement a custom command, override the {#run} method, not {#call}. In your implementation, you should set the body and status for requests. See details in the #run method documentation.

Note that if your command deals directly with libraries, you should consider subclassing the more specific {LibraryCommand} class instead.

@abstract @see run

Attributes

adapter[RW]

@return [Adapter] the server adapter

body[RW]

@return [String] the response body. Defaults to empty string.

caching[RW]

@return [Boolean] whether to cache

command_options[RW]

@return [Hash] the options passed to the command's constructor

headers[RW]

@return [Hash{String => String}] response headers

path[RW]

@return [String] the path after the command base URI

request[RW]

@return [Request] request object

status[RW]

@return [Numeric] status code. Defaults to 200 per request

Public Class Methods

new(opts = {}) click to toggle source

Creates a new command object, setting attributes named by keys in the options hash. After initialization, the options hash is saved in {#command_options} for further inspection.

@example Creating a Command

cmd = DisplayObjectCommand.new(:caching => true, :library => mylib)
cmd.library # => mylib
cmd.command_options # => {:caching => true, :library => mylib}

@param [Hash] opts the options hash, saved to {#command_options}

after initialization.
# File lib/yard/server/commands/base.rb, line 75
def initialize(opts = {})
  opts.each do |key, value|
    send("#{key}=", value) if respond_to?("#{key}=")
  end
  self.command_options = opts
end

Public Instance Methods

call(request) click to toggle source

The main method called by a router with a request object.

@note This command should not be overridden by subclasses. Implement

the callback method {#run} instead.

@param [Adapter Dependent] request the request object @return [Array(Numeric,Hash,Array<String>)] a Rack-style response

of status, headers, and body wrapped in an array.
# File lib/yard/server/commands/base.rb, line 89
def call(request)
  self.request = request
  self.path ||= request.path_info[1..-1]
  self.headers = {'Content-Type' => 'text/html'}
  self.body = ''
  self.status = 200
  add_cache_control
  begin
    run
  rescue FinishRequest
    nil # noop
  rescue NotFoundError => e
    self.body = e.message if e.message != e.class.to_s
    not_found
  end

  # keep this to support commands setting status manually.
  not_found if status == 404

  [status, headers, body.is_a?(Array) ? body : [body]]
end
run() click to toggle source

Subclass this method to implement a custom command. This method should set the {#status} and {#body}, and optionally modify the {#headers}. Note that #status defaults to 200.

@example A custom command

class ErrorCommand < Base
  def run
    self.body = 'ERROR! The System is down!'
    self.status = 500
    self.headers['Conten-Type'] = 'text/plain'
  end
end

@abstract @return [void]

# File lib/yard/server/commands/base.rb, line 128
def run
  raise NotImplementedError
end

Protected Instance Methods

cache(data) click to toggle source

Override this method to implement custom caching mechanisms for

@example Caching to memory

$memory_cache = {}
def cache(data)
  $memory_cache[path] = data
end

@param [String] data the data to cache @return [String] the same cached data (for chaining) @see StaticCaching

# File lib/yard/server/commands/base.rb, line 165
def cache(data)
  if caching && adapter.document_root
    path = File.join(adapter.document_root, request.path_info.sub(/\.html$/, '') + '.html')
    path = path.sub(%r{/\.html$}, '.html')
    FileUtils.mkdir_p(File.dirname(path))
    log.debug "Caching data to #{path}"
    File.open(path, 'wb') {|f| f.write(data) }
  end
  self.body = data
end
not_found() click to toggle source

Sets the body and headers for a 404 response. Does not modify the body if already set.

@return [void]

# File lib/yard/server/commands/base.rb, line 180
def not_found
  self.status = 404
  return unless body.empty?
  self.body = "Not found: #{request.path}"
  headers['Content-Type'] = 'text/plain'
  headers['X-Cascade'] = 'pass'
  headers.delete('Cache-Control')
end
redirect(url) click to toggle source

Sets the headers and status code for a redirection to a given URL @param [String] url the URL to redirect to @raise [FinishRequest] causes the request to terminate.

# File lib/yard/server/commands/base.rb, line 192
def redirect(url)
  headers['Location'] = url
  self.status = 302
  raise FinishRequest
end
render(object = nil) click to toggle source

Renders a specific object if provided, or a regular template rendering if object is not provided.

@todo This method is dependent on #options, it should be in {LibraryCommand}. @param [CodeObjects::Base, nil] object calls {CodeObjects::Base#format} if

an object is provided, or {Templates::Engine.render} if object is nil. Both
receive +#options+ as an argument.

@return [String] the resulting output to display

# File lib/yard/server/commands/base.rb, line 144
def render(object = nil)
  case object
  when CodeObjects::Base
    cache object.format(options)
  when nil
    cache Templates::Engine.render(options)
  else
    cache object
  end
end

Private Instance Methods

add_cache_control() click to toggle source

Add a conservative cache control policy to reduce load on requests served with “?1234567890” style timestamp query strings.

# File lib/yard/server/commands/base.rb, line 202
def add_cache_control
  return if request.query_string.to_i == 0
  headers['Cache-Control'] = 'private, max-age=300'
end