rackful 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|