/
marshal.rb
134 lines (111 loc) · 3.73 KB
/
marshal.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#Request builder for marshalled data that unmarshalls content after receiving it.
module Restfulie::Client::HTTP
module ResponseHolder
attr_accessor :response
def respond_to?(symbol)
respond_to_rel?(symbol.to_s) || super(symbol)
end
private
# whether this response contains specific relations
def respond_to_rel?(rel)
links.any? { |link| link.rel==rel }
end
end
module RequestMarshaller
include ::Restfulie::Client::HTTP::RequestBuilder
# accepts a series of media types by default
def intialize
@acceptable_mediatypes = "application/atom+xml"
end
@@representations = {
'application/atom+xml' => ::Restfulie::Common::Representation::Atom
}
def self.register_representation(media_type,representation)
@@representations[media_type] = representation
end
def accepts(media_types)
@acceptable_mediatypes = media_types
@default_representation = @@representations[media_types]
raise "Undefined representation for #{content_type}" unless @default_representation
super
end
def raw
@raw = true
self
end
#Executes super and unmarshalls it
def request!(method, path, *args)
representation = do_conneg_and_choose_representation(method, path, *args)
if representation
if has_payload?(method, path, *args)
payload = get_payload(method, path, *args)
payload = representation.marshal(payload)
args = set_marshalled_payload(method, path, payload, *args)
end
args = add_representation_headers(method, path, representation, *args)
end
response = super(method, path, *args)
parse_response(response, representation)
end
private
# parses the http response.
# first checks if its a 201, redirecting to the resource location.
# otherwise check if its a raw request, returning the content itself.
# finally, tries to parse the content with a mediatype handler or returns the response itself.
def parse_response(response, representation)
if response.code != 200
location = response.headers['location']
Restfulie.at(location).accepts(@acceptable_mediatypes).get!
elsif @raw
response
elsif representation
unmarshalled = representation.unmarshal(response.body)
unmarshalled.extend(ResponseHolder)
unmarshalled.response = response
unmarshalled
else
response
end
end
def do_conneg_and_choose_representation(method, path, *args)
#TODO make a request to server (conneg)
representation = @default_representation || @default_representation = @@representations.values.first
representation.new
end
def has_payload?(method, path, *args)
[:put,:post].include?(method)
end
def get_payload(method, path, *args)
args.extract_options! #remove header
args.shift #payload
end
def set_marshalled_payload(method, path, payload, *args)
headers = args.extract_options!
args.shift #old payload
args << payload << headers
args
end
def add_representation_headers(method, path, representation, *args)
headers = args.extract_options!
args << representation.headers[method]
args
end
end
#=This class includes RequestBuilder module.
class RequestMarshallerExecutor
include RequestMarshaller
# * <tt> host (e.g. 'http://restfulie.com') </tt>
# * <tt> default_headers (e.g. {'Cache-control' => 'no-cache'} ) </tt>
def initialize(host, default_headers = {})
self.host=host
self.default_headers=default_headers
end
def at(path)
@path = path
self
end
def path
@path
end
end
end