Class: Xenon::API
Constant Summary
collapse
- DEFAULT_MARSHALLERS =
[JsonMarshaller.new]
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
#authenticate, #authorize, #optional_authenticate
#complete, #extract, #extract_request, #fail, #map_request, #map_response, #reject
#path, #path_end, #path_prefix
#param_hash, #params
#get, #request_method
#body
#header, #optional_header, #respond_with_header
Instance Attribute Details
#context ⇒ Object
Returns the value of attribute context.
30
31
32
|
# File 'lib/xenon/api.rb', line 30
def context
@context
end
|
Class Method Details
.marshallers(*marshallers) ⇒ Object
12
13
14
15
|
# File 'lib/xenon/api.rb', line 12
def marshallers(*marshallers)
@marshallers = marshallers unless marshallers.nil? || marshallers.empty?
(@marshallers.nil? || @marshallers.empty?) ? DEFAULT_MARSHALLERS : @marshallers
end
|
.method_missing(name, *args, &block) ⇒ Object
37
38
39
40
41
42
43
|
# File 'lib/xenon/api.rb', line 37
def method_missing(name, *args, &block)
if instance_methods.include?(name)
routes << [name, args, block]
else
super
end
end
|
.request_marshaller(content_type) ⇒ Object
17
18
19
|
# File 'lib/xenon/api.rb', line 17
def request_marshaller(content_type)
marshallers.find { |m| m.unmarshal?(content_type.media_type) }
end
|
.response_marshaller(media_ranges) ⇒ Object
21
22
23
24
25
26
27
|
# File 'lib/xenon/api.rb', line 21
def response_marshaller(media_ranges)
weighted = marshallers.map do |marshaller|
media_range = media_ranges.find { |media_range| marshaller.marshal?(media_range) }
[marshaller, media_range ? media_range.q : 0]
end
weighted.select { |_, q| q > 0 }.sort_by { |_, q| q }.map { |m, _| m }.last
end
|
.routes ⇒ Object
33
34
35
|
# File 'lib/xenon/api.rb', line 33
def routes
@routes ||= []
end
|
Instance Method Details
#call(env) ⇒ Object
46
47
48
|
# File 'lib/xenon/api.rb', line 46
def call(env)
dup.call!(env)
end
|
#call!(env) ⇒ Object
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
|
# File 'lib/xenon/api.rb', line 50
def call!(env)
@context = Routing::Context.new(Request.new(Rack::Request.new(env)), Response.new)
accept = @context.request.('Accept')
response_marshaller = accept ? self.class.response_marshaller(accept.media_ranges) : self.class.marshallers.first
catch :complete do
begin
if response_marshaller
self.class.routes.each do |route|
name, args, block = route
route_block = proc { instance_eval(&block) }
send(name, *args, &route_block)
end
else
reject :accept, supported: self.class.marshallers.map(&:media_type)
end
handle_rejections(@context.rejections)
rescue => e
handle_error(e)
end
end
response_marshaller ||= self.class.marshallers.first
= @context.response..set(::ContentType.new(response_marshaller.content_type))
if @context.request.allow_response_body?
body = response_marshaller.marshal(@context.response.body)
else
body = []
end
resp = @context.response.copy(headers: , body: body)
[resp.status, resp..map { |h| [h.name, h.to_s] }.to_h, resp.body]
end
|
#handle_error(e) ⇒ Object
85
86
87
88
89
90
91
92
93
|
# File 'lib/xenon/api.rb', line 85
def handle_error(e)
puts "handle_error: #{e.class}: #{e}\n #{e.backtrace.join("\n ")}"
case e
when ParseError
fail 400, e.message
else
fail 500, e.message
end
end
|
#handle_rejections(rejections) ⇒ Object
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
|
# File 'lib/xenon/api.rb', line 95
def handle_rejections(rejections)
puts "handle_rejections: #{rejections}"
if rejections.empty?
fail 404
else
rejection = rejections.first
case rejection.reason
when :accept
fail 406, "Supported media types: #{rejection[:supported].join(", ")}"
when :forbidden
fail 403
when :header
fail 400, "Missing required header: #{rejection[:required]}"
when :method
supported = rejections.take_while { |r| r.reason == :method }.flat_map { |r| r[:supported] }
fail 405, "Supported methods: #{supported.map(&:upcase).join(", ")}"
when :unauthorized
if rejection[:scheme]
challenge = ::Challenge.new(rejection[:scheme], rejection.info.except(:scheme))
::WWWAuthenticate.new(challenge) do
fail 401
end
else
fail 401
end
else
fail 500
end
end
end
|