rackful 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,17 +1,26 @@
1
1
  # encoding: utf-8
2
2
 
3
+ # Required for parsing:
4
+ require 'rackful/global.rb'
3
5
 
4
- module Rackful
6
+ # Required for running:
5
7
 
6
8
 
9
+ module Rackful
7
10
 
8
11
  # Base class for all serializers.
9
12
  #
10
13
  # The serializers {Serializer::XHTML} and {Serializer::JSON} defined in this
11
14
  # library depend on the presence of method
12
15
  # {Rackful::Resource#to_rackful resource.to_rackful}.
13
- # @abstract Subclasses must implement method `#each` end define constant
14
- # `CONTENT_TYPES`
16
+ # @abstract Subclasses must implement method `#each` and call {::produces}
17
+ # @example Create a new Serializer subclass:
18
+ # class MySerializer < Rackful::Serializer
19
+ # produces 'text/plain'
20
+ # def each
21
+ # yield 'Hello world!'
22
+ # end
23
+ # end
15
24
  # @!attribute [r] request
16
25
  # @return [Request]
17
26
  # @!attribute [r] resource
@@ -22,7 +31,35 @@ module Rackful
22
31
  class Serializer
23
32
 
24
33
 
34
+ class << self
35
+ def content_types
36
+ @content_types ||= begin
37
+ retval = rackful_serializer_content_types
38
+ if superclass.respond_to?(:content_types)
39
+ retval += superclass.content_types
40
+ end
41
+ retval.uniq
42
+ end
43
+ end
44
+
45
+ # @overload parses( media_type, ... )
46
+ # @param media_type [String]
47
+ def produces *args
48
+ rackful_serializer_content_types.unshift(
49
+ *( args.map { |ct| ct.to_s }.reverse )
50
+ )
51
+ rackful_serializer_content_types.uniq!
52
+ self
53
+ end
54
+
55
+ private
56
+ def rackful_serializer_content_types
57
+ @rackful_serializer_content_types ||= []
58
+ end
59
+ end # class << self # Rackful::Serializer
60
+
25
61
  include Enumerable
62
+ include StatusCodes
26
63
 
27
64
 
28
65
  attr_reader :request, :resource, :content_type
@@ -63,12 +100,10 @@ class Serializer::XHTML < Serializer
63
100
 
64
101
  # The content types served by this serializer.
65
102
  # @see Serializer::CONTENT_TYPES
66
- CONTENT_TYPES = [
67
- 'application/xml; charset=UTF-8',
68
- 'text/xml; charset=UTF-8',
69
- 'text/html; charset=UTF-8',
70
- 'application/xhtml+xml; charset=UTF-8',
71
- ]
103
+ produces 'application/xml; charset=UTF-8',
104
+ 'text/xml; charset=UTF-8',
105
+ 'text/html; charset=UTF-8',
106
+ 'application/xhtml+xml; charset=UTF-8'
72
107
 
73
108
  # @api private
74
109
  # @return [URI::HTTP]
@@ -86,9 +121,7 @@ class Serializer::XHTML < Serializer
86
121
  tmp = ''
87
122
  # The XML header is only sent for XML media types:
88
123
  if /xml/ === self.content_type
89
- tmp += <<EOS
90
- <?xml version="1.0" encoding="UTF-8"?>
91
- EOS
124
+ tmp += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
92
125
  end
93
126
  tmp += <<EOS
94
127
  <!DOCTYPE html>
@@ -271,10 +304,7 @@ end # class Serializer::XHTML
271
304
  class Serializer::JSON < Serializer
272
305
 
273
306
 
274
- CONTENT_TYPES = [
275
- 'application/json',
276
- 'application/x-json'
277
- ]
307
+ produces 'application/json', 'application/x-json'
278
308
 
279
309
 
280
310
  # @yield [json]
@@ -312,5 +342,4 @@ class Serializer::JSON < Serializer
312
342
 
313
343
  end # class Serializer::JSON
314
344
 
315
-
316
345
  end # module Rackful
@@ -1,7 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ # Required for parsing:
4
+ require 'rackful/global.rb'
5
+
6
+ # Required for running:
7
+
8
+
1
9
  module Rackful
2
10
 
3
11
  # Rack compliant server class for implementing RESTful web services.
4
- class Server
12
+ class Rackful::Server
13
+
14
+ include StatusCodes
5
15
 
6
16
 
7
17
  # Constructor.
@@ -21,57 +31,36 @@ class Server
21
31
  # @yieldparam uri [URI::Generic] The {URI::Generic::normalize! normalized}
22
32
  # URI of the requested resource.
23
33
  # @yieldreturn [Resource] A (possibly {Resource#empty? empty}) resource, or nil.
24
- def initialize( &resource_registry )
34
+ def initialize &resource_registry
25
35
  @resource_registry = resource_registry
26
36
  end
27
37
 
28
38
 
29
- # Calls the code block passed to the {#initialize constructor}.
30
- # @param uri [URI::HTTP, String]
31
- # @return [Resource]
32
- # @raise [HTTP404NotFound]
33
- def resource_at(uri)
34
- uri = URI(uri) unless uri.kind_of?( URI::Generic )
35
- retval = @resource_registry.call( uri.normalize )
36
- raise HTTP404NotFound unless retval
37
- retval
38
- end
39
-
40
-
41
39
  # As required by the Rack specification.
42
40
  #
43
- # For thread safety, this method clones `self`, which handles the request in
44
- # {#call!}.
45
- # For reentrancy, the clone is stored in the environment.
46
41
  # @param env [{String => Mixed}]
47
42
  # @return [(status_code, response_headers, response_body)]
48
43
  def call( env )
49
- ( env['rackful.server'] ||= self.dup ).call!( env )
50
- end
51
-
52
-
53
- # @see #call
54
- # @return [(status_code, response_headers, response_body)]
55
- def call!( env )
44
+ env['rackful.resource_registry'] ||= @resource_registry
56
45
  request = Request.new( env )
57
46
  response = Rack::Response.new
58
47
  begin
59
- resource = resource_at( request.url )
60
- request.canonical_uri = resource.uri
48
+ resource = request.resource
61
49
  if request.url != request.canonical_uri.to_s
62
50
  if %w{HEAD GET}.include?( request.request_method )
51
+ raise HTTP404NotFound if resource.empty?
63
52
  raise HTTP301MovedPermanently, request.canonical_uri
64
53
  end
65
54
  response.header['Content-Location'] = request.canonical_uri.to_s
66
55
  end
67
- request.assert_if_headers resource
68
- if %w{HEAD GET OPTIONS PUT DELETE}.include?( request.request_method )
56
+ request.assert_if_headers
57
+ if %w{HEAD GET OPTIONS PATCH POST PUT DELETE}.include?( request.request_method )
69
58
  resource.__send__( :"http_#{request.request_method}", request, response )
70
59
  else
71
60
  resource.http_method request, response
72
61
  end
73
62
  rescue HTTPStatus => e
74
- serializer = e.serializer(request)
63
+ serializer = e.serializer(request, false)
75
64
  response = Rack::Response.new
76
65
  response['Content-Type'] = serializer.content_type
77
66
  response.status = e.status
@@ -88,22 +77,25 @@ class Server
88
77
  begin
89
78
  if 201 == response.status &&
90
79
  ( location = response['Location'] ) &&
91
- ( new_resource = resource_at( location ) ) &&
80
+ ( new_resource = request.resource_at( location ) ) &&
92
81
  ! new_resource.empty? \
93
82
  or ( (200...300) === response.status ||
94
83
  304 == response.status ) &&
95
84
  ! response['Location'] &&
96
- ( new_resource = resource_at( request.canonical_uri ) ) &&
85
+ ( new_resource = request.resource_at( request.canonical_uri ) ) &&
97
86
  ! new_resource.empty?
98
87
  response.headers.merge! new_resource.default_headers
99
88
  end
89
+ # Make sure the Location: response header contains an absolute URI:
90
+ if response['Location'] and response['Location'][0] == ?/
91
+ response['Location'] = request.canonical_uri + response['Location']
92
+ end
100
93
  rescue HTTP404NotFound => e
101
94
  end
102
95
  response.finish
103
96
  end
104
97
 
105
98
 
106
- end # class Server
107
-
99
+ end # class Rackful::Server
108
100
 
109
101
  end # module Rackful
data/lib/rackful/uri.rb CHANGED
@@ -1,3 +1,11 @@
1
+ # encoding: utf-8
2
+ # This file is required by `src/global.rb`.
3
+
4
+ # Required for parsing:
5
+ require 'uri'
6
+
7
+ # Required for running:
8
+
1
9
  # Extension and monkeypatch of Ruby’s StdLib URI::Generic class.
2
10
  class URI::Generic
3
11
 
data/rackful.gemspec CHANGED
@@ -2,7 +2,7 @@ Gem::Specification.new do |s|
2
2
 
3
3
  # Required properties:
4
4
  s.name = 'rackful'
5
- s.version = '0.2.0'
5
+ s.version = '0.2.1'
6
6
  s.summary = "Library for building ReSTful web services with Rack"
7
7
  s.description = <<EOS
8
8
  Rackful provides a minimal interface for developing ReSTful web services with
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rackful
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pieter van Beek
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-14 00:00:00.000000000 Z
11
+ date: 2014-03-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -55,6 +55,7 @@ files:
55
55
  - README.md
56
56
  - example/config.ru
57
57
  - lib/rackful.rb
58
+ - lib/rackful/global.rb
58
59
  - lib/rackful/httpstatus.rb
59
60
  - lib/rackful/middleware.rb
60
61
  - lib/rackful/middleware/headerspoofing.rb