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.
- checksums.yaml +4 -4
- data/example/config.ru +11 -10
- data/lib/rackful.rb +9 -5
- data/lib/rackful/global.rb +12 -0
- data/lib/rackful/httpstatus.rb +50 -51
- data/lib/rackful/middleware.rb +5 -0
- data/lib/rackful/middleware/headerspoofing.rb +7 -1
- data/lib/rackful/middleware/methodoverride.rb +8 -2
- data/lib/rackful/parser.rb +80 -101
- data/lib/rackful/request.rb +100 -129
- data/lib/rackful/resource.rb +263 -207
- data/lib/rackful/serializer.rb +46 -17
- data/lib/rackful/server.rb +25 -33
- data/lib/rackful/uri.rb +8 -0
- data/rackful.gemspec +1 -1
- metadata +3 -2
data/lib/rackful/serializer.rb
CHANGED
@@ -1,17 +1,26 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
# Required for parsing:
|
4
|
+
require 'rackful/global.rb'
|
3
5
|
|
4
|
-
|
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`
|
14
|
-
#
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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 +=
|
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
|
-
|
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
|
data/lib/rackful/server.rb
CHANGED
@@ -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
|
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
|
-
|
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 =
|
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
|
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
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.
|
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.
|
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-
|
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
|