Class: Rackful::Server

Inherits:
Object
  • Object
show all
Defined in:
gems/rackful-0.1.1/lib/rackful_server.rb,
gems/rackful-0.1.1.orig/lib/rackful_server.rb

Overview

Rack compliant server class for implementing RESTful web services.

Since:

Instance Attribute Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (Server) initialize(resource_factory)

An object responding thread safely to method `#[]`.

A Rackful::Server has no knowledge, and makes no presumptions, about your URI namespace. It requires a _Resource Factory_ which produces Resources given a certain absolute path.

The Resource Factory you provide need only implement one method, with signature `Resource #[]( String path )`. This method will be called with a URI-encoded path string, and must return a Resource, or `nil` if there’s no resource at the given path.

For example, if a Rackful client tries to access a resource with URI http://example.com/some/resource, then your Resource Factory can expect to be called like this:


    resource = resource_factory[ '/your/resource' ]

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

Since:

  • 0.0.1



47
48
49
50
# File 'gems/rackful-0.1.1/lib/rackful_server.rb', line 47

def initialize(resource_factory)
  super()
  @resource_factory = resource_factory
end

Instance Attribute Details

- (#[]) resource_factory (readonly)

An object responding thread safely to method `#[]`.

A Rackful::Server has no knowledge, and makes no presumptions, about your URI namespace. It requires a _Resource Factory_ which produces Resources given a certain absolute path.

The Resource Factory you provide need only implement one method, with signature `Resource #[]( String path )`. This method will be called with a URI-encoded path string, and must return a Resource, or `nil` if there’s no resource at the given path.

For example, if a Rackful client tries to access a resource with URI http://example.com/some/resource, then your Resource Factory can expect to be called like this:


    resource = resource_factory[ '/your/resource' ]

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

Returns:

  • (#[])

See Also:

Since:

  • 0.0.1



40
41
42
# File 'gems/rackful-0.1.1/lib/rackful_server.rb', line 40

def resource_factory
  @resource_factory
end

Instance Method Details

- (Array<(status_code, response_headers, response_body)>) call(p_env)

As required by the Rack specification.

For thread safety, this method clones `self`, which handles the request in #call!. A similar approach is taken by the Sinatra library.

Returns:

  • (Array<(status_code, response_headers, response_body)>)

Since:

  • 0.0.1



61
62
63
64
65
66
# File 'gems/rackful-0.1.1/lib/rackful_server.rb', line 61

def call(p_env)
  start = Time.now
  retval = dup.call! p_env
  #$stderr.puts( 'Duration: ' + ( Time.now - start ).to_s )
  retval
end

- (Array<(status_code, response_headers, response_body)>) call!(p_env)

Returns:

  • (Array<(status_code, response_headers, response_body)>)

Since:

  • 0.0.1



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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'gems/rackful-0.1.1/lib/rackful_server.rb', line 73

def call!(p_env)
  request = Request.new( self.resource_factory, p_env )
  # See also Request::current():
  Thread.current[:rackful_request] = request
  response = Rack::Response.new
  begin
    raise HTTP404NotFound \
      unless resource = self.resource_factory[Path.new(request.path)]
    unless resource.path == request.path
      response.header['Content-Location'] = resource.path
      request.content_path = resource.path
    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
    # Already handled by HTTPStatus#initialize:
    #raise if $DEBUG && 500 <= e.status
    bct = e.class.best_content_type request.accept, false
    serializer = e.serializer(bct)
    response = Rack::Response.new serializer, e.status, e.headers
  ensure
    # 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
  end
  if  201 == response.status &&
      ( location = response['Location'] ) &&
      ( new_resource = request.resource_factory[location] ) &&
      ! new_resource.empty? \
  or  ( (200...300) === response.status ||
         304        ==  response.status ) &&
      ! response['Location'] &&
      ( new_resource = request.resource_factory[request.path] ) &&
      ! new_resource.empty?
    response.headers.merge! new_resource.default_headers
  end
  r = response.finish
  #~ $stderr.puts r.inspect
  r
end