class Roda
module RodaPlugins
# The not_allowed plugin makes Roda attempt to automatically
# support the 405 Method Not Allowed response status. The plugin
# changes the +r.get+ and +r.post+ verb methods to automatically
# return a 405 status if they are called with any arguments, and
# the arguments match but the request method does not match. So
# this code:
#
# r.get '' do
# "a"
# end
#
# will return a 200 response for GET / and a 405
# response for POST /.
#
# This plugin also changes the +r.is+ method so that if you use
# a verb method inside +r.is+, it returns a 405 status if none
# of the verb methods match. So this code:
#
# r.is '' do
# r.get do
# "a"
# end
#
# r.post do
# "b"
# end
# end
#
# will return a 200 response for GET / and POST /,
# but a 405 response for PUT /.
#
# Note that this plugin will probably not do what you want for
# code such as:
#
# r.get '' do
# "a"
# end
#
# r.post '' do
# "b"
# end
#
# Since for a POST / request, when +r.get+ method matches
# the path but not the request method, it will return an immediate
# 405 response. You must DRY up this code for it work correctly,
# like this:
#
# r.is '' do
# r.get do
# "a"
# end
#
# r.post do
# "b"
# end
# end
#
# In all cases where it uses a 405 response, it also sets the +Allow+
# header in the response to contain the request methods supported.
#
# To make this affect the verb methods added by the all_verbs plugin,
# load this plugin first.
module NotAllowed
# Redefine the +r.get+ and +r.post+ methods when loading the plugin.
def self.configure(app)
app.request_module do
app::RodaRequest.def_verb_method(self, :get)
app::RodaRequest.def_verb_method(self, :post)
end
end
module RequestClassMethods
# Define a method named +verb+ in the given module which will
# return a 405 response if the method is called with any
# arguments and the arguments terminally match but the
# request method does not.
#
# If called without any arguments, check to see if the call
# is inside a terminal match, and in that case record the
# request method used.
def def_verb_method(mod, verb)
mod.class_eval(<<-END, __FILE__, __LINE__+1)
def #{verb}(*args, &block)
if args.empty?
@_is_verbs << "#{verb.to_s.upcase}" if @_is_verbs
always(&block) if #{verb == :get ? :is_get : verb}?
else
args << ::Roda::RodaPlugins::Base::RequestMethods::TERM
if_match(args) do |*args|
if #{verb == :get ? :is_get : verb}?
block_result(yield(*args))
throw :halt, response.finish
end
response.status = 405
response['Allow'] = '#{verb.to_s.upcase}'
nil
end
end
end
END
end
end
module RequestMethods
# Keep track of verb calls inside the block. If there are any
# verb calls inside the block, but the block returned, assume
# that the verb calls inside the block did not match, and
# since there was already a successful terminal match, the
# request method must not be allowed, so return a 405
# response in that case.
def is(*verbs)
super(*verbs) do
begin
@_is_verbs = []
ret = if verbs.empty?
yield
else
yield(*captures)
end
unless @_is_verbs.empty?
response.status = 405
response['Allow'] = @_is_verbs.join(', ')
end
ret
ensure
@_is_verbs = nil
end
end
end
end
end
register_plugin(:not_allowed, NotAllowed)
end
end