Class: Rackful::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/rackful/server.rb

Overview

Rack compliant server class for implementing RESTful web services.

Instance Method Summary (collapse)

Constructor Details

- (Server) initialize {|uri| ... }

Constructor.

This generic server class has no knowledge, and makes no presumptions, about your URI namespace. It depends on the code block you provide here to produce the Resource object which lives at a certain URI. This block will be called with a normalized URI, and must return a Resource, or nil if there’s no resource at the given URI.

If there’s no resource at the given URI, but you’d still like to respond to POST or PUT requests to this URI, you can return an empty resource.

The provided code block must be thread-safe and reentrant.

Yield Parameters:

  • uri (URI::Generic)

    The normalized URI of the requested resource.

Yield Returns:



24
25
26
# File 'lib/rackful/server.rb', line 24

def initialize( &resource_registry )
  @resource_registry = resource_registry
end

Instance Method Details

- ((status_code, response_headers, response_body)) call(env)

As required by the Rack specification.

For thread safety, this method clones self, which handles the request in #call!. For reentrancy, the clone is stored in the environment.

Parameters:

  • env ({String => Mixed})

Returns:

  • ((status_code, response_headers, response_body))


48
49
50
# File 'lib/rackful/server.rb', line 48

def call( env )
  ( env['rackful.server'] ||= self.dup ).call!( env )
end

- ((status_code, response_headers, response_body)) call!(env)

Returns:

  • ((status_code, response_headers, response_body))

See Also:



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/rackful/server.rb', line 55

def call!( env )
  request = Request.new( env )
  response = Rack::Response.new
  begin
    resource = resource_at( request.url )
    request.canonical_uri = resource.uri
    if request.url != request.canonical_uri.to_s
      if %w{HEAD GET}.include?( request.request_method )
        raise HTTP301MovedPermanently, request.canonical_uri
      end
      response.header['Content-Location'] = request.canonical_uri.to_s
    end
    request.assert_if_headers resource
    if %w{HEAD GET OPTIONS PUT DELETE}.include?( request.request_method )
      resource.__send__( :http_#{request.request_method}", request, response )
    else
      resource.http_method request, response
    end
  rescue HTTPStatus => e
    serializer = e.serializer(request)
    response = Rack::Response.new
    response['Content-Type'] = serializer.content_type
    response.status = e.status
    if serializer.respond_to? :headers
      response.headers.merge!( serializer.headers )
    end
    response.body = serializer
  end
  # The next line fixes a small peculiarity in RFC2616: the response body of
  # a `HEAD` request _must_ be empty, even for responses outside 2xx.
  if request.head?
    response.body = []
  end
  begin
    if  201 == response.status &&
        ( location = response['Location'] ) &&
        ( new_resource = resource_at( location ) ) &&
        ! new_resource.empty?     \
or  ( (200...300) === response.status ||
           304        ==  response.status ) &&
        ! response['Location'] &&
        ( new_resource = resource_at( request.canonical_uri ) ) &&
        ! new_resource.empty?
      response.headers.merge! new_resource.default_headers
    end
  rescue HTTP404NotFound => e
  end
  response.finish
end

- (Resource) resource_at(uri)

Calls the code block passed to the constructor.

Parameters:

  • uri (URI::HTTP, String)

Returns:

Raises:



33
34
35
36
37
38
# File 'lib/rackful/server.rb', line 33

def resource_at(uri)
  uri = URI(uri) unless uri.kind_of?( URI::Generic )
  retval = @resource_registry.call( uri.normalize )
  raise HTTP404NotFound unless retval
  retval
end