lib/roda/plugins/params_capturing.rb
# frozen-string-literal: true # class Roda module RodaPlugins # The params_capturing plugin makes string and symbol matchers # update the request params with the value of the captured segments, # using the matcher as the key: # # plugin :params_capturing # # route do |r| # # GET /foo/123/abc/67 # r.on("foo/:bar/:baz", :quux) do # r[:bar] #=> '123' # r[:baz] #=> 'abc' # r[:quux] #=> '67' # end # end # # Note that this updating of the request params using the matcher as # the key is only done if all arguments to the matcher are symbols # or strings. # # All matchers will update the request params by adding all # captured segments to the +captures+ key, including # symbol and string matchers: # # r.on(:x, /(\d+)\/(\w+)/, ':y') do # r[:x] #=> nil # r[:y] #=> nil # r[:captures] #=> ["foo", "123", "abc", "67"] # end # # Note that the request params +captures+ entry will be appended to with # each nested match: # # r.on(:w) do # r.on(:x) do # r.on(:y) do # r.on(:z) do # r[:captures] # => ["foo", "123", "abc", "67"] # end # end # end # end # # Note that any existing params captures entry will be overwritten # by this plugin. You can use +r.GET+ or +r.POST+ to get the underlying # entry, depending on how it was submitted. # # Also note that the param keys are actually stored in +r.params+ as # strings and not symbols (<tt>r[]</tt> converts the argument # to a string before looking it up in +r.params+). # # Also note that this plugin will not work correctly if you are using # the symbol_matchers plugin with custom symbol matching and are using # symbols that capture multiple values or no values. module ParamsCapturing module RequestMethods def initialize(*) super params['captures'] = [] end private if RUBY_VERSION >= '1.9' # Regexp to scan for capture names. Uses positive lookbehind # so it is only valid on ruby 1.9+, hence the use of eval. STRING_PARAM_CAPTURE_REGEXP = eval("/(?<=:)\\w+/") # Add the capture names from this string to list of param # capture names if param capturing. def _match_string(str) if pc = @_params_captures pc.concat(str.scan(STRING_PARAM_CAPTURE_REGEXP)) end super end else # :nocov: # Ruby 1.8 doesn't support positive lookbehind, so include the # colon in the scan, and strip it out later. STRING_PARAM_CAPTURE_RANGE = 1..-1 def _match_string(str) if pc = @_params_captures pc.concat(str.scan(/:\w+/).map{|s| s[STRING_PARAM_CAPTURE_RANGE]}) end super end # :nocov: end # Add the symbol to the list of param capture names if param capturing. def _match_symbol(sym) if pc = @_params_captures pc << sym.to_s end super end # If all arguments are strings or symbols, turn on param capturing during # the matching, but turn it back off before yielding to the block. Add # any captures to the params based on the param capture names added by # the matchers. def if_match(args) params = self.params if args.all?{|x| x.is_a?(String) || x.is_a?(Symbol)} pc = @_params_captures = [] end super do |*a| if pc @_params_captures = nil pc.zip(a).each do |k,v| params[k] = v end end params['captures'].concat(a) yield(*a) end end end end register_plugin(:params_capturing, ParamsCapturing) end end