rackful 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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