lib/roda.rb in roda-cj-0.9.3 vs lib/roda.rb in roda-cj-0.9.4
- old
+ new
@@ -299,10 +299,21 @@
end
pattern
end
+ # Define a verb method in the given that will yield to the match block
+ # if the request method matches and there are either no arguments or
+ # there is a successful terminal match on the arguments.
+ def def_verb_method(mod, verb)
+ mod.class_eval(<<-END, __FILE__, __LINE__+1)
+ def #{verb}(*args, &block)
+ _verb(args, &block) if #{verb == :get ? :is_get : verb}?
+ end
+ END
+ end
+
# Since RodaRequest is anonymously subclassed when Roda is subclassed,
# and then assigned to a constant of the Roda subclass, make inspect
# reflect the likely name for the class.
def inspect
"#{roda_class.inspect}::RodaRequest"
@@ -312,11 +323,11 @@
# The pattern to use for consuming, based on the given argument. The returned
# pattern requires the path starts with a string and does not match partial
# segments.
def consume_pattern(pattern)
- /\A(\/(?:#{pattern}))(\/|\z)/
+ /\A(\/(?:#{pattern}))(?=\/|\z)/
end
end
# Instance methods for RodaRequest, mostly related to handling routing
# for the request.
@@ -325,12 +336,12 @@
SCRIPT_NAME = "SCRIPT_NAME".freeze
REQUEST_METHOD = "REQUEST_METHOD".freeze
EMPTY_STRING = "".freeze
SLASH = "/".freeze
SEGMENT = "([^\\/]+)".freeze
- EMPTY_ARRAY = [].freeze
TERM_INSPECT = "TERM".freeze
+ GET_REQUEST_METHOD = 'GET'.freeze
TERM = Object.new
def TERM.inspect
TERM_INSPECT
end
@@ -352,28 +363,28 @@
end
# As request routing modifies SCRIPT_NAME and PATH_INFO, this exists
# as a helper method to get the full request of the path info.
def full_path_info
- "#{env[SCRIPT_NAME]}#{env[PATH_INFO]}"
+ "#{@env[SCRIPT_NAME]}#{@env[PATH_INFO]}"
end
- # If this is not a GET method, returns immediately. Otherwise, if there
- # are arguments, do a terminal match on the arguments, otherwise do a
- # regular match.
- def get(*args, &block)
- _verb(args, &block) if get?
- end
-
# Immediately stop execution of the route block and return the given
# rack response array of status, headers, and body. If no argument
# is given, uses the current response.
def halt(res=response.finish)
throw :halt, res
end
- # Handle #on block return values. By default, if a string is given
+ # Whether this request is a get request. Similar to the default
+ # Rack::Request get? method, but can be overridden without changing
+ # rack's behavior.
+ def is_get?
+ @env[REQUEST_METHOD] == GET_REQUEST_METHOD
+ end
+
+ # Handle match block return values. By default, if a string is given
# and the response is empty, use the string as the response body.
def block_result(result)
res = response
if res.empty? && (body = block_result_body(result))
res.write(body)
@@ -381,36 +392,39 @@
end
# Show information about current request, including request class,
# request method and full path.
def inspect
- "#<#{self.class.inspect} #{env[REQUEST_METHOD]} #{full_path_info}>"
+ "#<#{self.class.inspect} #{@env[REQUEST_METHOD]} #{full_path_info}>"
end
- # Adds TERM as the final argument and passes to #on, ensuring that
- # there is only a match if #on has fully matched the path.
+ # Does a terminal match on the input, matching only if the arguments
+ # have fully matched the patch.
def is(*args, &block)
- args << TERM
- _on(args, &block)
+ if args.empty?
+ if @env[PATH_INFO] == EMPTY_STRING
+ always(&block)
+ end
+ else
+ args << TERM
+ if_match(args, &block)
+ end
end
# Attempts to match on all of the arguments. If all of the
# arguments match, control is yielded to the block, and after
# the block returns, the rack response will be returned.
# If any of the arguments fails, ensures the request state is
# returned to that before matches were attempted.
def on(*args, &block)
- _on(args, &block)
+ if args.empty?
+ always(&block)
+ else
+ if_match(args, &block)
+ end
end
- # If this is not a GET method, returns immediately. Otherwise, if there
- # are arguments, do a terminal match on the arguments, otherwise do a
- # regular match.
- def post(*args, &block)
- _verb(args, &block) if post?
- end
-
# The response related to the current request.
def response
scope.response
end
@@ -418,23 +432,21 @@
def redirect(path, status=302)
response.redirect(path, status)
throw :halt, response.finish
end
- # If the current path is the root ("/"), match on the block. If a request
- # method is given, return immediately if the request does not use the given
- # method.
- def root(request_method=nil, &block)
- if env[PATH_INFO] == SLASH && (!request_method || send(:"#{request_method}?"))
- _on(EMPTY_ARRAY, &block)
+ # If this is a GET request for the root ("/"), yield to the match block.
+ def root(&block)
+ if @env[PATH_INFO] == SLASH && is_get?
+ always(&block)
end
end
# Call the given rack app with the environment and immediately return
# the response as the response for this request.
def run(app)
- throw :halt, app.call(env)
+ throw :halt, app.call(@env)
end
private
# Match any of the elements in the given array. Return at the
@@ -478,37 +490,27 @@
# segment matches.
def _match_symbol_regexp(s)
SEGMENT
end
- # Internal match method taking array of matchers instead of multiple
- # arguments.
- def _on(args)
- script = env[SCRIPT_NAME]
- path = env[PATH_INFO]
-
- # For every block, we make sure to reset captures so that
- # nesting matchers won't mess with each other's captures.
- captures.clear
-
- return unless match_all(args)
- block_result(yield(*captures))
- throw :halt, response.finish
- ensure
- env[SCRIPT_NAME] = script
- env[PATH_INFO] = path
- end
-
# Backbone of the verb method support, using a terminal match if
# args is not empty, or a regular match if it is empty.
def _verb(args, &block)
- unless args.empty?
+ if args.empty?
+ always(&block)
+ else
args << TERM
+ if_match(args, &block)
end
- _on(args, &block)
end
+ # Yield to the match block and return rack response after the block returns.
+ def always
+ block_result(yield)
+ throw :halt, response.finish
+ end
+
# The body to use for the response if the response does not return
# a body. By default, a String is returned directly, and nil is
# returned otherwise.
def block_result_body(result)
if result.is_a?(String)
@@ -519,21 +521,42 @@
# Attempts to match the pattern to the current path. If there is no
# match, returns false without changes. Otherwise, modifies
# SCRIPT_NAME to include the matched path, removes the matched
# path from PATH_INFO, and updates captures with any regex captures.
def consume(pattern)
+ env = @env
return unless matchdata = env[PATH_INFO].match(pattern)
vars = matchdata.captures
# Don't mutate SCRIPT_NAME, breaks try
env[SCRIPT_NAME] += vars.shift
- env[PATH_INFO] = "#{vars.pop}#{matchdata.post_match}"
+ env[PATH_INFO] = matchdata.post_match
captures.concat(vars)
end
+ # If all of the arguments match, yields to the match block and
+ # returns the rack response when the block returns. If any of
+ # the match arguments doesn't match, does nothing.
+ def if_match(args)
+ env = @env
+ script = env[SCRIPT_NAME]
+ path = env[PATH_INFO]
+
+ # For every block, we make sure to reset captures so that
+ # nesting matchers won't mess with each other's captures.
+ captures.clear
+
+ return unless match_all(args)
+ block_result(yield(*captures))
+ throw :halt, response.finish
+ ensure
+ env[SCRIPT_NAME] = script
+ env[PATH_INFO] = path
+ end
+
# Attempt to match the argument to the given request, handling
# common ruby types.
def match(matcher)
case matcher
when String
@@ -541,11 +564,11 @@
when Regexp
_match_regexp(matcher)
when Symbol
_match_symbol(matcher)
when TERM
- env[PATH_INFO] == EMPTY_STRING
+ @env[PATH_INFO] == EMPTY_STRING
when Hash
_match_hash(matcher)
when Array
_match_array(matcher)
when Proc
@@ -561,20 +584,20 @@
end
# Match files with the given extension. Requires that the
# request path end with the extension.
def match_extension(ext)
- consume(self.class.cached_matcher(ext){"([^\\/]+?)\.#{ext}\\z"})
+ consume(self.class.cached_matcher([:extension, ext]){"([^\\/]+?)\.#{ext}\\z"})
end
# Match by request method. This can be an array if you want
# to match on multiple methods.
def match_method(type)
if type.is_a?(Array)
type.any?{|t| match_method(t)}
else
- type.to_s.upcase == env[REQUEST_METHOD]
+ type.to_s.upcase == @env[REQUEST_METHOD]
end
end
# Match the given parameter if present, even if the parameter is empty.
# Adds any match to the captures.
@@ -696,6 +719,8 @@
end
end
extend RodaPlugins::Base::ClassMethods
plugin RodaPlugins::Base
+ RodaRequest.def_verb_method(RodaPlugins::Base::RequestMethods, :get)
+ RodaRequest.def_verb_method(RodaPlugins::Base::RequestMethods, :post)
end