rack-mount-0.8.3/0000755000175000017500000000000012307336063012613 5ustar jonasjonasrack-mount-0.8.3/.gitignore0000644000175000017500000000001512307336063014577 0ustar jonasjonasGemfile.lock rack-mount-0.8.3/Rakefile0000644000175000017500000000207712307336063014266 0ustar jonasjonasrequire 'rake/testtask' task :default => :test Rake::TestTask.new do |t| t.libs << 'test' t.warning = true end task :compile => [ 'lib/rack/mount/strexp/parser.rb', 'lib/rack/mount/strexp/tokenizer.rb' ] file 'lib/rack/mount/strexp/parser.rb' => 'lib/rack/mount/strexp/parser.y' do |t| sh "racc -l -o #{t.name} #{t.prerequisites.first}" sh "sed -i '' -e 's/ end # module Mount/ end # module Mount/' #{t.name}" sh "sed -i '' -e 's/ end # module Rack/end # module Rack/' #{t.name}" end file 'lib/rack/mount/strexp/tokenizer.rb' => 'lib/rack/mount/strexp/tokenizer.rex' do |t| sh "rex -o #{t.name} #{t.prerequisites.first}" end namespace :vendor do task :update => [:update_regin] task :update_regin do system 'git clone git://github.com/josh/regin.git' FileUtils.rm_rf('lib/rack/mount/vendor/regin') FileUtils.cp_r('regin/lib', 'lib/rack/mount/vendor/regin') FileUtils.rm_rf('regin') FileUtils.rm_rf('lib/rack/mount/vendor/regin/regin/parser.y') FileUtils.rm_rf('lib/rack/mount/vendor/regin/regin/tokenizer.rex') end end rack-mount-0.8.3/lib/0000755000175000017500000000000012307336063013361 5ustar jonasjonasrack-mount-0.8.3/lib/rack/0000755000175000017500000000000012307336063014301 5ustar jonasjonasrack-mount-0.8.3/lib/rack/mount.rb0000644000175000017500000000242312307336063015771 0ustar jonasjonasrequire 'rack' module Rack #:nodoc: # A stackable dynamic tree based Rack router. # # Rack::Mount supports Rack's Cascade style of trying several routes until # it finds one that is not a 404. This allows multiple routes to be nested # or stacked on top of each other. Since the application endpoint can # trigger the router to continue matching, middleware can be used to add # arbitrary conditions to any route. This allows you to route based on # other request attributes, session information, or even data dynamically # pulled from a database. module Mount autoload :CodeGeneration, 'rack/mount/code_generation' autoload :GeneratableRegexp, 'rack/mount/generatable_regexp' autoload :Multimap, 'rack/mount/multimap' autoload :Prefix, 'rack/mount/prefix' autoload :RegexpWithNamedGroups, 'rack/mount/regexp_with_named_groups' autoload :Route, 'rack/mount/route' autoload :RouteSet, 'rack/mount/route_set' autoload :RoutingError, 'rack/mount/route_set' autoload :Strexp, 'rack/mount/strexp' autoload :Utils, 'rack/mount/utils' autoload :Version, 'rack/mount/version' module Analysis #:nodoc: autoload :Histogram, 'rack/mount/analysis/histogram' autoload :Splitting, 'rack/mount/analysis/splitting' end end end rack-mount-0.8.3/lib/rack/mount/0000755000175000017500000000000012307336063015443 5ustar jonasjonasrack-mount-0.8.3/lib/rack/mount/utils.rb0000644000175000017500000001073312307336063017134 0ustar jonasjonasbegin require 'regin' rescue LoadError $: << File.expand_path(File.join(File.dirname(__FILE__), 'vendor/regin')) require 'regin' end require 'uri' module Rack::Mount # Private utility methods used throughout Rack::Mount. #-- # This module is a trash can. Try to move these functions into # more appropriate contexts. #++ module Utils def silence_debug old_debug, $DEBUG = $DEBUG, nil yield ensure $DEBUG = old_debug end module_function :silence_debug def debug(msg) warn "Rack::Mount #{msg}" if $DEBUG end module_function :debug # Normalizes URI path. # # Strips off trailing slash and ensures there is a leading slash. # # normalize_path("/foo") # => "/foo" # normalize_path("/foo/") # => "/foo" # normalize_path("foo") # => "/foo" # normalize_path("") # => "/" def normalize_path(path) path = "/#{path}" path.squeeze!('/') path.sub!(%r{/+\Z}, '') path = '/' if path == '' path end module_function :normalize_path # Removes trailing nils from array. # # pop_trailing_blanks!([1, 2, 3]) # => [1, 2, 3] # pop_trailing_blanks!([1, 2, 3, nil, ""]) # => [1, 2, 3] # pop_trailing_blanks!([nil]) # => [] # pop_trailing_blanks!([""]) # => [] def pop_trailing_blanks!(ary) while ary.length > 0 && (ary.last.nil? || ary.last == '') ary.pop end ary end module_function :pop_trailing_blanks! RESERVED_PCHAR = ':@&=+$,;%' SAFE_PCHAR = "#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}" if RUBY_VERSION >= '1.9' UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false).freeze else UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false, 'N').freeze end Parser = URI.const_defined?(:Parser) ? URI::Parser.new : URI def escape_uri(uri) Parser.escape(uri.to_s, UNSAFE_PCHAR) end module_function :escape_uri if ''.respond_to?(:force_encoding) def unescape_uri(uri) Parser.unescape(uri).force_encoding('utf-8') end else def unescape_uri(uri) URI.unescape(uri) end end module_function :unescape_uri # Taken from Rack 1.1.x to build nested query strings def build_nested_query(value, prefix = nil) #:nodoc: case value when Array value.map { |v| build_nested_query(v, "#{prefix}[]") }.join("&") when Hash value.map { |k, v| build_nested_query(v, prefix ? "#{prefix}[#{Rack::Utils.escape(k)}]" : Rack::Utils.escape(k)) }.join("&") when String raise ArgumentError, "value must be a Hash" if prefix.nil? "#{prefix}=#{Rack::Utils.escape(value)}" else prefix end end module_function :build_nested_query # Determines whether the regexp must match the entire string. # # regexp_anchored?(/^foo$/) # => true # regexp_anchored?(/foo/) # => false # regexp_anchored?(/^foo/) # => false # regexp_anchored?(/foo$/) # => false def regexp_anchored?(regexp) regexp.source =~ /\A(\\A|\^).*(\\Z|\$)\Z/m ? true : false end module_function :regexp_anchored? def normalize_extended_expression(regexp) return regexp if (regexp.options & Regexp::EXTENDED) == 0 source = regexp.source source.gsub!(/#.+$/, '') source.gsub!(/\s+/, '') source.gsub!(/\\\//, '/') Regexp.compile(source) end module_function :normalize_extended_expression def parse_regexp(regexp) cache = @@_parse_regexp_cache ||= {} if expression = cache[regexp] return expression end unless regexp.is_a?(RegexpWithNamedGroups) regexp = RegexpWithNamedGroups.new(regexp) end expression = Regin.parse(regexp) unless Regin.regexp_supports_named_captures? tag_captures = Proc.new do |group| case group when Regin::Group # TODO: dup instead of mutating group.instance_variable_set('@name', regexp.names[group.index]) if group.index tag_captures.call(group.expression) when Regin::Expression group.each { |child| tag_captures.call(child) } end end tag_captures.call(expression) end cache[regexp] = expression.freeze expression rescue Racc::ParseError, Regin::Parser::ScanError [] end module_function :parse_regexp end end rack-mount-0.8.3/lib/rack/mount/vendor/0000755000175000017500000000000012307336063016740 5ustar jonasjonasrack-mount-0.8.3/lib/rack/mount/vendor/regin/0000755000175000017500000000000012307336063020044 5ustar jonasjonasrack-mount-0.8.3/lib/rack/mount/vendor/regin/regin.rb0000644000175000017500000000364112307336063021501 0ustar jonasjonasmodule Regin autoload :Alternation, 'regin/alternation' autoload :Anchor, 'regin/anchor' autoload :Atom, 'regin/atom' autoload :Character, 'regin/character' autoload :CharacterClass, 'regin/character_class' autoload :Collection, 'regin/collection' autoload :Expression, 'regin/expression' autoload :Group, 'regin/group' autoload :Options, 'regin/options' autoload :Parser, 'regin/parser' # Detect named capture support begin old_debug, $DEBUG = $DEBUG, nil eval('foo = /(?.*)/').named_captures # Returns true if the interpreter is using the Oniguruma Regexp lib # and supports named captures. # # /(?bar)/ def self.regexp_supports_named_captures? true end rescue SyntaxError, NoMethodError def self.regexp_supports_named_captures? #:nodoc: false end ensure $DEBUG = old_debug end POSIX_BRACKET_TYPES = %w( alnum alpha ascii blank cntrl digit graph lower print punct space upper word xdigit foo ) # Returns array of supported POSX bracket types def self.supported_posix_bracket_types @supported_posix_bracket_types ||= [] end # Detect supported posix bracket types begin old_debug, $DEBUG = $DEBUG, nil POSIX_BRACKET_TYPES.each do |type| begin eval("foo = /[[:#{type}:]]/") supported_posix_bracket_types << type rescue SyntaxError, RegexpError end end ensure $DEBUG = old_debug end # Parses Regexp and returns a Expression data structure. def self.parse(regexp) Parser.parse_regexp(regexp) end # Recompiles Regexp by parsing it and turning it back into a Regexp. # # (In the future Regin will perform some Regexp optimizations # such as removing unnecessary captures and options) def self.compile(source) regexp = Regexp.compile(source) expression = parse(regexp) Regexp.compile(expression.to_s(true), expression.flags) end end rack-mount-0.8.3/lib/rack/mount/vendor/regin/regin/0000755000175000017500000000000012307336063021150 5ustar jonasjonasrack-mount-0.8.3/lib/rack/mount/vendor/regin/regin/collection.rb0000644000175000017500000000243312307336063023632 0ustar jonasjonasmodule Regin class Collection include Enumerable def initialize(*args) @array = Array.new(*args) end def each @array.each{ |item| yield item } end def [](i) @array[i] end def length @array.length end alias_method :size, :length def first @array.first end def last @array.last end def +(other) ary = other.is_a?(self.class) ? other.internal_array : other self.class.new(@array + ary) end def to_regexp(anchored = false) re = to_s(true) re = "\\A#{re}\\Z" if anchored Regexp.compile(re, flags) end def match(char) to_regexp.match(char) end def include?(char) any? { |e| e.include?(char) } end def ==(other) #:nodoc: case other when String other == to_s when Array other == @array else eql?(other) end end def eql?(other) #:nodoc: other.instance_of?(self.class) && @array.eql?(other.internal_array) end protected def internal_array #:nodoc: @array end def extract_options(args) if args.last.is_a?(Hash) return args[0..-2], args.last else return args, {} end end end end rack-mount-0.8.3/lib/rack/mount/vendor/regin/regin/options.rb0000644000175000017500000000260012307336063023166 0ustar jonasjonasmodule Regin class Options def self.from_int(flags) multiline = flags & Regexp::MULTILINE != 0 ignorecase = flags & Regexp::IGNORECASE != 0 extended = flags & Regexp::EXTENDED != 0 new(multiline, ignorecase, extended) end attr_reader :multiline, :ignorecase, :extended def initialize(*args) if args.first.is_a?(Hash) @multiline = args[0][:multiline] @ignorecase = args[0][:ignorecase] @extended = args[0][:extended] else @multiline = args[0] @ignorecase = args[1] @extended = args[2] end end def any?(explicit = false) if explicit !multiline.nil? || !ignorecase.nil? || !extended.nil? else multiline || ignorecase || extended end end def to_h(explicit = false) if explicit options = {} options[:multiline] = multiline unless multiline.nil? options[:ignorecase] = ignorecase unless ignorecase.nil? options[:extended] = extended unless extended.nil? options else { :multiline => multiline, :ignorecase => ignorecase, :extended => extended } end end def to_i flag = 0 flag |= Regexp::MULTILINE if multiline flag |= Regexp::IGNORECASE if ignorecase flag |= Regexp::EXTENDED if extended flag end end end rack-mount-0.8.3/lib/rack/mount/vendor/regin/regin/character.rb0000644000175000017500000000201612307336063023430 0ustar jonasjonasmodule Regin class Character < Atom attr_reader :quantifier def initialize(value, options = {}) @quantifier = options[:quantifier] super end def option_names %w( quantifier ) + super end # Returns true if expression could be treated as a literal string. # # A Character is literal is there is no quantifier attached to it. def literal? quantifier.nil? && !ignorecase end def to_s(parent = false) if !parent && ignorecase "(?i-mx:#{value})#{quantifier}" else "#{value}#{quantifier}" end end def to_regexp(anchored = false) re = to_s(true) re = "\\A#{re}\\Z" if anchored Regexp.compile(re, ignorecase) end def match(char) to_regexp(true).match(char) end def include?(char) if ignorecase value.downcase == char.downcase else value == char end end def eql?(other) #:nodoc: super && quantifier.eql?(other.quantifier) end end end rack-mount-0.8.3/lib/rack/mount/vendor/regin/regin/atom.rb0000644000175000017500000000205412307336063022436 0ustar jonasjonasmodule Regin class Atom attr_reader :value, :ignorecase def initialize(value, options = {}) @value = value @ignorecase = options[:ignorecase] end def option_names %w( ignorecase ) end # Returns true if expression could be treated as a literal string. def literal? false end def casefold? ignorecase ? true : false end def dup(options = {}) original_options = option_names.inject({}) do |h, m| h[m.to_sym] = send(m) h end self.class.new(value, original_options.merge(options)) end def to_s(parent = false) "#{value}" end def inspect #:nodoc: "#<#{self.class.to_s.sub('Regin::', '')} #{to_s.inspect}>" end def ==(other) #:nodoc: case other when String other == to_s else eql?(other) end end def eql?(other) #:nodoc: other.instance_of?(self.class) && self.value.eql?(other.value) && (!!self.ignorecase).eql?(!!other.ignorecase) end end end rack-mount-0.8.3/lib/rack/mount/vendor/regin/regin/group.rb0000644000175000017500000000442612307336063022637 0ustar jonasjonasmodule Regin class Group attr_reader :expression, :quantifier, :lookahead, :capture, :index, :name def initialize(expression, options = {}) @quantifier = @index = @name = nil @capture = true @expression = expression.dup(options) @quantifier = options[:quantifier] if options.key?(:quantifier) @lookahead = options[:lookahead] if options.key?(:lookahead) @capture = options[:capture] if options.key?(:capture) @index = options[:index] if options.key?(:index) @name = options[:name] if options.key?(:name) end def option_names %w( quantifier capture index name ) end # Returns true if expression could be treated as a literal string. # # A Group is literal if its expression is literal and it has no quantifier. def literal? quantifier.nil? && lookahead.nil? && expression.literal? end def to_s(parent = false) if lookahead == :postive "(?=#{expression.to_s(parent)})#{quantifier}" elsif lookahead == :negative "(?!#{expression.to_s(parent)})#{quantifier}" elsif !expression.options? "(#{capture ? '' : '?:'}#{expression.to_s(parent)})#{quantifier}" elsif capture == false "#{expression.to_s}#{quantifier}" else "(#{expression.to_s})#{quantifier}" end end def to_regexp(anchored = false) re = to_s re = "\\A#{re}\\Z" if anchored Regexp.compile(re) end def dup(options = {}) original_options = option_names.inject({}) do |h, m| h[m.to_sym] = send(m) h end self.class.new(expression, original_options.merge(options)) end def inspect #:nodoc: to_s.inspect end def match(char) to_regexp.match(char) end def include?(char) expression.include?(char) end def capture? capture end def ==(other) #:nodoc: case other when String other == to_s else eql?(other) end end def eql?(other) #:nodoc: other.is_a?(self.class) && self.expression == other.expression && self.quantifier == other.quantifier && self.capture == other.capture && self.index == other.index && self.name == other.name end end end rack-mount-0.8.3/lib/rack/mount/vendor/regin/regin/parser.rb0000644000175000017500000003123212307336063022772 0ustar jonasjonas# # DO NOT MODIFY!!!! # This file is automatically generated by Racc 1.4.6 # from Racc grammer file "". # require 'racc/parser.rb' module Regin class Parser < Racc::Parser #:nodoc: all def self.parse_regexp(regexp) options = Options.from_int(regexp.options) parser = new parser.options_stack << options.to_h expression = parser.scan_str(regexp.source) expression = expression.dup(options.to_h) if options.any? expression end attr_accessor :options_stack def initialize @capture_index = 0 @capture_index_stack = [] @options_stack = [] end ##### State transition tables begin ### racc_action_table = [ 43, 44, 46, 48, 4, 52, 17, 9, 10, 11, 13, 30, 28, 19, 20, 21, 4, 6, 7, 9, 10, 11, 13, 55, 45, 47, 49, 50, 4, 6, 7, 9, 10, 11, 13, 80, 56, 47, 49, 50, 4, 6, 7, 9, 10, 11, 13, 83, 47, 49, 50, 81, 4, 6, 7, 9, 10, 11, 13, 74, 47, 49, 50, 31, 4, 6, 7, 9, 10, 11, 13, 47, 49, 50, 32, 85, 4, 6, 7, 9, 10, 11, 13, 47, 49, 50, 86, 33, 4, 6, 7, 9, 10, 11, 13, 47, 49, 50, 88, 89, 4, 6, 7, 9, 10, 11, 13, 47, 49, 50, 27, 91, 4, 6, 7, 9, 10, 11, 13, 22, 34, 23, 36, 41, 25, 6, 7, 58, 60, 62, 63, 64, 65, 66, 67, 68, 69, 70, 57, 59, 61, 22, 22, 38, 22, 72, 25, 39, 95, 39, 47, 49, 50 ] racc_action_check = [ 28, 28, 28, 28, 13, 29, 3, 13, 13, 13, 13, 14, 13, 3, 3, 3, 31, 13, 13, 31, 31, 31, 31, 35, 28, 28, 28, 28, 0, 31, 31, 0, 0, 0, 0, 51, 35, 84, 84, 84, 80, 0, 0, 80, 80, 80, 80, 73, 73, 73, 73, 71, 1, 80, 80, 1, 1, 1, 1, 42, 42, 42, 42, 15, 48, 1, 1, 48, 48, 48, 48, 83, 83, 83, 19, 75, 46, 48, 48, 46, 46, 46, 46, 87, 87, 87, 76, 20, 30, 46, 46, 30, 30, 30, 30, 77, 77, 77, 78, 79, 43, 30, 30, 43, 43, 43, 43, 74, 74, 74, 12, 81, 44, 43, 43, 44, 44, 44, 44, 4, 21, 4, 22, 27, 4, 44, 44, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 23, 24, 24, 37, 37, 23, 24, 90, 37, 45, 45, 45 ] racc_action_pointer = [ 25, 49, nil, -5, 116, nil, nil, nil, nil, nil, nil, nil, 110, 1, 9, 61, nil, nil, nil, 63, 76, 112, 108, 138, 139, nil, nil, 123, -12, -5, 85, 13, nil, nil, nil, 15, 105, 141, nil, nil, nil, nil, 23, 97, 109, 113, 73, nil, 61, nil, nil, 21, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 37, nil, 11, 70, 65, 76, 58, 88, 89, 37, 107, nil, 34, 0, nil, nil, 46, nil, nil, 138, nil, nil, nil, nil, nil ] racc_action_default = [ -59, -5, -7, -9, -59, -10, -23, -24, -15, -13, -14, -16, -59, -59, -1, -2, -6, -27, -8, -25, -26, -59, -59, -59, -59, -36, -35, -59, -59, -59, -59, -59, -28, -29, -32, -59, -59, -59, -11, -34, -33, 96, -59, -59, -59, -59, -59, -56, -59, -57, -58, -59, -17, -3, -4, -31, -30, -49, -38, -50, -39, -51, -40, -41, -42, -43, -44, -45, -46, -47, -48, -59, -12, -59, -59, -59, -59, -59, -59, -59, -59, -59, -55, -59, -59, -18, -19, -59, -21, -22, -59, -37, -54, -53, -52, -20 ] racc_goto_table = [ 12, 24, 40, 35, 42, 53, 54, 16, 18, 71, 51, nil, nil, 29, nil, 40, nil, nil, 73, nil, 37, 77, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 75, 76, nil, 78, nil, 79, 82, 84, nil, nil, 87, nil, nil, nil, nil, nil, 92, 93, nil, nil, 94, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 90 ] racc_goto_check = [ 1, 9, 13, 12, 15, 3, 3, 5, 7, 14, 11, nil, nil, 1, nil, 13, nil, nil, 15, nil, 9, 15, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 1, nil, 1, nil, 1, 15, 15, nil, nil, 15, nil, nil, nil, nil, nil, 15, 15, nil, nil, 15, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1 ] racc_goto_pointer = [ nil, 0, nil, -25, nil, 6, nil, 5, nil, -3, nil, -18, -18, -22, -27, -24 ] racc_goto_default = [ nil, nil, 14, 15, 1, 2, 3, nil, 5, nil, 8, nil, nil, 26, nil, nil ] racc_reduce_table = [ 0, 0, :racc_error, 1, 41, :_reduce_1, 1, 41, :_reduce_none, 3, 42, :_reduce_3, 3, 42, :_reduce_4, 1, 43, :_reduce_5, 2, 44, :_reduce_6, 1, 44, :_reduce_7, 2, 45, :_reduce_8, 1, 45, :_reduce_none, 1, 46, :_reduce_none, 3, 46, :_reduce_11, 4, 46, :_reduce_12, 1, 46, :_reduce_13, 1, 46, :_reduce_14, 1, 46, :_reduce_15, 1, 46, :_reduce_16, 3, 48, :_reduce_17, 5, 48, :_reduce_18, 5, 48, :_reduce_19, 6, 48, :_reduce_20, 5, 48, :_reduce_21, 5, 48, :_reduce_22, 1, 50, :_reduce_none, 1, 50, :_reduce_none, 1, 47, :_reduce_none, 1, 47, :_reduce_none, 1, 47, :_reduce_none, 2, 47, :_reduce_28, 2, 47, :_reduce_29, 3, 47, :_reduce_30, 2, 52, :_reduce_31, 1, 52, :_reduce_none, 2, 49, :_reduce_33, 2, 49, :_reduce_34, 1, 49, :_reduce_none, 1, 49, :_reduce_none, 5, 53, :_reduce_37, 1, 54, :_reduce_none, 1, 54, :_reduce_none, 1, 54, :_reduce_none, 1, 54, :_reduce_none, 1, 54, :_reduce_none, 1, 54, :_reduce_none, 1, 54, :_reduce_none, 1, 54, :_reduce_none, 1, 54, :_reduce_none, 1, 54, :_reduce_none, 1, 54, :_reduce_none, 1, 54, :_reduce_none, 1, 54, :_reduce_none, 1, 54, :_reduce_none, 4, 51, :_reduce_52, 4, 51, :_reduce_53, 4, 51, :_reduce_54, 3, 51, :_reduce_55, 1, 55, :_reduce_56, 1, 55, :_reduce_57, 1, 55, :_reduce_58 ] racc_reduce_n = 59 racc_shift_n = 96 racc_token_table = { false => 0, :error => 1, :BAR => 2, :LBRACK => 3, :RBRACK => 4, :NEGATE => 5, :CCLASS => 6, :DOT => 7, :CHAR => 8, :LPAREN => 9, :RPAREN => 10, :QMARK => 11, :EQUAL => 12, :BANG => 13, :COLON => 14, :NAME => 15, :L_ANCHOR => 16, :R_ANCHOR => 17, :STAR => 18, :PLUS => 19, :LCURLY => 20, :RCURLY => 21, "alnum" => 22, "alpha" => 23, "ascii" => 24, "blank" => 25, "cntrl" => 26, "digit" => 27, "graph" => 28, "lower" => 29, "print" => 30, "punct" => 31, "space" => 32, "upper" => 33, "word" => 34, "xdigit" => 35, :MINUS => 36, :MULTILINE => 37, :IGNORECASE => 38, :EXTENDED => 39 } racc_nt_base = 40 racc_use_result_var = true Racc_arg = [ racc_action_table, racc_action_check, racc_action_default, racc_action_pointer, racc_goto_table, racc_goto_check, racc_goto_default, racc_goto_pointer, racc_nt_base, racc_reduce_table, racc_token_table, racc_shift_n, racc_reduce_n, racc_use_result_var ] Racc_token_to_s_table = [ "$end", "error", "BAR", "LBRACK", "RBRACK", "NEGATE", "CCLASS", "DOT", "CHAR", "LPAREN", "RPAREN", "QMARK", "EQUAL", "BANG", "COLON", "NAME", "L_ANCHOR", "R_ANCHOR", "STAR", "PLUS", "LCURLY", "RCURLY", "\"alnum\"", "\"alpha\"", "\"ascii\"", "\"blank\"", "\"cntrl\"", "\"digit\"", "\"graph\"", "\"lower\"", "\"print\"", "\"punct\"", "\"space\"", "\"upper\"", "\"word\"", "\"xdigit\"", "MINUS", "MULTILINE", "IGNORECASE", "EXTENDED", "$start", "expression", "alternation", "subexpression", "expression_ary", "quantified_atom", "atom", "quantifier", "group", "bracket_expression", "anchor", "options", "quantifier_char", "posix_bracket_expression", "posix_bracket_type", "modifier" ] Racc_debug_parser = false ##### State transition tables end ##### # reduce 0 omitted def _reduce_1(val, _values, result) result = Expression.new(val[0]) result end # reduce 2 omitted def _reduce_3(val, _values, result) result = val[0] + [val[2]] result end def _reduce_4(val, _values, result) result = Alternation.new(val[0], val[2]) result end def _reduce_5(val, _values, result) result = Expression.new(val[0]) result end def _reduce_6(val, _values, result) result = val[0] + [val[1]] result end def _reduce_7(val, _values, result) result = [val[0]] result end def _reduce_8(val, _values, result) result = val[0].dup(:quantifier => val[1]) result end # reduce 9 omitted # reduce 10 omitted def _reduce_11(val, _values, result) result = CharacterClass.new(val[1]) result end def _reduce_12(val, _values, result) result = CharacterClass.new(val[2], :negate => true) result end def _reduce_13(val, _values, result) result = CharacterClass.new(val[0]) result end def _reduce_14(val, _values, result) result = CharacterClass.new('.') result end def _reduce_15(val, _values, result) result = Anchor.new(val[0]) result end def _reduce_16(val, _values, result) result = Character.new(val[0]) result end def _reduce_17(val, _values, result) result = Group.new(val[1], :index => @capture_index_stack.pop) result end def _reduce_18(val, _values, result) result = Group.new(val[3], :index => @capture_index_stack.pop, :lookahead => :postive) result end def _reduce_19(val, _values, result) result = Group.new(val[3], :index => @capture_index_stack.pop, :lookahead => :negative) result end def _reduce_20(val, _values, result) result = Group.new(val[4], val[2].merge(:capture => false)) @options_stack.pop result end def _reduce_21(val, _values, result) result = Group.new(val[3], :capture => false); result end def _reduce_22(val, _values, result) result = Group.new(val[3], :name => val[2], :index => @capture_index_stack.pop); result end # reduce 23 omitted # reduce 24 omitted # reduce 25 omitted # reduce 26 omitted # reduce 27 omitted def _reduce_28(val, _values, result) result = val.join result end def _reduce_29(val, _values, result) result = val.join result end def _reduce_30(val, _values, result) result = val.join result end def _reduce_31(val, _values, result) result = val.join result end # reduce 32 omitted def _reduce_33(val, _values, result) result = val.join result end def _reduce_34(val, _values, result) result = val.join result end # reduce 35 omitted # reduce 36 omitted def _reduce_37(val, _values, result) result = val.join result end # reduce 38 omitted # reduce 39 omitted # reduce 40 omitted # reduce 41 omitted # reduce 42 omitted # reduce 43 omitted # reduce 44 omitted # reduce 45 omitted # reduce 46 omitted # reduce 47 omitted # reduce 48 omitted # reduce 49 omitted # reduce 50 omitted # reduce 51 omitted def _reduce_52(val, _values, result) @options_stack << result = { val[1] => false, val[2] => false, val[3] => false } result end def _reduce_53(val, _values, result) @options_stack << result = { val[0] => true, val[2] => false, val[3] => false } result end def _reduce_54(val, _values, result) @options_stack << result = { val[0] => true, val[1] => true, val[3] => false } result end def _reduce_55(val, _values, result) @options_stack << result = { val[0] => true, val[1] => true, val[2] => true } result end def _reduce_56(val, _values, result) result = :multiline result end def _reduce_57(val, _values, result) result = :ignorecase result end def _reduce_58(val, _values, result) result = :extended result end def _reduce_none(val, _values, result) val[0] end end # class Parser end # module Regin require 'regin/tokenizer' rack-mount-0.8.3/lib/rack/mount/vendor/regin/regin/tokenizer.rb0000644000175000017500000001240312307336063023507 0ustar jonasjonas#-- # DO NOT MODIFY!!!! # This file is automatically generated by rex 1.0.5 # from lexical definition file "lib/regin/tokenizer.rex". #++ require 'racc/parser' class Regin::Parser < Racc::Parser require 'strscan' class ScanError < StandardError ; end attr_reader :lineno attr_reader :filename attr_accessor :state def scan_setup(str) @ss = StringScanner.new(str) @lineno = 1 @state = nil end def action yield end def scan_str(str) scan_setup(str) do_parse end alias :scan :scan_str def load_file( filename ) @filename = filename open(filename, "r") do |f| scan_setup(f.read) end end def scan_file( filename ) load_file(filename) do_parse end def next_token return if @ss.eos? # skips empty actions until token = _next_token or @ss.eos?; end token end def _next_token text = @ss.peek(1) @lineno += 1 if text == "\n" token = case @state when nil case when (text = @ss.scan(/\\[dDsSwW]/)) action { [:CCLASS, text] } when (text = @ss.scan(/\^|\\A/)) action { [:L_ANCHOR, text] } when (text = @ss.scan(/\$|\\Z/)) action { [:R_ANCHOR, text] } when (text = @ss.scan(/<(\w+)>/)) action { [:NAME, @ss[1]] } when (text = @ss.scan(/\(/)) action { @capture_index_stack << @capture_index @capture_index += 1 @state = :OPTIONS if @ss.peek(1) == '?'; [:LPAREN, text] } when (text = @ss.scan(/\)/)) action { [:RPAREN, text] } when (text = @ss.scan(/\[/)) action { @state = :CCLASS; [:LBRACK, text] } when (text = @ss.scan(/\{/)) action { [:LCURLY, text] } when (text = @ss.scan(/\}/)) action { [:RCURLY, text] } when (text = @ss.scan(/\|/)) action { [:BAR, text] } when (text = @ss.scan(/\./)) action { [:DOT, text] } when (text = @ss.scan(/\!/)) action { [:BANG, text] } when (text = @ss.scan(/\=/)) action { [:EQUAL, text] } when (text = @ss.scan(/\?/)) action { [:QMARK, text] } when (text = @ss.scan(/\+/)) action { [:PLUS, text] } when (text = @ss.scan(/\*/)) action { [:STAR, text] } when (text = @ss.scan(/\#/)) action { if @options_stack[-1][:extended] @state = :COMMENT; next_token else [:CHAR, text] end } when (text = @ss.scan(/\s|\n/)) action { if @options_stack[-1][:extended] next_token else [:CHAR, text] end } when (text = @ss.scan(/\\(.)/)) action { [:CHAR, @ss[1]] } when (text = @ss.scan(/./)) action { [:CHAR, text] } else text = @ss.string[@ss.pos .. -1] raise ScanError, "can not match: '" + text + "'" end # if when :CCLASS case when (text = @ss.scan(/\[/)) action { [:LBRACK, text] } when (text = @ss.scan(/\]/)) action { @state = nil; [:RBRACK, text] } when (text = @ss.scan(/\^/)) action { [@ss.string[@ss.pos-2, 1] == '[' ? :NEGATE : :CHAR, text] } when (text = @ss.scan(/:/)) action { if @ss.string[@ss.pos-2, 1] == '[' @state = :POSIX_CCLASS [:COLON, text] else [:CHAR, text] end } when (text = @ss.scan(/\\-/)) action { [:CHAR, text] } when (text = @ss.scan(/\\[dDsSwW]/)) action { [:CHAR, text] } when (text = @ss.scan(/\\(.)/)) action { [:CHAR, @ss[1]] } when (text = @ss.scan(/./)) action { [:CHAR, text] } else text = @ss.string[@ss.pos .. -1] raise ScanError, "can not match: '" + text + "'" end # if when :POSIX_CCLASS case when (text = @ss.scan(/\w+/)) action { [text, text] } when (text = @ss.scan(/:/)) action { [:COLON, text] } when (text = @ss.scan(/\]/)) action { @state = :CCLASS; [:RBRACK, text] } else text = @ss.string[@ss.pos .. -1] raise ScanError, "can not match: '" + text + "'" end # if when :OPTIONS case when (text = @ss.scan(/\?/)) action { @state = nil unless @ss.peek(1) =~ /-|m|i|x|:/ [:QMARK, text] } when (text = @ss.scan(/\-/)) action { [:MINUS, text] } when (text = @ss.scan(/m/)) action { [:MULTILINE, text] } when (text = @ss.scan(/i/)) action { [:IGNORECASE, text] } when (text = @ss.scan(/x/)) action { [:EXTENDED, text] } when (text = @ss.scan(/\:/)) action { @capture_index_stack.pop @capture_index -= 1 @state = nil; [:COLON, text] } else text = @ss.string[@ss.pos .. -1] raise ScanError, "can not match: '" + text + "'" end # if when :COMMENT case when (text = @ss.scan(/\n/)) action { @state = nil; next_token } when (text = @ss.scan(/./)) action { next_token } else text = @ss.string[@ss.pos .. -1] raise ScanError, "can not match: '" + text + "'" end # if else raise ScanError, "undefined state: '" + state.to_s + "'" end # case state token end # def _next_token end # class rack-mount-0.8.3/lib/rack/mount/vendor/regin/regin/alternation.rb0000644000175000017500000000140612307336063024016 0ustar jonasjonasmodule Regin class Alternation < Collection def initialize(*args) args, options = extract_options(args) if args.length == 1 && args.first.instance_of?(Array) super(args.first) else super(args) end if options.key?(:ignorecase) @array.map! { |e| e.dup(:ignorecase => options[:ignorecase]) } end end # Returns true if expression could be treated as a literal string. # # Alternation groups are never literal. def literal? false end def flags 0 end def dup(options = {}) self.class.new(to_a, options) end def to_s(parent = false) map { |e| e.to_s(parent) }.join('|') end def inspect #:nodoc: to_s.inspect end end end rack-mount-0.8.3/lib/rack/mount/vendor/regin/regin/character_class.rb0000644000175000017500000000176512307336063024627 0ustar jonasjonasmodule Regin class CharacterClass < Character def initialize(value, options = {}) @negate = options[:negate] super end def option_names %w( negate ) + super end attr_reader :negate def negated? negate ? true : false end # Returns true if expression could be treated as a literal string. # # A CharacterClass is never literal. def literal? false end def bracketed? value != '.' && value !~ /^\\[dDsSwW]$/ end def to_s(parent = false) if bracketed? if !parent && ignorecase "(?i-mx:[#{negate && '^'}#{value}])#{quantifier}" else "[#{negate && '^'}#{value}]#{quantifier}" end else super end end def include?(char) re = quantifier ? to_s.sub(/#{Regexp.escape(quantifier)}$/, '') : to_s Regexp.compile("\\A#{re}\\Z").match(char) end def eql?(other) #:nodoc: super && negate == other.negate end end end rack-mount-0.8.3/lib/rack/mount/vendor/regin/regin/anchor.rb0000644000175000017500000000005512307336063022747 0ustar jonasjonasmodule Regin class Anchor < Atom end end rack-mount-0.8.3/lib/rack/mount/vendor/regin/regin/version.rb0000644000175000017500000000004512307336063023161 0ustar jonasjonasmodule Regin Version = '0.3.7' end rack-mount-0.8.3/lib/rack/mount/vendor/regin/regin/expression.rb0000644000175000017500000000615512307336063023703 0ustar jonasjonasmodule Regin class Expression < Collection attr_reader :ignorecase, :multiline, :extended def initialize(*args) args, options = extract_options(args) @multiline = @ignorecase = @extended = nil if args.length == 1 && args.first.instance_of?(Array) super(args.first) else args = args.map { |e| e.instance_of?(String) ? Character.new(e) : e } super(args) end self.multiline = options[:multiline] if options.key?(:multiline) self.ignorecase = options[:ignorecase] if options.key?(:ignorecase) self.extended = options[:extended] if options.key?(:extended) end # Returns true if expression could be treated as a literal string. # # A Expression is literal if all its elements are literal. def literal? !ignorecase && all? { |e| e.literal? } end def anchored? anchored_to_start? && anchored_to_end? end def anchored_to_start? first.is_a?(Anchor) && first == '\A' end def anchored_to_end? last.is_a?(Anchor) && last == '\Z' end def anchored_to_line? anchored_to_start_of_line? && anchored_to_end_of_line? end def anchored_to_start_of_line? anchored_to_start? || (first.is_a?(Anchor) && first == '^') end def anchored_to_end_of_line? anchored_to_end? || (last.is_a?(Anchor) && last == '$') end def options? options.any?(true) end def flags options.to_i end def +(other) ary = other.is_a?(self.class) ? other.internal_array : other ary = @array + ary + [options.to_h(true)] self.class.new(*ary) end def dup(options = {}) expression = super() expression.multiline = options[:multiline] if options.key?(:multiline) expression.ignorecase = options[:ignorecase] if options.key?(:ignorecase) expression.extended = options[:extended] if options.key?(:extended) expression end def to_s(parent = false) if parent || !options? map { |e| e.to_s(parent) }.join else with, without = [], [] multiline ? (with << 'm') : (without << 'm') ignorecase ? (with << 'i') : (without << 'i') extended ? (with << 'x') : (without << 'x') with = with.join without = without.any? ? "-#{without.join}" : '' "(?#{with}#{without}:#{map { |e| e.to_s(true) }.join})" end end def inspect #:nodoc: "#" end def casefold? ignorecase end def eql?(other) #:nodoc: super && !!self.multiline == !!other.multiline && !!self.ignorecase == !!other.ignorecase && !!self.extended == !!other.extended end protected def options Options.new(multiline, ignorecase, extended) end def multiline=(multiline) @multiline = multiline end def ignorecase=(ignorecase) if @ignorecase.nil? @array.map! { |e| e.dup(:ignorecase => ignorecase) } @ignorecase = ignorecase end end def extended=(extended) @extended = extended end end end rack-mount-0.8.3/lib/rack/mount/strexp/0000755000175000017500000000000012307336063016770 5ustar jonasjonasrack-mount-0.8.3/lib/rack/mount/strexp/parser.rb0000644000175000017500000000610612307336063020614 0ustar jonasjonas# # DO NOT MODIFY!!!! # This file is automatically generated by Racc 1.4.6 # from Racc grammer file "". # require 'racc/parser.rb' require 'rack/mount/utils' require 'rack/mount/strexp/tokenizer' module Rack module Mount class StrexpParser < Racc::Parser if Regin.regexp_supports_named_captures? REGEXP_NAMED_CAPTURE = '(?<%s>%s)'.freeze else REGEXP_NAMED_CAPTURE = '(?:<%s>%s)'.freeze end attr_accessor :anchor, :requirements ##### State transition tables begin ### racc_action_table = [ 1, 2, 3, 9, 4, 1, 2, 3, 12, 4, 1, 2, 3, 11, 4, 1, 2, 3, nil, 4 ] racc_action_check = [ 0, 0, 0, 5, 0, 3, 3, 3, 9, 3, 8, 8, 8, 8, 8, 6, 6, 6, nil, 6 ] racc_action_pointer = [ -2, nil, nil, 3, nil, 3, 13, nil, 8, 8, nil, nil, nil ] racc_action_default = [ -8, -4, -5, -8, -7, -8, -1, -3, -8, -8, -2, -6, 13 ] racc_goto_table = [ 6, 5, 10, 8, 10 ] racc_goto_check = [ 2, 1, 3, 2, 3 ] racc_goto_pointer = [ nil, 1, 0, -4 ] racc_goto_default = [ nil, nil, nil, 7 ] racc_reduce_table = [ 0, 0, :racc_error, 1, 8, :_reduce_1, 2, 9, :_reduce_2, 1, 9, :_reduce_none, 1, 10, :_reduce_4, 1, 10, :_reduce_5, 3, 10, :_reduce_6, 1, 10, :_reduce_7 ] racc_reduce_n = 8 racc_shift_n = 13 racc_token_table = { false => 0, :error => 1, :PARAM => 2, :GLOB => 3, :LPAREN => 4, :RPAREN => 5, :CHAR => 6 } racc_nt_base = 7 racc_use_result_var = true Racc_arg = [ racc_action_table, racc_action_check, racc_action_default, racc_action_pointer, racc_goto_table, racc_goto_check, racc_goto_default, racc_goto_pointer, racc_nt_base, racc_reduce_table, racc_token_table, racc_shift_n, racc_reduce_n, racc_use_result_var ] Racc_token_to_s_table = [ "$end", "error", "PARAM", "GLOB", "LPAREN", "RPAREN", "CHAR", "$start", "target", "expr", "token" ] Racc_debug_parser = false ##### State transition tables end ##### # reduce 0 omitted def _reduce_1(val, _values, result) result = anchor ? "\\A#{val.join}\\Z" : "\\A#{val.join}" result end def _reduce_2(val, _values, result) result = val.join result end # reduce 3 omitted def _reduce_4(val, _values, result) name = val[0].to_sym requirement = requirements[name] result = REGEXP_NAMED_CAPTURE % [name, requirement] result end def _reduce_5(val, _values, result) name = val[0].to_sym requirement = requirements.key?(name) ? requirements[name] : '.+' result = REGEXP_NAMED_CAPTURE % [name, requirement] result end def _reduce_6(val, _values, result) result = "(?:#{val[1]})?" result end def _reduce_7(val, _values, result) result = Regexp.escape(val[0]) result end def _reduce_none(val, _values, result) val[0] end end # class StrexpParser end # module Mount end # module Rack rack-mount-0.8.3/lib/rack/mount/strexp/tokenizer.rb0000644000175000017500000000320012307336063021322 0ustar jonasjonas#-- # DO NOT MODIFY!!!! # This file is automatically generated by rex 1.0.5.beta1 # from lexical definition file "lib/rack/mount/strexp/tokenizer.rex". #++ require 'racc/parser' class Rack::Mount::StrexpParser < Racc::Parser require 'strscan' class ScanError < StandardError ; end attr_reader :lineno attr_reader :filename attr_accessor :state def scan_setup(str) @ss = StringScanner.new(str) @lineno = 1 @state = nil end def action yield end def scan_str(str) scan_setup(str) do_parse end alias :scan :scan_str def load_file( filename ) @filename = filename open(filename, "r") do |f| scan_setup(f.read) end end def scan_file( filename ) load_file(filename) do_parse end def next_token return if @ss.eos? text = @ss.peek(1) @lineno += 1 if text == "\n" token = case @state when nil case when (text = @ss.scan(/\\(\(|\)|:|\*)/)) action { [:CHAR, @ss[1]] } when (text = @ss.scan(/\:([a-zA-Z_]\w*)/)) action { [:PARAM, @ss[1]] } when (text = @ss.scan(/\*([a-zA-Z_]\w*)/)) action { [:GLOB, @ss[1]] } when (text = @ss.scan(/\(/)) action { [:LPAREN, text] } when (text = @ss.scan(/\)/)) action { [:RPAREN, text] } when (text = @ss.scan(/./)) action { [:CHAR, text] } else text = @ss.string[@ss.pos .. -1] raise ScanError, "can not match: '" + text + "'" end # if else raise ScanError, "undefined state: '" + state.to_s + "'" end # case state token end # def next_token end # class rack-mount-0.8.3/lib/rack/mount/strexp/tokenizer.rex0000644000175000017500000000051412307336063021522 0ustar jonasjonasclass Rack::Mount::StrexpParser macro RESERVED \(|\)|:|\* ALPHA_U [a-zA-Z_] rule \\({RESERVED}) { [:CHAR, @ss[1]] } \:({ALPHA_U}\w*) { [:PARAM, @ss[1]] } \*({ALPHA_U}\w*) { [:GLOB, @ss[1]] } \( { [:LPAREN, text] } \) { [:RPAREN, text] } . { [:CHAR, text] } end rack-mount-0.8.3/lib/rack/mount/strexp/parser.y0000644000175000017500000000163012307336063020456 0ustar jonasjonasclass Rack::Mount::StrexpParser rule target: expr { result = anchor ? "\\A#{val.join}\\Z" : "\\A#{val.join}" } expr: expr token { result = val.join } | token token: PARAM { name = val[0].to_sym requirement = requirements[name] result = REGEXP_NAMED_CAPTURE % [name, requirement] } | GLOB { name = val[0].to_sym requirement = requirements.key?(name) ? requirements[name] : '.+' result = REGEXP_NAMED_CAPTURE % [name, requirement] } | LPAREN expr RPAREN { result = "(?:#{val[1]})?" } | CHAR { result = Regexp.escape(val[0]) } end ---- header ---- require 'rack/mount/utils' require 'rack/mount/strexp/tokenizer' ---- inner if Regin.regexp_supports_named_captures? REGEXP_NAMED_CAPTURE = '(?<%s>%s)'.freeze else REGEXP_NAMED_CAPTURE = '(?:<%s>%s)'.freeze end attr_accessor :anchor, :requirements rack-mount-0.8.3/lib/rack/mount/regexp_with_named_groups.rb0000644000175000017500000000317712307336063023070 0ustar jonasjonasmodule Rack::Mount if Regin.regexp_supports_named_captures? RegexpWithNamedGroups = Regexp else require 'strscan' # A wrapper that adds shim named capture support to older # versions of Ruby. # # Because the named capture syntax causes a parse error, an # alternate syntax is used to indicate named captures. # # Ruby 1.9+ named capture syntax: # # /(?[a-z]+)/ # # Ruby 1.8 shim syntax: # # /(?:[a-z]+)/ class RegexpWithNamedGroups < Regexp def self.new(regexp) #:nodoc: if regexp.is_a?(RegexpWithNamedGroups) regexp else super end end # Wraps Regexp with named capture support. def initialize(regexp) regexp = Regexp.compile(regexp) unless regexp.is_a?(Regexp) source, options = regexp.source, regexp.options @names, scanner = [], StringScanner.new(source) while scanner.skip_until(/\(/) if scanner.scan(/\?:<([^>]+)>/) @names << scanner[1] elsif scanner.scan(/\?(i?m?x?\-?i?m?x?)?:/) # ignore noncapture else @names << nil end end source.gsub!(/\?:<([^>]+)>/, '') @names = [] unless @names.any? @names.freeze super(source, options) end def names @names.dup end def named_captures named_captures = {} names.each_with_index { |n, i| named_captures[n] = [i+1] if n } named_captures end def eql?(other) super && @names.eql?(other.names) end end end end rack-mount-0.8.3/lib/rack/mount/multimap.rb0000644000175000017500000000637012307336063017626 0ustar jonasjonasmodule Rack::Mount class Multimap #:nodoc: def initialize(default = []) @hash = Hash.new(default) end def initialize_copy(original) @hash = Hash.new(original.default.dup) original.hash.each_pair do |key, container| @hash[key] = container.dup end end def store(*args) keys = args.dup value = keys.pop key = keys.shift raise ArgumentError, 'wrong number of arguments (1 for 2)' unless value unless key.respond_to?(:=~) raise ArgumentError, "unsupported key: #{args.first.inspect}" end if key.is_a?(Regexp) if keys.empty? @hash.each_pair { |k, l| l << value if k =~ key } self.default << value else @hash.each_pair { |k, _| if k =~ key args[0] = k _store(*args) end } self.default = self.class.new(default) unless default.is_a?(self.class) default[*keys.dup] = value end else _store(*args) end end alias_method :[]=, :store def [](*keys) i, l, r, k = 0, keys.length, self, self.class while r.is_a?(k) r = i < l ? r.hash[keys[i]] : r.default i += 1 end r end def height containers_with_default.max { |a, b| a.length <=> b.length }.length end def average_height lengths = containers_with_default.map { |e| e.length } lengths.inject(0) { |sum, len| sum += len }.to_f / lengths.size end def containers_with_default containers = [] each_container_with_default { |container| containers << container } containers end protected def _store(*args) keys = args value = args.pop raise ArgumentError, 'wrong number of arguments (1 for 2)' unless value if keys.length > 1 update_container(keys.shift) do |container| container = self.class.new(container) unless container.is_a?(self.class) container[*keys] = value container end elsif keys.length == 1 update_container(keys.first) do |container| container << value container end else self << value end end def <<(value) @hash.each_value { |container| container << value } self.default << value self end def each_container_with_default(&block) @hash.each_value do |container| iterate_over_container(container, &block) end iterate_over_container(default, &block) self end def default @hash.default end def default=(value) @hash.default = value end def hash @hash end private def update_container(key) container = @hash[key] container = container.dup if container.equal?(default) container = yield(container) @hash[key] = container end def iterate_over_container(container) if container.respond_to?(:each_container_with_default) container.each_container_with_default do |value| yield value end else yield container end end end end rack-mount-0.8.3/lib/rack/mount/route.rb0000644000175000017500000000713112307336063017130 0ustar jonasjonasrequire 'rack/mount/generatable_regexp' require 'rack/mount/regexp_with_named_groups' require 'rack/mount/utils' module Rack::Mount # Route is an internal class used to wrap a single route attributes. # # Plugins should not depend on any method on this class or instantiate # new Route objects. Instead use the factory method, RouteSet#add_route # to create new routes and add them to the set. class Route # Valid rack application to call if conditions are met attr_reader :app # A hash of conditions to match against. Conditions may be expressed # as strings or regexps to match against. attr_reader :conditions # A hash of values that always gets merged into the parameters hash attr_reader :defaults # Symbol identifier for the route used with named route generations attr_reader :name attr_reader :named_captures def initialize(app, conditions, defaults, name) unless app.respond_to?(:call) raise ArgumentError, 'app must be a valid rack application' \ ' and respond to call' end @app = app @name = name ? name.to_sym : nil @defaults = (defaults || {}).freeze @conditions = {} conditions.each do |method, pattern| next unless method && pattern pattern = Regexp.compile("\\A#{Regexp.escape(pattern)}\\Z") if pattern.is_a?(String) if pattern.is_a?(Regexp) pattern = Utils.normalize_extended_expression(pattern) pattern = RegexpWithNamedGroups.new(pattern) pattern.extend(GeneratableRegexp::InstanceMethods) pattern.defaults = @defaults end @conditions[method] = pattern.freeze end @named_captures = {} @conditions.map { |method, condition| next unless condition.respond_to?(:named_captures) @named_captures[method] = Hash[condition.named_captures.map { |k, v| [k.to_sym, v.last - 1] }].freeze } @named_captures.freeze @has_significant_params = @conditions.any? { |method, condition| (condition.respond_to?(:required_params) && condition.required_params.any?) || (condition.respond_to?(:required_defaults) && condition.required_defaults.any?) } if @conditions.has_key?(:path_info) && !Utils.regexp_anchored?(@conditions[:path_info]) @prefix = true @app = Prefix.new(@app) else @prefix = false end @conditions.freeze end def prefix? @prefix end def generation_keys @conditions.inject({}) { |keys, (_, condition)| if condition.respond_to?(:required_defaults) keys.merge!(condition.required_defaults) else keys end } end def significant_params? @has_significant_params end def generate(method, params = {}, recall = {}, options = {}) if method.nil? result = Hash[@conditions.map { |m, condition| [m, condition.generate(params, recall, options)] if condition.respond_to?(:generate) }] return nil if result.values.compact.empty? else if condition = @conditions[method] if condition.respond_to?(:generate) result = condition.generate(params, recall, options) end end end if result @defaults.each do |key, value| params.delete(key) if params[key] == value end end result end def inspect #:nodoc: "#<#{self.class.name} @app=#{@app.inspect} @conditions=#{@conditions.inspect} @defaults=#{@defaults.inspect} @name=#{@name.inspect}>" end end end rack-mount-0.8.3/lib/rack/mount/prefix.rb0000644000175000017500000000162712307336063017273 0ustar jonasjonasrequire 'rack/mount/utils' module Rack::Mount class Prefix #:nodoc: EMPTY_STRING = ''.freeze PATH_INFO = 'PATH_INFO'.freeze SCRIPT_NAME = 'SCRIPT_NAME'.freeze SLASH = '/'.freeze KEY = 'rack.mount.prefix'.freeze def initialize(app, prefix = nil) @app, @prefix = app, prefix freeze end def call(env) if prefix = env[KEY] || @prefix old_path_info = env[PATH_INFO].dup old_script_name = env[SCRIPT_NAME].dup begin env[PATH_INFO] = env[PATH_INFO].sub(prefix, EMPTY_STRING) env[PATH_INFO] = EMPTY_STRING if env[PATH_INFO] == SLASH env[SCRIPT_NAME] = Utils.normalize_path(env[SCRIPT_NAME].to_s + prefix) @app.call(env) ensure env[PATH_INFO] = old_path_info env[SCRIPT_NAME] = old_script_name end else @app.call(env) end end end end rack-mount-0.8.3/lib/rack/mount/strexp.rb0000644000175000017500000000374212307336063017323 0ustar jonasjonasrequire 'rack/mount/strexp/parser' module Rack::Mount class Strexp class << self # Parses segmented string expression and converts it into a Regexp # # Strexp.compile('foo') # # => %r{\Afoo\Z} # # Strexp.compile('foo/:bar', {}, ['/']) # # => %r{\Afoo/(?[^/]+)\Z} # # Strexp.compile(':foo.example.com') # # => %r{\A(?.+)\.example\.com\Z} # # Strexp.compile('foo/:bar', {:bar => /[a-z]+/}, ['/']) # # => %r{\Afoo/(?[a-z]+)\Z} # # Strexp.compile('foo(.:extension)') # # => %r{\Afoo(\.(?.+))?\Z} # # Strexp.compile('src/*files') # # => %r{\Asrc/(?.+)\Z} def compile(str, requirements = {}, separators = [], anchor = true) return Regexp.compile(str) if str.is_a?(Regexp) requirements = requirements ? requirements.dup : {} normalize_requirements!(requirements, separators) parser = StrexpParser.new parser.anchor = anchor parser.requirements = requirements begin re = parser.scan_str(str) rescue Racc::ParseError => e raise RegexpError, e.message end Regexp.compile(re) end alias_method :new, :compile private def normalize_requirements!(requirements, separators) requirements.each do |key, value| if value.is_a?(Regexp) if regexp_has_modifiers?(value) requirements[key] = value else requirements[key] = value.source end else requirements[key] = Regexp.escape(value) end end requirements.default ||= separators.any? ? "[^#{separators.join}]+" : '.+' requirements end def regexp_has_modifiers?(regexp) regexp.options & (Regexp::IGNORECASE | Regexp::EXTENDED) != 0 end end end end rack-mount-0.8.3/lib/rack/mount/code_generation.rb0000644000175000017500000000645112307336063021123 0ustar jonasjonasmodule Rack::Mount module CodeGeneration #:nodoc: def _expired_recognize(env) #:nodoc: raise 'route set not finalized' end def rehash super optimize_recognize! end private def expire! if @optimized_recognize_defined remove_metaclass_method :recognize class << self alias_method :recognize, :_expired_recognize end @optimized_recognize_defined = false end super end def optimize_container_iterator(container) Utils.debug "optimizing container - size #{container.size}" body = [] container.each_with_index { |route, i| body << "route = self[#{i}]" body << 'matches = {}' body << 'params = route.defaults.dup' conditions = [] route.conditions.each do |method, condition| b = [] if condition.is_a?(Regexp) b << "if m = obj.#{method}.match(#{condition.inspect})" b << "matches[:#{method}] = m" if (named_captures = route.named_captures[method]) && named_captures.any? b << 'captures = m.captures' b << 'p = nil' b << named_captures.map { |k, j| "params[#{k.inspect}] = p if p = captures[#{j}]" }.join('; ') end else b << "if m = obj.#{method} == route.conditions[:#{method}]" end b << 'true' b << 'end' conditions << "(#{b.join('; ')})" end body << <<-RUBY if #{conditions.join(' && ')} yield route, matches, params end RUBY } container.instance_eval(<<-RUBY, __FILE__, __LINE__) def optimized_each(obj) #{body.join("\n")} nil end RUBY end def optimize_recognize! Utils.debug "optimizing recognize" uses_cache = false keys = @recognition_keys.map { |key| if key.respond_to?(:call_source) uses_cache = true key.call_source(:cache, :obj) else "obj.#{key}" end }.join(', ') @optimized_recognize_defined = true remove_metaclass_method :recognize instance_eval(<<-RUBY, __FILE__, __LINE__) def recognize(obj) #{"cache = {}" if uses_cache} container = @recognition_graph[#{keys}] optimize_container_iterator(container) unless container.respond_to?(:optimized_each) if block_given? container.optimized_each(obj) do |route, matches, params| yield route, matches, params end else container.optimized_each(obj) do |route, matches, params| return route, matches, params end end nil end RUBY end # method_defined? can't distinguish between instance # and meta methods. So we have to rescue if the method # has not been defined in the metaclass yet. def remove_metaclass_method(symbol) metaclass = class << self; self; end Utils.silence_debug { metaclass.send(:remove_method, symbol) } rescue NameError nil end end end rack-mount-0.8.3/lib/rack/mount/version.rb0000644000175000017500000000005312307336063017453 0ustar jonasjonasmodule Rack::Mount Version = '0.8.3' end rack-mount-0.8.3/lib/rack/mount/generatable_regexp.rb0000644000175000017500000001310412307336063021612 0ustar jonasjonasrequire 'rack/mount/utils' module Rack::Mount class GeneratableRegexp < Regexp #:nodoc: class DynamicSegment #:nodoc: attr_reader :name, :requirement def initialize(name, requirement) @name, @requirement = name.to_sym, requirement freeze end def ==(obj) @name == obj.name && @requirement == obj.requirement end def =~(str) @requirement =~ str end def to_hash { @name => @requirement } end def inspect "/(?<#{@name}>#{@requirement.source})/" end end module InstanceMethods def self.extended(obj) obj.segments end def defaults=(defaults) @required_captures = nil @required_params = nil @required_defaults = nil @defaults = defaults end def defaults @defaults ||= {} end def generatable? segments.any? end def generate(params = {}, recall = {}, options = {}) return nil unless generatable? merged = recall.merge(params) return nil unless required_params.all? { |p| merged.include?(p) } return nil unless required_defaults.all? { |k, v| merged[k] == v } generate_from_segments(segments, params, merged, options) end def segments @segments ||= begin defaults segments = [] catch(:halt) do expression = Utils.parse_regexp(self) segments = parse_segments(expression) end segments end end def captures segments.flatten.find_all { |s| s.is_a?(DynamicSegment) } end def required_captures @required_captures ||= segments.find_all { |s| s.is_a?(DynamicSegment) && !@defaults.include?(s.name) }.freeze end def required_params @required_params ||= required_captures.map { |s| s.name }.freeze end def required_defaults @required_defaults ||= begin required_defaults = @defaults.dup captures.inject({}) { |h, s| h.merge!(s.to_hash) }.keys.each { |name| required_defaults.delete(name) } required_defaults end end def freeze segments captures required_captures required_params required_defaults super end private def parse_segments(segments) s = [] segments.each_with_index do |part, index| case part when Regin::Anchor # ignore when Regin::Character throw :halt unless part.literal? if s.last.is_a?(String) s.last << part.value.dup else s << part.value.dup end when Regin::Group if part.name s << DynamicSegment.new(part.name, part.expression.to_regexp(true)) else s << parse_segments(part.expression) end when Regin::Expression return parse_segments(part) else throw :halt end end s end EMPTY_STRING = ''.freeze def generate_from_segments(segments, params, merged, options, optional = false) if optional return EMPTY_STRING if segments.all? { |s| s.is_a?(String) } return EMPTY_STRING unless segments.flatten.any? { |s| params.has_key?(s.name) if s.is_a?(DynamicSegment) } return EMPTY_STRING if segments.any? { |segment| if segment.is_a?(DynamicSegment) value = merged[segment.name] || @defaults[segment.name] value = parameterize(segment.name, value, options) merged_value = parameterize(segment.name, merged[segment.name], options) default_value = parameterize(segment.name, @defaults[segment.name], options) if value.nil? || segment !~ value true elsif merged_value == default_value # Nasty control flow return :clear_remaining_segments else false end end } end generated = segments.map do |segment| case segment when String segment when DynamicSegment value = params[segment.name] || merged[segment.name] || @defaults[segment.name] value = parameterize(segment.name, value, options) if value && segment =~ value.to_s value else return end when Array value = generate_from_segments(segment, params, merged, options, true) if value == :clear_remaining_segments segment.each { |s| params.delete(s.name) if s.is_a?(DynamicSegment) } EMPTY_STRING elsif value.nil? EMPTY_STRING else value end end end # Delete any used items from the params segments.each { |s| params.delete(s.name) if s.is_a?(DynamicSegment) } generated.join end def parameterize(name, value, options) if block = options[:parameterize] block.call(name, value) else value end end end include InstanceMethods def initialize(regexp) super segments end end end rack-mount-0.8.3/lib/rack/mount/analysis/0000755000175000017500000000000012307336063017266 5ustar jonasjonasrack-mount-0.8.3/lib/rack/mount/analysis/splitting.rb0000644000175000017500000001110012307336063021621 0ustar jonasjonasrequire 'rack/mount/utils' module Rack::Mount module Analysis class Splitting NULL = "\0" class Key < Struct.new(:method, :index, :separators) def self.split(value, separator_pattern) keys = value.split(separator_pattern) keys.shift if keys[0] == '' keys << NULL keys end def call(cache, obj) (cache[method] ||= self.class.split(obj.send(method), separators))[index] end def call_source(cache, obj) "(#{cache}[:#{method}] ||= Analysis::Splitting::Key.split(#{obj}.#{method}, #{separators.inspect}))[#{index}]" end def inspect "#{method}[#{index}].split(#{separators.inspect})" end end def initialize(*keys) clear keys.each { |key| self << key } end def clear @raw_keys = [] @key_frequency = Analysis::Histogram.new self end def <<(key) raise ArgumentError unless key.is_a?(Hash) @raw_keys << key nil end def possible_keys @possible_keys ||= begin @raw_keys.map do |key| key.inject({}) { |requirements, (method, requirement)| process_key(requirements, method, requirement) requirements } end end end def report @report ||= begin possible_keys.each { |keys| keys.each_pair { |key, _| @key_frequency << key } } return [] if @key_frequency.count <= 1 @key_frequency.keys_in_upper_quartile end end def expire! @possible_keys = @report = nil end def process_key(requirements, method, requirement) separators = separators(method) if requirement.is_a?(Regexp) && separators.any? generate_split_keys(requirement, separators).each_with_index do |value, index| requirements[Key.new(method, index, Regexp.union(*separators))] = value end else if requirement.is_a?(Regexp) expression = Utils.parse_regexp(requirement) if expression.is_a?(Regin::Expression) && expression.anchored_to_line? expression = Regin::Expression.new(expression.reject { |e| e.is_a?(Regin::Anchor) }) return requirements[method] = expression.to_s if expression.literal? end end requirements[method] = requirement end end private def separators(key) key == :path_info ? ["/", "."] : [] end def generate_split_keys(regexp, separators) #:nodoc: segments = [] buf = nil parts = Utils.parse_regexp(regexp) parts.each_with_index do |part, index| case part when Regin::Anchor if part.value == '$' || part.value == '\Z' segments << join_buffer(buf, regexp) if buf segments << NULL buf = nil break end when Regin::CharacterClass break if separators.any? { |s| part.include?(s) } buf = nil segments << part.to_regexp(true) when Regin::Character if separators.any? { |s| part.include?(s) } segments << join_buffer(buf, regexp) if buf peek = parts[index+1] if peek.is_a?(Regin::Character) && separators.include?(peek.value) segments << '' end buf = nil else buf ||= Regin::Expression.new([]) buf += [part] end when Regin::Group if part.quantifier == '?' value = part.expression.first if separators.any? { |s| value.include?(s) } segments << join_buffer(buf, regexp) if buf buf = nil end break elsif part.quantifier == nil break if separators.any? { |s| part.include?(s) } buf = nil segments << part.to_regexp(true) else break end else break end end while segments.length > 0 && (segments.last.nil? || segments.last == '') segments.pop end segments end def join_buffer(parts, regexp) if parts.literal? parts.to_s else parts.to_regexp(true) end end end end end rack-mount-0.8.3/lib/rack/mount/analysis/histogram.rb0000644000175000017500000000306512307336063021614 0ustar jonasjonasmodule Rack::Mount module Analysis class Histogram < Hash #:nodoc: attr_reader :count def initialize @count = 0 super(0) expire_caches! end def <<(value) @count += 1 self[value] += 1 if value expire_caches! self end def sorted_by_frequency sort_by { |_, value| value }.reverse! end def max @max ||= values.max || 0 end def min @min ||= values.min || 0 end def mean @mean ||= calculate_mean end def standard_deviation @standard_deviation ||= calculate_standard_deviation end def upper_quartile_limit @upper_quartile_limit ||= calculate_upper_quartile_limit end def keys_in_upper_quartile @keys_in_upper_quartile ||= compute_keys_in_upper_quartile end private def calculate_mean count / size end def calculate_variance values.inject(0) { |sum, e| sum + (e - mean) ** 2 } / count.to_f end def calculate_standard_deviation Math.sqrt(calculate_variance) end def calculate_upper_quartile_limit mean + standard_deviation end def compute_keys_in_upper_quartile sorted_by_frequency.select { |_, value| value >= upper_quartile_limit }.map! { |key, _| key } end def expire_caches! @max = @min = @mean = @standard_deviation = nil @keys_in_upper_quartile = nil end end end end rack-mount-0.8.3/lib/rack/mount/route_set.rb0000644000175000017500000002562112307336063020007 0ustar jonasjonasrequire 'rack/mount/multimap' require 'rack/mount/route' require 'rack/mount/utils' module Rack::Mount class RoutingError < StandardError; end class RouteSet # Initialize a new RouteSet without optimizations def self.new_without_optimizations(options = {}, &block) new(options.merge(:_optimize => false), &block) end # Basic RouteSet initializer. # # If a block is given, the set is yielded and finalized. # # See other aspects for other valid options: # - Generation::RouteSet.new # - Recognition::RouteSet.new def initialize(options = {}, &block) @parameters_key = options.delete(:parameters_key) || 'rack.routing_args' @parameters_key.freeze @named_routes = {} @recognition_key_analyzer = Analysis::Splitting.new @generation_keys = [:controller, :action] @generation_route_keys = [] @request_class = options.delete(:request_class) || Rack::Request @valid_conditions = @request_class.public_instance_methods.map! { |m| m.to_sym } extend CodeGeneration unless options[:_optimize] == false @optimized_recognize_defined = false @routes = [] expire! if block_given? yield self rehash end end # Builder method to add a route to the set # # app:: A valid Rack app to call if the conditions are met. # conditions:: A hash of conditions to match against. # Conditions may be expressed as strings or # regexps to match against. # defaults:: A hash of values that always gets merged in # name:: Symbol identifier for the route used with named # route generations def add_route(app, conditions = {}, defaults = {}, name = nil) unless conditions.is_a?(Hash) raise ArgumentError, 'conditions must be a Hash' end unless conditions.all? { |method, pattern| @valid_conditions.include?(method) } raise ArgumentError, 'conditions may only include ' + @valid_conditions.inspect end route = Route.new(app, conditions, defaults, name) @routes << route @recognition_key_analyzer << route.conditions @named_routes[route.name] = route if route.name @generation_route_keys << route.generation_keys expire! route end def recognize(obj) raise 'route set not finalized' unless @recognition_graph cache = {} keys = @recognition_keys.map { |key| if key.respond_to?(:call) key.call(cache, obj) else obj.send(key) end } @recognition_graph[*keys].each do |route| matches = {} params = route.defaults.dup if route.conditions.all? { |method, condition| value = obj.send(method) if condition.is_a?(Regexp) && (m = value.match(condition)) matches[method] = m captures = m.captures route.named_captures[method].each do |k, i| if v = captures[i] params[k] = v end end true elsif value == condition true else false end } if block_given? yield route, matches, params else return route, matches, params end end end nil end X_CASCADE = 'X-Cascade'.freeze PASS = 'pass'.freeze PATH_INFO = 'PATH_INFO'.freeze # Rack compatible recognition and dispatching method. Routes are # tried until one returns a non-catch status code. If no routes # match, then catch status code is returned. # # This method can only be invoked after the RouteSet has been # finalized. def call(env) raise 'route set not finalized' unless @recognition_graph env[PATH_INFO] = Utils.normalize_path(env[PATH_INFO]) request = nil req = @request_class.new(env) recognize(req) do |route, matches, params| # TODO: We only want to unescape params from uri related methods params.each { |k, v| params[k] = Utils.unescape_uri(v) if v.is_a?(String) } if route.prefix? env[Prefix::KEY] = matches[:path_info].to_s end old_params = env[@parameters_key] env[@parameters_key] = (old_params || {}).merge(params) result = route.app.call(env) if result[1][X_CASCADE] == PASS env[@parameters_key] = old_params else return result end end request || [404, {'Content-Type' => 'text/html', 'X-Cascade' => 'pass'}, ['Not Found']] end # Generates a url from Rack env and identifiers or significant keys. # # To generate a url by named route, pass the name in as a +Symbol+. # url(env, :dashboard) # => "/dashboard" # # Additional parameters can be passed in as a hash # url(env, :people, :id => "1") # => "/people/1" # # If no named route is given, it will fall back to a slower # generation search. # url(env, :controller => "people", :action => "show", :id => "1") # # => "/people/1" def url(env, *args) named_route, params = nil, {} case args.length when 2 named_route, params = args[0], args[1].dup when 1 if args[0].is_a?(Hash) params = args[0].dup else named_route = args[0] end else raise ArgumentError end only_path = params.delete(:only_path) recall = env[@parameters_key] || {} unless result = generate(:all, named_route, params, recall, :parameterize => lambda { |name, param| Utils.escape_uri(param) }) return end parts, params = result return unless parts params.each do |k, v| if v params[k] = v else params.delete(k) end end req = stubbed_request_class.new(env) req._stubbed_values = parts.merge(:query_string => Utils.build_nested_query(params)) only_path ? req.fullpath : req.url end def generate(method, *args) #:nodoc: raise 'route set not finalized' unless @generation_graph method = nil if method == :all named_route, params, recall, options = extract_params!(*args) merged = recall.merge(params) route = nil if named_route if route = @named_routes[named_route.to_sym] recall = route.defaults.merge(recall) url = route.generate(method, params, recall, options) [url, params] else raise RoutingError, "#{named_route} failed to generate from #{params.inspect}" end else keys = @generation_keys.map { |key| if k = merged[key] k.to_s else nil end } @generation_graph[*keys].each do |r| next unless r.significant_params? if url = r.generate(method, params, recall, options) return [url, params] end end raise RoutingError, "No route matches #{params.inspect}" end end # Number of routes in the set def length @routes.length end def rehash #:nodoc: Utils.debug "rehashing" @recognition_keys = build_recognition_keys @recognition_graph = build_recognition_graph @generation_graph = build_generation_graph self end # Finalizes the set and builds optimized data structures. You *must* # freeze the set before you can use call and url. # So remember to call freeze after you are done adding routes. def freeze unless frozen? rehash stubbed_request_class @recognition_key_analyzer = nil @generation_route_keys = nil @valid_conditions = nil @routes.each { |route| route.freeze } @routes.freeze end super end protected def recognition_stats { :keys => @recognition_keys, :keys_size => @recognition_keys.size, :graph_size => @recognition_graph.size, :graph_height => @recognition_graph.height, :graph_average_height => @recognition_graph.average_height } end private def expire! #:nodoc: @recognition_keys = @recognition_graph = nil @recognition_key_analyzer.expire! @generation_graph = nil end # An internal helper method for constructing a nested set from # the linear route set. # # build_nested_route_set([:request_method, :path_info]) { |route, method| # route.send(method) # } def build_nested_route_set(keys, &block) graph = Multimap.new @routes.each_with_index do |route, index| catch(:skip) do k = keys.map { |key| block.call(key, index) } Utils.pop_trailing_blanks!(k) k.map! { |key| key || /.*/ } graph[*k] = route end end graph end def build_recognition_graph build_nested_route_set(@recognition_keys) { |k, i| @recognition_key_analyzer.possible_keys[i][k] } end def build_recognition_keys keys = @recognition_key_analyzer.report Utils.debug "recognition keys - #{keys.inspect}" keys end def build_generation_graph build_nested_route_set(@generation_keys) { |k, i| throw :skip unless @routes[i].significant_params? if k = @generation_route_keys[i][k] k.to_s else nil end } end def extract_params!(*args) case args.length when 4 named_route, params, recall, options = args when 3 if args[0].is_a?(Hash) params, recall, options = args else named_route, params, recall = args end when 2 if args[0].is_a?(Hash) params, recall = args else named_route, params = args end when 1 if args[0].is_a?(Hash) params = args[0] else named_route = args[0] end else raise ArgumentError end named_route ||= nil params ||= {} recall ||= {} options ||= {} [named_route, params.dup, recall.dup, options.dup] end def stubbed_request_class @stubbed_request_class ||= begin klass = Class.new(@request_class) klass.public_instance_methods.each do |method| next if method =~ /^__|object_id/ klass.class_eval <<-RUBY def #{method}(*args, &block) @_stubbed_values[:#{method}] || super end RUBY end klass.class_eval { attr_accessor :_stubbed_values } klass end end end end rack-mount-0.8.3/Gemfile0000644000175000017500000000003112307336063014100 0ustar jonasjonassource :rubygems gemspec rack-mount-0.8.3/.travis.yml0000644000175000017500000000007512307336063014726 0ustar jonasjonasrvm: - 1.8.7 - 1.9.1 - 1.9.2 - jruby - rbx - ree rack-mount-0.8.3/rack-mount.gemspec0000644000175000017500000000105512307336063016241 0ustar jonasjonasGem::Specification.new do |s| s.name = 'rack-mount' s.version = '0.8.3' s.homepage = "https://github.com/josh/rack-mount" s.summary = "Stackable dynamic tree based Rack router" s.description = <<-EOS A stackable dynamic tree based Rack router. EOS s.files = Dir["README.rdoc", "LICENSE", "lib/**/*.rb"] s.add_dependency 'rack', '>=1.0.0' s.add_development_dependency 'racc' s.add_development_dependency 'rake' s.add_development_dependency 'rexical' s.authors = ["Joshua Peek"] s.email = "josh@joshpeek.com" end rack-mount-0.8.3/LICENSE0000644000175000017500000000203612307336063013621 0ustar jonasjonasCopyright (c) 2009 Joshua Peek Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.rack-mount-0.8.3/README.rdoc0000644000175000017500000000303112307336063014416 0ustar jonasjonas= Rack::Mount A stackable dynamic tree based Rack router. Rack::Mount supports Rack's +X-Cascade+ convention to continue trying routes if the response returns +pass+. This allows multiple routes to be nested or stacked on top of each other. Since the application endpoint can trigger the router to continue matching, middleware can be used to add arbitrary conditions to any route. This allows you to route based on other request attributes, session information, or even data dynamically pulled from a database. === Usage Rack::Mount provides a plugin API to build custom DSLs on top of. The API is extremely minimal and only 3 methods are exposed as the public API. Rack::Mount::RouteSet#add_route:: builder method for adding routes to the set Rack::Mount::RouteSet#call:: Rack compatible recognition and dispatching method Rack::Mount::RouteSet#generate:: generates a route condition from identifiers or significant keys === Example require 'rack/mount' Routes = Rack::Mount::RouteSet.new do |set| # add_route takes a rack application and conditions to match with # # valid conditions methods are any method on Rack::Request # the values to match against may be strings or regexps # # See Rack::Mount::RouteSet#add_route for more options. set.add_route FooApp, { :request_method => 'GET', :path_info => %r{^/foo$} }, {}, :foo end # The route set itself is a simple rack app you mount run Routes # generate path for route named "foo" Routes.generate(:path_info, :foo) #=> "/foo" rack-mount-0.8.3/test/0000755000175000017500000000000012307336063013572 5ustar jonasjonasrack-mount-0.8.3/test/test_utils.rb0000644000175000017500000000306612307336063016323 0ustar jonasjonasrequire 'abstract_unit' class TestUtils < Test::Unit::TestCase include Rack::Mount::Utils def test_normalize_path assert_equal '/foo', normalize_path('/foo') assert_equal '/foo', normalize_path('/foo/') assert_equal '/foo', normalize_path('foo') assert_equal '/', normalize_path('') end def test_pop_trailing_nils assert_equal [1, 2, 3], pop_trailing_blanks!([1, 2, 3]) assert_equal [1, 2, 3], pop_trailing_blanks!([1, 2, 3, nil, nil]) assert_equal [1, 2, 3], pop_trailing_blanks!([1, 2, 3, nil, '']) assert_equal [], pop_trailing_blanks!([nil]) assert_equal [], pop_trailing_blanks!(['']) end def test_build_nested_query assert_equal 'foo', build_nested_query('foo' => nil) assert_equal 'foo=', build_nested_query('foo' => '') assert_equal 'foo=bar', build_nested_query('foo' => 'bar') assert_equal 'foo=1&bar=2', build_nested_query('foo' => '1', 'bar' => '2') assert_equal 'my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F', build_nested_query('my weird field' => 'q1!2"\'w$5&7/z8)?') assert_equal 'foo[]', build_nested_query('foo' => [nil]) assert_equal 'foo[]=', build_nested_query('foo' => ['']) assert_equal 'foo[]=bar', build_nested_query('foo' => ['bar']) end def test_normalize_extended_expression assert_equal %r{foo}, normalize_extended_expression(/foo/) assert_equal %r{^/extended/foo$}, normalize_extended_expression(/^\/extended\/ # comment foo # bar $/x) end end rack-mount-0.8.3/test/test_multimap.rb0000644000175000017500000001373312307336063017015 0ustar jonasjonasrequire 'abstract_unit' class TestMultimap < Test::Unit::TestCase def test_one_level set['/people'] = '/people' set['/people'] = '/people/1' set['/people'] = '/people/new' set['/companies'] = '/companies' assert_equal ['/people', '/people/1', '/people/new'], set['/people'] assert_equal ['/companies'], set['/companies'] assert_equal [], set['/notfound'] assert_equal 3, set.containers_with_default.length assert_equal 3, set.height end def test_one_level_with_defaults set['/people'] = '/people' set['/people'] = '/people/1' set[/.+/] = '/:controller/edit' set['/people'] = '/people/new' set['/companies'] = '/companies' set[/.+/] = '/:controller/:action' assert_equal ['/people', '/people/1', '/:controller/edit', '/people/new', '/:controller/:action'], set['/people'] assert_equal ['/:controller/edit', '/companies', '/:controller/:action'], set['/companies'] assert_equal ['/:controller/edit', '/:controller/:action'], set['/notfound'] assert_equal 3, set.containers_with_default.length assert_equal 5, set.height end def test_regexp set['/abc'] = '/abc' set['/abc'] = '/abc/show' set['/123'] = '/123' set['/456'] = '/456' set[/\d{3}/] = '/:id' set[/.+/] = '/:action' assert_equal ['/abc', '/abc/show', '/:action'], set['/abc'] assert_equal ['/123', '/:id', '/:action'], set['/123'] assert_equal ['/456', '/:id', '/:action'], set['/456'] assert_equal ['/:id', '/:action'], set['/789'] assert_equal ['/:id', '/:action'], set['/notfound'] assert_equal 4, set.containers_with_default.length assert_equal 3, set.height end def test_nested_buckets set['/admin', '/people'] = '/admin/people' set['/admin', '/people'] = '/admin/people/1' set['/admin', '/people'] = '/admin/people/new' set['/admin', '/companies'] = '/admin/companies' assert_equal ['/admin/people', '/admin/people/1', '/admin/people/new'], set['/admin', '/people', '/notfound'] assert_equal ['/admin/people', '/admin/people/1', '/admin/people/new'], set['/admin', '/people'] assert_equal ['/admin/companies'], set['/admin', '/companies'] assert_equal [], set['/admin', '/notfound'] assert_equal [], set['/notfound'] assert_equal 4, set.containers_with_default.length assert_equal 3, set.height end def test_nested_buckets_with_defaults set['/admin'] = '/admin/accounts/new' set['/admin', '/people'] = '/admin/people' set['/admin', '/people'] = '/admin/people/1' set['/admin'] = '/admin/:controller/edit' set['/admin', '/people'] = '/admin/people/new' set['/admin', '/companies'] = '/admin/companies' set[/.+/, '/companies'] = '/:namespace/companies' set[/.+/] = '/:controller/:action' assert_equal ['/admin/accounts/new', '/admin/people', '/admin/people/1', '/admin/:controller/edit', '/admin/people/new', '/:controller/:action'], set['/admin', '/people'] assert_equal ['/admin/accounts/new', '/admin/:controller/edit', '/admin/companies', '/:namespace/companies', '/:controller/:action'], set['/admin', '/companies'] assert_equal ['/admin/accounts/new', '/admin/:controller/edit', '/:controller/:action'], set['/admin', '/notfound'] assert_equal ['/:controller/:action'], set['/notfound'] assert_equal 5, set.containers_with_default.length assert_equal 6, set.height end def test_another_nested_buckets_with_defaults set['DELETE'] = 'DELETE .*' set[/.+/, '/people'] = 'ANY /people/new' set['GET', '/people'] = 'GET /people' set[/.+/, '/people'] = 'ANY /people/export' set['GET', '/people'] = 'GET /people/1' set['POST', '/messages'] = 'POST /messages' set[/.+/, '/messages'] = 'ANY /messages/export' assert_equal ['ANY /people/new', 'GET /people', 'ANY /people/export', 'GET /people/1'], set['GET', '/people'] assert_equal ['ANY /people/new', 'ANY /people/export'], set['POST', '/people'] assert_equal ['ANY /people/new', 'ANY /people/export'], set['PUT', '/people'] assert_equal ['ANY /messages/export'], set['GET', '/messages'] assert_equal ['POST /messages', 'ANY /messages/export'], set['POST', '/messages'] assert_equal 12, set.containers_with_default.length assert_equal 4, set.height end def test_nested_with_regexp set['GET', 'people'] = 'GET /people' set['POST', 'people'] = 'POST /people' set['GET', 'people', 'new'] = 'GET /people/new' set['GET', 'people', /\d+/] = 'GET /people/:id' set['GET', 'people', /\d+/, 'edit'] = 'GET /people/:id/edit' set['POST', 'people', /\d+/] = 'POST /people/:id' set['PUT', 'people', /\d+/] = 'PUT /people/:id' set['DELETE', 'people', /\d+/] = 'DELETE /people/:id' assert_equal ['GET /people', 'GET /people/:id'], set['GET', 'people'] assert_equal ['GET /people', 'GET /people/new'], set['GET', 'people', 'new'] assert_equal ['GET /people', 'GET /people/:id'], set['GET', 'people', '1'] assert_equal ['GET /people', 'GET /people/:id', 'GET /people/:id/edit'], set['GET', 'people', '1', 'edit'] assert_equal 11, set.containers_with_default.length assert_equal 3, set.height end def test_nested_default_bucket set[/.+/, '/people'] = 'GET /people' set[/.+/, '/people'] = 'GET /people/1' set[/.+/, '/messages'] = 'POST /messages' set[/.+/] = 'ANY /:controller/:action' assert_equal 3, set.containers_with_default.length assert_equal 3, set.height end def test_default_bucket_is_cleared_and_copied set[] = 'a' set['b'] = 'b' set['b', 'c'] = 'bc' set['c', /d/, 'e'] = 'cde' set[/c/, 'd', 'e'] = 'ce' assert_equal ['a'], set['a'] assert_equal ['a', 'b'], set['b'] assert_equal ['a'], set['c'] assert_equal ['a', 'b', 'bc'], set['b', 'c'] assert_equal ['a', 'b'], set['b', 'd'] assert_equal ['a'], set['c', 'd'] assert_equal ['a', 'cde', 'ce'], set['c', 'd', 'e'] assert_equal 9, set.containers_with_default.length assert_equal 3, set.height end private def set @set ||= Rack::Mount::Multimap.new end end rack-mount-0.8.3/test/graphing.rb0000644000175000017500000000314712307336063015723 0ustar jonasjonasrequire 'graphviz' class Object def to_graph_node "node#{object_id}" end def to_graph_label inspect.dot_escape end def add_to_graph(graph) graph.add_node(to_graph_node, :label => to_graph_label) end end class Array def to_graph_label "{#{map { |e| e.to_graph_label }.join('|')}}" end end class String DOT_ESCAPE = %w( \\ < > { } ) DOT_ESCAPE_REGEXP = Regexp.compile("(#{Regexp.union(*DOT_ESCAPE).source})") def dot_escape gsub(DOT_ESCAPE_REGEXP) {|s| "\\#{s}" } end end require 'multimap' class Multimap def to_graph_label label = [] @hash.each_key do |key| label << "<#{key.to_graph_node}> #{key.to_graph_label}" end "#{label.join('|')}|" end def add_to_graph(graph) hash_node = super @hash.each_pair do |key, container| node = container.add_to_graph(graph) graph.add_edge({hash_node => key.to_graph_node}, node) end unless default.nil? node = default.add_to_graph(graph) graph.add_edge({hash_node => :default}, node) end hash_node end def to_graph g = GraphViz::new('G') g[:nodesep] = '.05' g[:rankdir] = 'LR' g.node[:shape] = 'record' g.node[:width] = '.1' g.node[:height] = '.1' add_to_graph(g) g end end require 'rack/mount' class Rack::Mount::RouteSet def to_graph @recognition_graph.to_graph end def open_graph! output = File.join(Dir::tmpdir, 'graph.png') to_graph.output(:png => output) system("open #{output}") end end class Rack::Mount::Route def to_graph_label @conditions[:path_info].to_graph_label end end rack-mount-0.8.3/test/test_strexp.rb0000644000175000017500000001345012307336063016506 0ustar jonasjonasrequire 'abstract_unit' class TestStrexp < Test::Unit::TestCase Strexp = Rack::Mount::Strexp def test_leaves_regexps_alone assert_equal %r{foo}, Strexp.compile(%r{foo}) end def test_unanchored_segment assert_equal %r{\A/foo(?:/bar)?}, Strexp.compile('/foo(/bar)', {}, [], false) end def test_does_mutate_args str = 'foo/:bar'.freeze requirements = { :bar => /[a-z]+/.freeze }.freeze separators = ['/'.freeze].freeze if supports_named_captures? assert_equal eval('%r{\Afoo/(?[a-z]+)\Z}'), Strexp.compile(str, requirements, separators) else assert_equal %r{\Afoo/(?:[a-z]+)\Z}, Strexp.compile(str, requirements, separators) end end def test_static_string assert_equal %r{\Afoo\Z}, Strexp.compile('foo') end def test_dynamic_segment if supports_named_captures? assert_equal eval('%r{\A(?.+)\.example\.com\Z}'), Strexp.compile(':foo.example.com') else assert_equal %r{\A(?:.+)\.example\.com\Z}, Strexp.compile(':foo.example.com') end end def test_dynamic_segment_with_leading_underscore if supports_named_captures? assert_equal eval('%r{\A(?<_foo>.+)\.example\.com\Z}'), Strexp.compile(':_foo.example.com') else assert_equal %r{\A(?:<_foo>.+)\.example\.com\Z}, Strexp.compile(':_foo.example.com') end end def test_skips_invalid_group_names assert_equal %r{\A:123\.example\.com\Z}, Strexp.compile(':123.example.com') assert_equal %r{\A:\$\.example\.com\Z}, Strexp.compile(':$.example.com') end def test_escaped_dynamic_segment assert_equal %r{\A:foo\.example\.com\Z}, Strexp.compile('\:foo.example.com') end def test_dynamic_segment_with_separators if supports_named_captures? assert_equal eval('%r{\Afoo/(?[^/]+)\Z}'), Strexp.compile('foo/:bar', {}, ['/']) else assert_equal %r{\Afoo/(?:[^/]+)\Z}, Strexp.compile('foo/:bar', {}, ['/']) end end def test_dynamic_segment_with_requirements if supports_named_captures? assert_equal eval('%r{\Afoo/(?[a-z]+)\Z}'), Strexp.compile('foo/:bar', {:bar => /[a-z]+/}, ['/']) else assert_equal %r{\Afoo/(?:[a-z]+)\Z}, Strexp.compile('foo/:bar', {:bar => /[a-z]+/}, ['/']) end end def test_dynamic_segment_with_requirements_with_case_insensitive if supports_named_captures? bar = /bar/i assert_equal eval('%r{\Afoo/(?#{bar})\Z}'), Strexp.compile('foo/:bar', {:bar => /bar/i}) else bar = /bar/i assert_equal %r{\Afoo/(?:#{bar})\Z}, Strexp.compile('foo/:bar', {:bar => /bar/i}) end end def test_dynamic_segment_with_negative_lookbehind_requirement if supports_named_captures? assert_equal eval('%r{\Afoo/(?(?!baz).*)\Z}'), Strexp.compile('foo/:bar', {:bar => /(?!baz).*/}, ['/']) else assert_equal %r{\Afoo/(?:(?!baz).*)\Z}, Strexp.compile('foo/:bar', {:bar => /(?!baz).*/}, ['/']) end end def test_dynamic_segment_inside_optional_segment if supports_named_captures? assert_equal eval('%r{\Afoo(?:\.(?.+))?\Z}'), Strexp.compile('foo(.:extension)') else assert_equal %r{\Afoo(?:\.(?:.+))?\Z}, Strexp.compile('foo(.:extension)') end end def test_glob_segment if supports_named_captures? assert_equal eval('%r{\Asrc/(?.+)\Z}'), Strexp.compile('src/*files') else assert_equal %r{\Asrc/(?:.+)\Z}, Strexp.compile('src/*files') end end def test_glob_ignores_seperators if supports_named_captures? assert_equal eval('%r{\Asrc/(?.+)\Z}'), Strexp.compile('src/*files', {}, %w( / . ? )) else assert_equal %r{\Asrc/(?:.+)\Z}, Strexp.compile('src/*files', {}, %w( / . ? )) end end def test_glob_segment_at_the_beginning if supports_named_captures? assert_equal eval('%r{\A(?.+)/foo\.txt\Z}'), Strexp.compile('*files/foo.txt') else assert_equal %r{\A(?:.+)/foo\.txt\Z}, Strexp.compile('*files/foo.txt') end end def test_glob_segment_in_the_middle if supports_named_captures? assert_equal eval('%r{\Asrc/(?.+)/foo\.txt\Z}'), Strexp.compile('src/*files/foo.txt') else assert_equal %r{\Asrc/(?:.+)/foo\.txt\Z}, Strexp.compile('src/*files/foo.txt') end end def test_multiple_glob_segments if supports_named_captures? assert_equal eval('%r{\Asrc/(?.+)/dir/(?.+)/foo\.txt\Z}'), Strexp.compile('src/*files/dir/*morefiles/foo.txt') else assert_equal %r{\Asrc/(?:.+)/dir/(?:.+)/foo\.txt\Z}, Strexp.compile('src/*files/dir/*morefiles/foo.txt') end end def test_escaped_glob_segment assert_equal %r{\Asrc/\*files\Z}, Strexp.compile('src/\*files') end def test_glob_uses_requirements if supports_named_captures? assert_equal eval('%r{\Asrc/(?.+?)\Z}'), Strexp.compile('src/*files', {:files => /.+?/}) else assert_equal %r{\Asrc/(?:.+?)\Z}, Strexp.compile('src/*files', {:files => /.+?/}) end end def test_optional_segment assert_equal %r{\A/foo(?:/bar)?\Z}, Strexp.compile('/foo(/bar)') end def test_consecutive_optional_segments assert_equal %r{\A/foo(?:/bar)?(?:/baz)?\Z}, Strexp.compile('/foo(/bar)(/baz)') end def test_multiple_optional_segments assert_equal %r{\A(?:/foo)?(?:/bar)?(?:/baz)?\Z}, Strexp.compile('(/foo)(/bar)(/baz)') end def test_escapes_optional_segment_parenthesis assert_equal %r{\A/foo\(/bar\)\Z}, Strexp.compile('/foo\(/bar\)') end def test_escapes_one_optional_segment_parenthesis assert_equal %r{\A/foo\((?:/bar)?\Z}, Strexp.compile('/foo\((/bar)') end def test_raises_regexp_error_if_optional_segment_parenthesises_are_unblanced assert_raise(RegexpError) { Strexp.compile('/foo((/bar)') } assert_raise(RegexpError) { Strexp.compile('/foo(/bar))') } end end rack-mount-0.8.3/test/test_regexp_with_named_groups.rb0000644000175000017500000000354212307336063022252 0ustar jonasjonasrequire 'abstract_unit' unless supports_named_captures? class TestRegexpWithNamedGroups < Test::Unit::TestCase RegexpWithNamedGroups = Rack::Mount::RegexpWithNamedGroups def test_simple_regexp regexp = RegexpWithNamedGroups.new(/foo/) assert_equal(/foo/, regexp) assert_equal([], regexp.names) assert_equal({}, regexp.named_captures) end def test_simple_string regexp = RegexpWithNamedGroups.new('foo') assert_equal(/foo/, regexp) assert_equal([], regexp.names) assert_equal({}, regexp.named_captures) end def test_regexp_with_captures regexp = RegexpWithNamedGroups.new(/(bar|baz)/) assert_equal(/(bar|baz)/, regexp) assert_equal([], regexp.names) assert_equal({}, regexp.named_captures) end def test_regexp_with_named_captures regexp = RegexpWithNamedGroups.new(/(?:bar|baz)/) assert_equal(/(bar|baz)/, regexp) assert_equal(['foo'], regexp.names) assert_equal({'foo' => [1]}, regexp.named_captures) end def test_regexp_with_non_captures regexp = RegexpWithNamedGroups.new(/(?:foo)(?:baz)/) assert_equal(/(?:foo)(baz)/, regexp) assert_equal(['bar'], regexp.names) assert_equal({'bar' => [1]}, regexp.named_captures) end def test_ignores_noncapture_indexes regexp = RegexpWithNamedGroups.new(/foo(?:bar)(?:baz)/) assert_equal(/foo(?:bar)(baz)/, regexp) assert_equal(['baz'], regexp.names) assert_equal({ 'baz' => [1]}, regexp.named_captures) end def test_ignores_noncapture_regexp_options regexp = RegexpWithNamedGroups.new(/foo(?:(?i-mx:bar))(?:baz)/) assert_equal(/foo((?i-mx:bar))(baz)/, regexp) assert_equal(['bar', 'baz'], regexp.names) assert_equal({ 'bar' => [1], 'baz' => [2]}, regexp.named_captures) end end end rack-mount-0.8.3/test/test_prefix.rb0000644000175000017500000000214712307336063016457 0ustar jonasjonasrequire 'abstract_unit' class TestPrefix < Test::Unit::TestCase Prefix = Rack::Mount::Prefix def test_path_prefix_shifting @app = Prefix.new(EchoApp, '/foo') get '/foo/bar' assert_success assert_equal '/bar', env['PATH_INFO'] assert_equal '/foo', env['SCRIPT_NAME'] end def test_path_prefix_restores_original_path_when_it_leaves_the_scope @app = Prefix.new(EchoApp, '/foo') env = {'PATH_INFO' => '/foo/bar', 'SCRIPT_NAME' => ''} @app.call(env) assert_equal({'PATH_INFO' => '/foo/bar', 'SCRIPT_NAME' => ''}, env) end def test_path_prefix_shifting_doesnt_normalize_path @app = Prefix.new(EchoApp, '/foo') get '/foo/bar' assert_success assert_equal '/bar', env['PATH_INFO'] assert_equal '/foo', env['SCRIPT_NAME'] get '/foo/bar/' assert_success assert_equal '/bar/', env['PATH_INFO'] assert_equal '/foo', env['SCRIPT_NAME'] end def test_path_prefix_shifting_with_root @app = Prefix.new(EchoApp, '/foo') get '/foo' assert_success assert_equal '', env['PATH_INFO'] assert_equal '/foo', env['SCRIPT_NAME'] end end rack-mount-0.8.3/test/test_recognition.rb0000644000175000017500000004134212307336063017502 0ustar jonasjonas# encoding: utf-8 require 'abstract_unit' require 'fixtures/basic_set' class TestRecognition < Test::Unit::TestCase def setup @app = BasicSet assert !set_included_modules.include?(Rack::Mount::CodeGeneration) end def test_raw_recognize assert_recognizes({ :controller => 'foo', :action => 'index' }, '/foo') assert_recognizes({ :controller => 'foo_bar', :action => 'index' }, '/foo/bar') assert_recognizes({ :controller => 'homepage' }, '/') assert_recognizes({ :controller => 'sessions', :action => 'new' }, '/login') assert_recognizes({ :controller => 'people', :action => 'show', :id => '1' }, '/people/1') assert_recognizes(nil, '/admin/widgets/show/random') end def test_recognize_with_block req = Rack::Request.new(Rack::MockRequest.env_for('/foo')) results = [] @app.recognize(req) { |route, matches, params| results << params } assert_equal([ { :controller => 'foo', :action => 'index' }, { :controller => 'foo', :action => 'shadowed' } ], results) end def test_path get '/foo' assert_success assert_equal({ :controller => 'foo', :action => 'index' }, routing_args) post '/foo' assert_success assert_equal({ :controller => 'foo', :action => 'index' }, routing_args) put '/foo' assert_success assert_equal({ :controller => 'foo', :action => 'index' }, routing_args) delete '/foo' assert_success assert_equal({ :controller => 'foo', :action => 'index' }, routing_args) end def test_nested_path get '/foo/bar' assert_success assert_equal({ :controller => 'foo_bar', :action => 'index' }, routing_args) end def test_path_mapped_with_leading_slash get '/baz' assert_success assert_equal({ :controller => 'baz', :action => 'index' }, routing_args) end def test_path_does_get_shadowed get '/people' assert_success assert_equal({ :controller => 'people', :action => 'index' }, routing_args) get '/people/new' assert_success assert_equal({ :controller => 'people', :action => 'new' }, routing_args) end def test_root_path get '/' assert_success assert_equal({ :controller => 'homepage' }, routing_args) end def test_another_with_controller_scope get '/login' assert_success assert_equal({ :controller => 'sessions', :action => 'new' }, routing_args) post '/login' assert_success assert_equal({ :controller => 'sessions', :action => 'create' }, routing_args) get '/logout' assert_not_found delete '/logout' assert_success assert_equal({ :controller => 'sessions', :action => 'destroy' }, routing_args) end def test_only_method_condition delete '/all' assert_success assert_equal({ :controller => 'global', :action => 'destroy' }, routing_args) get '/all' assert_not_found end def test_schema_condition get '/ssl', 'rack.url_scheme' => 'http' assert_success assert_equal({ :controller => 'ssl', :action => 'nonssl' }, routing_args) get '/ssl', 'rack.url_scheme' => 'https' assert_success assert_equal({ :controller => 'ssl', :action => 'ssl' }, routing_args) end def test_host_condition get '/host', 'HTTP_HOST' => '37s.backpackit.com' assert_success assert_equal({ :controller => 'account', :account => '37s' }, routing_args) get '/host', 'HTTP_HOST' => 'josh.backpackit.com' assert_success assert_equal({ :controller => 'account', :account => 'josh' }, routing_args) end def test_full_uri_condition get '/full/foo' assert_success assert_equal({ :scheme => 'http', :host => 'example.org', :foo => 'foo' }, routing_args) end def test_xhr_boolean_condition get '/xhr', 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest' assert_success assert_equal({ :controller => 'xhr' }, routing_args) get '/xhr' assert_not_found end def test_slashes get '/slashes/trailing/' assert_success assert_equal({ :controller => 'slash', :action => 'trailing' }, routing_args) get '/slashes/trailing' assert_success assert_equal({ :controller => 'slash', :action => 'trailing' }, routing_args) get '/slashes/repeated' assert_success assert_equal({ :controller => 'slash', :action => 'repeated' }, routing_args) end def test_not_found get '/admin/widgets/show/random' assert_not_found end def test_extracts_id get '/people/1' assert_success assert_equal({ :controller => 'people', :action => 'show', :id => '1' }, routing_args) put '/people/1' assert_success assert_equal({ :controller => 'people', :action => 'update', :id => '1' }, routing_args) delete '/people/1' assert_success assert_equal({ :controller => 'people', :action => 'destroy', :id => '1' }, routing_args) get '/people/2/edit' assert_success assert_equal({ :controller => 'people', :action => 'edit', :id => '2' }, routing_args) end def test_requirements get '/geocode/60614' assert_success assert_equal({ :controller => 'geocode', :action => 'show', :postalcode => '60614' }, routing_args) get '/geocode2/60614' assert_success assert_equal({ :controller => 'geocode', :action => 'show', :postalcode => '60614' }, routing_args) end def test_path_with_globbing get '/files/images/photo.jpg' assert_success assert_equal({ :controller => 'files', :action => 'index', :files => 'images/photo.jpg' }, routing_args) end def test_path_with_key_with_slash get '/files2/images/photo' assert_success assert_equal({ :controller => 'files2', :action => 'show', :key => 'images/photo' }, routing_args) end def test_with_controller_scope get '/global/index' assert_success assert_equal({ :controller => 'global', :action => 'index' }, routing_args) get '/global/show' assert_success assert_equal({ :controller => 'global', :action => 'show' }, routing_args) get '/global/export' assert_success assert_equal({ :controller => 'global', :action => 'export' }, routing_args) get '/global/hide_notice' assert_success assert_equal({ :controller => 'global', :action => 'hide_notice' }, routing_args) get '/export/1/foo' assert_success assert_equal({ :controller => 'global', :action => 'export', :id => '1', :file => 'foo' }, routing_args) end def test_optional_route get '/optional/index' assert_success assert_equal({ :controller => 'optional', :action => 'index' }, routing_args) get '/optional/index.xml' assert_success assert_equal({ :controller => 'optional', :action => 'index', :format => 'xml' }, routing_args) end def test_namespaced_resources get '/account/subscription' assert_success assert_equal({ :controller => 'account/subscription', :action => 'index' }, routing_args) get '/account/credit' assert_success assert_equal({ :controller => 'account/credit', :action => 'index' }, routing_args) end def test_nested_route get '/admin/users' assert_success assert_equal({ :controller => 'admin/users' }, routing_args) get '/admin/groups' assert_success assert_equal({ :controller => 'admin/groups' }, routing_args) end def test_regexp get '/regexp/foo/bar/123' assert_success assert_equal({ :controller => 'foo', :action => 'bar', :id => '123' }, routing_args) get '/regexp/foos/baz/123' assert_success assert_equal({ :controller => 'foo', :action => 'baz', :id => '123' }, routing_args) get '/regexp/bar/abc/123' assert_success assert_equal({ :controller => 'foo', :action => 'abc', :id => '123' }, routing_args) get '/regexp/baz/abc/123' assert_success assert_equal({ :controller => 'foo' }, routing_args) get '/regexp/bars/foo/baz' assert_not_found end def test_unnamed_capture get '/death/star' assert_success assert_equal({ :controller => 'star' }, routing_args) get '/new/death/star' assert_success assert_equal({ :controller => 'star' }, routing_args) get '/death.wsdl/star' assert_success assert_equal({ :controller => 'star' }, routing_args) end def test_method_regexp get '/method' assert_success assert_equal({ :controller => 'method', :action => 'index' }, routing_args) post '/method' assert_success assert_equal({ :controller => 'method', :action => 'index' }, routing_args) put '/method' assert_not_found end def test_default_route_extracts_parameters get '/default/foo/bar/1.xml' assert_success assert_equal({ :controller => 'foo', :action => 'bar', :id => '1', :format => 'xml' }, routing_args) get '/default/foo/bar/1' assert_success assert_equal({ :controller => 'foo', :action => 'bar', :id => '1' }, routing_args) get '/default/foo/bar' assert_success assert_equal({ :controller => 'foo', :action => 'bar' }, routing_args) get '/default/foo' assert_success assert_equal({ :controller => 'foo' }, routing_args) end def test_params_override_defaults get '/params_with_defaults/bar' assert_success assert_equal({ :params_with_defaults => true, :controller => 'bar' }, routing_args) get '/params_with_defaults' assert_success assert_equal({ :params_with_defaults => true, :controller => 'foo' }, routing_args) end def test_escaped_optional_capture get '/escaped/(foo)' assert_success assert_equal({ :controller => 'escaped/foo' }, routing_args) end def test_path_prefix get '/prefix/foo/bar/1' assert_success assert_equal({ :controller => 'foo', :action => 'bar', :id => '1' }, routing_args) assert_equal '/foo/bar/1', @env['PATH_INFO'] assert_equal '/prefix', @env['SCRIPT_NAME'] get '/prefix2/foo/bar/1' assert_success assert_equal({}, routing_args) assert_equal '/foo/bar/1', @env['PATH_INFO'] assert_equal '/prefix2', @env['SCRIPT_NAME'] get '/prefix2.foo/bar/1' assert_success assert_equal({}, routing_args) assert_equal '.foo/bar/1', @env['PATH_INFO'] assert_equal '/prefix2', @env['SCRIPT_NAME'] end def test_case_insensitive_path get '/ignorecase/foo' assert_success assert_equal({ :controller => 'ignorecase' }, routing_args) get '/ignorecase/FOO' assert_success assert_equal({ :controller => 'ignorecase' }, routing_args) get '/ignorecase/Foo' assert_success assert_equal({ :controller => 'ignorecase' }, routing_args) get '/ignorecase/josh/1' assert_success assert_equal({ :controller => 'ignorecase', :name => 'josh', :id => '1' }, routing_args) end def test_extended_path get '/extended/foo' assert_success assert_equal({ :controller => 'extended' }, routing_args) end def test_static_group get '/static_group/foobar' assert_success end def test_uri_escaping get '/uri_escaping/foo' assert_success assert_equal({ :controller => 'uri_escaping', :value => 'foo' }, routing_args) get '/uri_escaping/foo%20bar' assert_success assert_equal({ :controller => 'uri_escaping', :value => 'foo bar' }, routing_args) get '/uri_escaping/%E2%88%9E' assert_success assert_equal({ :controller => 'uri_escaping', :value => '∞' }, routing_args) end def test_nested_routing_parameters_are_merged_with_parents get '/nested/123/ok' assert_success assert_equal({ :set => 'A', :id => '123', :response => 'ok' }, routing_args) end def test_nested_routing_parameters_after_cascade get '/nested/123/pass' assert_success assert_equal({ :set => 'B', :id => '123' }, routing_args) end private def new_route_set(*args, &block) Rack::Mount::RouteSet.new_without_optimizations(*args, &block) end end class TestOptimizedRecognition < TestRecognition def setup @app = OptimizedBasicSet assert set_included_modules.include?(Rack::Mount::CodeGeneration) end private def new_route_set(*args, &block) Rack::Mount::RouteSet.new(*args, &block) end end class TestLinearRecognition < TestRecognition def setup @app = LinearBasicSet end private def new_route_set(*args, &block) Rack::Mount::RouteSet.new_with_linear_graph(*args, &block) end end class TestRecognitionSplitKeyEdgeCases < Test::Unit::TestCase def test_path_prefix_without_split_keys @app = new_route_set do |set| induce_recognition_keys(set, %w( . )) set.add_route(EchoApp, :path_info => %r{^/foo}) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/bar.:format')) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/baz.:format')) end get '/foo' assert_success get '/foo/bar' assert_success get '/bar.html' assert_success get '/baz.xml' assert_success end def test_small_set_with_ambiguous_splitting @app = new_route_set do |set| induce_recognition_keys(set, %w( / . s )) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/signin')) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/messages/:id')) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/posts(.:format)')) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/blog', {}, [], false)) end get '/signin' assert_success get '/messages/1' assert_success get '/posts' assert_success get '/posts.xml' assert_success get '/blog' assert_success get '/blog/archives' assert_success end def test_set_with_leading_split_char @app = new_route_set do |set| induce_recognition_keys(set, %w( . / s ), 1, [:request_method]) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/sa'), :request_method => 'POST') set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/:section', {}, %w( . / )), :request_method => 'GET') end post '/sa' assert_success assert_equal({}, routing_args) get '/sa' assert_success assert_equal({ :section => 'sa' }, routing_args) get '/s' assert_success assert_equal({ :section => 's' }, routing_args) end def test_double_split_char_is_last @app = new_route_set do |set| induce_recognition_keys(set, %w( . / s ), 3) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/foo/as')) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/foos/bs')) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/foo/css')) end get '/foo/as' assert_success get '/foos/bs' assert_success get '/foo/css' assert_success end def test_set_without_slash_in_seperators @app = new_route_set do |set| induce_recognition_keys(set, %w( . )) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/foo.:format')) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/bar.:format')) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/baz.:format')) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/blog', {}, [], false)) end get '/foo.html' assert_success get '/bar.xml' assert_success get '/baz.json' assert_success get '/blog' assert_success get '/blog/archives' assert_success end def test_set_without_split_keys @app = new_route_set do |set| induce_recognition_keys(set, :path_info) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/foo')) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/bar')) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/baz')) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/blog', {}, [], false)) end get '/foo' assert_success get '/bar' assert_success get '/baz' assert_success get '/blog' assert_success get '/blog/archives' assert_success end def test_small_set_with_unbound_path @app = new_route_set do |set| set.add_route(EchoApp, :path_info => %r{^/foo}) set.add_route(EchoApp, :path_info => %r{^/bar}) end get '/foo' assert_success get '/foo/bar' assert_success end private def new_route_set(*args, &block) Rack::Mount::RouteSet.new(*args, &block) end SplitKey = Rack::Mount::Analysis::Splitting::Key def induce_recognition_keys(set, separators, count = 1, extras = []) keys = [] separators_hash = {} if separators.is_a?(Array) count.times do |index| keys << SplitKey.new(:path_info, index, Regexp.union(*separators)) end separators_hash[:path_info] = separators else keys << separators end keys += extras (class << set; self; end).instance_eval do define_method :build_recognition_keys do keys end end end end rack-mount-0.8.3/test/abstract_unit.rb0000644000175000017500000000272212307336063016764 0ustar jonasjonasrequire 'test/unit' require 'rack/mount' require 'fixtures' class Test::Unit::TestCase private def set_included_modules class << @app; included_modules; end end def env @env end def response @response end def routing_args_key 'rack.routing_args' end def routing_args @env[routing_args_key] end def assert_recognizes(params, path) req = Rack::Request.new(Rack::MockRequest.env_for(path)) _, _, actual_params = @app.recognize(req) assert_equal(params, actual_params) end def get(path, options = {}) process(path, options.merge(:method => 'GET')) end def post(path, options = {}) process(path, options.merge(:method => 'POST')) end def put(path, options = {}) process(path, options.merge(:method => 'PUT')) end def delete(path, options = {}) process(path, options.merge(:method => 'DELETE')) end def process(path, options = {}) @path = path require 'rack/mock' env = Rack::MockRequest.env_for(path, options) @response = @app.call(env) if @response && @response[0] == 200 @env = YAML.load(@response[2][0]) else @env = nil end end def assert_success assert(@response) assert_equal(200, @response[0], "No route matches #{@path.inspect}") end def assert_not_found assert(@response) assert_equal(404, @response[0]) end end rack-mount-0.8.3/test/fixtures/0000755000175000017500000000000012307336063015443 5ustar jonasjonasrack-mount-0.8.3/test/fixtures/optimized_basic_set.rb0000644000175000017500000000007412307336063022011 0ustar jonasjonasOptimizedBasicSet = Rack::Mount::RouteSet.new(&BasicSetMap) rack-mount-0.8.3/test/fixtures/basic_set_map.rb0000644000175000017500000000027412307336063020564 0ustar jonasjonasfile = File.join(File.dirname(__FILE__), 'basic_set_map_19.rb') if supports_named_captures? load(file) else src = File.read(file) src.gsub!(/\?<([^>]+)>/, '?:<\1>') eval(src) end rack-mount-0.8.3/test/fixtures/basic_set.rb0000644000175000017500000000011112307336063017715 0ustar jonasjonasBasicSet = Rack::Mount::RouteSet.new_without_optimizations(&BasicSetMap) rack-mount-0.8.3/test/fixtures/nested_set_b.rb0000644000175000017500000000015012307336063020422 0ustar jonasjonasNestedSetB = Rack::Mount::RouteSet.new do |set| set.add_route(EchoApp, { :path_info => '/pass' }) end rack-mount-0.8.3/test/fixtures/default_set.rb0000644000175000017500000000063412307336063020272 0ustar jonasjonasDefaultSet = Rack::Mount::RouteSet.new do |set| if Regin.regexp_supports_named_captures? re = eval('%r{^/(?[a-z0-9]+)(/(?[a-z0-9]+)(/(?[a-z0-9]+)(\.(?[a-z]+))?)?)?$}') set.add_route(EchoApp, :path_info => re) else set.add_route(EchoApp, :path_info => %r{^/(?:[a-z0-9]+)(/(?:[a-z0-9]+)(/(?:[a-z0-9]+)(\.(?:[a-z]+))?)?)?$}) end end rack-mount-0.8.3/test/fixtures/nested_set_a.rb0000644000175000017500000000037312307336063020430 0ustar jonasjonasNestedSetA = Rack::Mount::RouteSet.new do |set| set.add_route(EchoApp, { :path_info => '/ok' }, { :response => 'ok' }) set.add_route(lambda { |env| [404, {'X-Cascade' => 'pass'}, []] }, { :path_info => '/pass' }, { :response => 'not_found' }) end rack-mount-0.8.3/test/fixtures/linear_basic_set.rb0000644000175000017500000000063312307336063021260 0ustar jonasjonasmodule ForceLinearGraph private def build_generation_keys [] end def build_recognition_keys [] end end class << Rack::Mount::RouteSet def new_with_linear_graph(options = {}, &block) set = new_without_optimizations(options, &block) set.extend(ForceLinearGraph) set.rehash set end end LinearBasicSet = Rack::Mount::RouteSet.new_with_linear_graph(&BasicSetMap) rack-mount-0.8.3/test/fixtures/basic_set_map_19.rb0000644000175000017500000002641112307336063021076 0ustar jonasjonasBasicSetMap = Proc.new do |set| set.add_route(EchoApp, { :path_info => %r{^/people(\.(?[a-z]+))?$}, :request_method => 'GET' }, { :controller => 'people', :action => 'index' }) set.add_route(EchoApp, { :path_info => %r{^/people(\.(?[a-z]+))?$}, :request_method => 'POST' }, { :controller => 'people', :action => 'create' }) set.add_route(EchoApp, { :path_info => %r{^/people/new(\.(?[a-z]+))?$}, :request_method => 'GET' }, { :controller => 'people', :action => 'new' }) set.add_route(EchoApp, { :path_info => %r{^/people/(?[^/]+)/edit(\.(?[a-z]+))?$}, :request_method => 'GET' }, { :controller => 'people', :action => 'edit' }) set.add_route(EchoApp, { :path_info => %r{^/people/(?[^/]+)(\.(?[a-z]+))?$}, :request_method => 'GET' }, { :controller => 'people', :action => 'show' }, :person) set.add_route(EchoApp, { :path_info => %r{^/people/(?[^/]+)(\.(?[a-z]+))?$}, :request_method => 'PUT' }, { :controller => 'people', :action => 'update' }) set.add_route(EchoApp, { :path_info => %r{^/people/(?[^/]+)(\.(?[a-z]+))?$}, :request_method => 'DELETE' }, { :controller => 'people', :action => 'destroy' }) set.add_route(EchoApp, { :path_info => '/' }, { :controller => 'homepage' }, :root) set.add_route(EchoApp, { :path_info => %r{^/ws/(?[a-z]+)(/(?[a-z]+)(/(?[0-9]+))?)?$} }, { :ws => true }) set.add_route(EchoApp, { :path_info => %r{^/geocode/(?\d{5}(-\d{4})?)$} }, { :controller => 'geocode', :action => 'show' }, :geocode) set.add_route(EchoApp, { :path_info => %r{^/geocode2/(?\d{5}(-\d{4})?)$} }, { :controller => 'geocode', :action => 'show' }, :geocode2) set.add_route(EchoApp, { :path_info => '/login', :request_method => 'GET' }, { :controller => 'sessions', :action => 'new' }, :login) set.add_route(EchoApp, { :path_info => '/login', :request_method => 'POST' }, { :controller => 'sessions', :action => 'create' }) set.add_route(EchoApp, { :path_info => '/logout', :request_method => 'DELETE' }, { :controller => 'sessions', :action => 'destroy' }, :logout) set.add_route(EchoApp, { :path_info => %r{^/global/(?[a-z0-9]+)$} }, { :controller => 'global' }) set.add_route(EchoApp, { :path_info => %r{^/global/export$} }, { :controller => 'global', :action => 'export' }, :export_request) set.add_route(EchoApp, { :path_info => %r{^/global/hide_notice$} }, { :controller => 'global', :action => 'hide_notice' }, :hide_notice) set.add_route(EchoApp, { :path_info => %r{^/export/(?[a-z0-9]+)/(?.*)$} }, { :controller => 'global', :action => 'export' }, :export_download) set.add_route(EchoApp, { :path_info => %r{^/account/subscription(\.(?[a-z]+))?$}, :request_method => 'GET' }, { :controller => 'account/subscription', :action => 'index' }) set.add_route(EchoApp, { :path_info => %r{^/account/subscription(\.(?[a-z]+))?$}, :request_method => 'POST' }, { :controller => 'account/subscription', :action => 'create' }) set.add_route(EchoApp, { :path_info => %r{^/account/subscription/new(\.(?[a-z]+))?$}, :request_method => 'GET' }, { :controller => 'account/subscription', :action => 'new' }) set.add_route(EchoApp, { :path_info => %r{^/account/subscription/(?[a-z0-9]+)/edit(\.(?[a-z]+))?$}, :request_method => 'GET' }, { :controller => 'account/subscription', :action => 'edit' }) set.add_route(EchoApp, { :path_info => %r{^/account/subscription/(?[a-z0-9]+)(\.(?[a-z]+))?$}, :request_method => 'GET' }, { :controller => 'account/subscription', :action => 'show' }) set.add_route(EchoApp, { :path_info => %r{^/account/subscription/(?[a-z0-9]+)(\.(?[a-z]+))?$}, :request_method => 'PUT' }, { :controller => 'account/subscription', :action => 'update' }) set.add_route(EchoApp, { :path_info => %r{^/account/subscription/(?[a-z0-9]+)(\.(?[a-z]+))?$}, :request_method => 'DELETE' }, { :controller => 'account/subscription', :action => 'destroy' }) set.add_route(EchoApp, { :path_info => %r{^/account/credit(\.(?[a-z]+))?$}, :request_method => 'GET' }, { :controller => 'account/credit', :action => 'index' }) set.add_route(EchoApp, { :path_info => %r{^/account/credit(\.(?[a-z]+))?$}, :request_method => 'POST' }, { :controller => 'account/credit', :action => 'create' }) set.add_route(EchoApp, { :path_info => %r{^/account/credit/new(\.(?[a-z]+))?$}, :request_method => 'GET' }, { :controller => 'account/credit', :action => 'new' }) set.add_route(EchoApp, { :path_info => %r{^/account/credit/(?[a-z0-9]+)/edit(\.(?[a-z]+))?$}, :request_method => 'GET' }, { :controller => 'account/credit', :action => 'edit' }) set.add_route(EchoApp, { :path_info => %r{^/account/credit/(?[a-z0-9]+)(\.(?[a-z]+))?$}, :request_method => 'GET' }, { :controller => 'account/credit', :action => 'show' }) set.add_route(EchoApp, { :path_info => %r{^/account/credit/(?[a-z0-9]+)(\.(?[a-z]+))?$}, :request_method => 'PUT' }, { :controller => 'account/credit', :action => 'update' }) set.add_route(EchoApp, { :path_info => %r{^/account/credit/(?[a-z0-9]+)(\.(?[a-z]+))?$}, :request_method => 'DELETE' }, { :controller => 'account/credit', :action => 'destroy' }) set.add_route(EchoApp, { :path_info => %r{^/account/credit_card(\.(?[a-z]+))?$}, :request_method => 'GET' }, { :controller => 'account/credit_card', :action => 'index' }) set.add_route(EchoApp, { :path_info => %r{^/account/credit_card(\.(?[a-z]+))?$}, :request_method => 'POST' }, { :controller => 'account/credit_card', :action => 'create' }) set.add_route(EchoApp, { :path_info => %r{^/account/credit_card/new(\.(?[a-z]+))?$}, :request_method => 'GET' }, { :controller => 'account/credit_card', :action => 'new' }) set.add_route(EchoApp, { :path_info => %r{^/account/credit_card/(?[a-z0-9]+)/edit(\.(?[a-z]+))?$}, :request_method => 'GET' }, { :controller => 'account/credit_card', :action => 'edit' }) set.add_route(EchoApp, { :path_info => %r{^/account/credit_card/(?[a-z0-9]+)(\.(?[a-z]+))?$}, :request_method => 'GET' }, { :controller => 'account/credit_card', :action => 'show' }) set.add_route(EchoApp, { :path_info => %r{^/account/credit_card/(?[a-z0-9]+)(\.(?[a-z]+))?$}, :request_method => 'PUT' }, { :controller => 'account/credit_card', :action => 'update' }) set.add_route(EchoApp, { :path_info => %r{^/account/credit_card/(?[a-z0-9]+)(\.(?[a-z]+))?$}, :request_method => 'DELETE' }, { :controller => 'account/credit_card', :action => 'destroy' }) set.add_route(EchoApp, { :path_info => %r{^/account2(/(?[a-z]+))?$} }, { :controller => 'account2', :action => 'subscription' }) set.add_route(EchoApp, :path_info => %r{^/(?admin/users|admin/groups)$}) set.add_route(EchoApp, { :path_info => %r{^/feed/(?[a-z]+)$} }, { :controller => 'feed', :kind => 'rss' }, :feed) set.add_route(EchoApp, { :path_info => %r{^/feed2(\.(?[a-z]+))?$} }, { :controller => 'feed2', :format => 'rss' }, :feed2) set.add_route(EchoApp, { :path_info => Rack::Mount::Utils.normalize_path('foo') }, { :controller => 'foo', :action => 'index' }) set.add_route(EchoApp, { :path_info => Rack::Mount::Utils.normalize_path('foo/bar') }, { :controller => 'foo_bar', :action => 'index' }) set.add_route(EchoApp, { :path_info => Rack::Mount::Utils.normalize_path('/baz') }, { :controller => 'baz', :action => 'index' }) set.add_route(EchoApp, { :path_info => Rack::Mount::Utils.normalize_path('foo') }, { :controller => 'foo', :action => 'shadowed' }) set.add_route(EchoApp, { :path_info => Rack::Mount::Utils.normalize_path('/slashes/trailing/') }, { :controller => 'slash', :action => 'trailing' }) set.add_route(EchoApp, { :path_info => Rack::Mount::Utils.normalize_path('//slashes/repeated') }, { :controller => 'slash', :action => 'repeated' }) set.add_route(EchoApp, { :path_info => '/ssl', :scheme => 'http' }, { :controller => 'ssl', :action => 'nonssl' }) set.add_route(EchoApp, { :path_info => '/ssl', :scheme => 'https' }, { :controller => 'ssl', :action => 'ssl' }) set.add_route(EchoApp, { :path_info => '/method', :request_method => /get|post/i }, { :controller => 'method', :action => 'index' }) set.add_route(EchoApp, { :path_info => '/host', :host => %r{^(?[0-9a-z]+)\.backpackit\.com$} }, { :controller => 'account' }) set.add_route(EchoApp, { :path_info => '/xhr', :xhr? => true }, { :controller => 'xhr' }) set.add_route(EchoApp, { :url => %r{^(?.+)://(?.+)/full/(?.+)$} }, {}, :full_url) set.add_route(EchoApp, { :path_info => %r{^/static_group/(foo)(bar)$} }) set.add_route(EchoApp, { :path_info => %r{^/optional/index(\.(?[a-z]+))?$} }, { :controller => 'optional', :action => 'index' }) set.add_route(EchoApp, { :path_info => %r{^/regexp/foos?/(?bar|baz)/(?[a-z0-9]+)$} }, { :controller => 'foo' }) set.add_route(EchoApp, { :path_info => %r{^/regexp/bar/(?[a-z]+)/(?[0-9]+)$} }, { :controller => 'foo' }, :complex_regexp) set.add_route(EchoApp, { :path_info => %r{^/regexp/baz/[a-z]+/[0-9]+$} }, { :controller => 'foo' }, :complex_regexp_fail) set.add_route(EchoApp, { :path_info => %r{^/escaped/\(foo\)$} }, { :controller => 'escaped/foo' }, :escaped_optional_capture) set.add_route(EchoApp, { :path_info => %r{^/ignorecase/(?(?i-mx:josh))$} }, { :controller => 'ignorecase' }, :ignore) set.add_route(EchoApp, { :path_info => %r{^/ignorecase/(?(?i-mx:josh))/(?[0-9]+)$} }, { :controller => 'ignorecase' }, :ignore_with_id) set.add_route(EchoApp, { :path_info => %r{^/ignorecase/foo$}i }, { :controller => 'ignorecase' }) set.add_route(EchoApp, { :path_info => (/^\/extended\/ # comment foo # bar $/x) }, { :controller => 'extended' }) set.add_route(EchoApp, { :path_info => %r{^/uri_escaping/(?.+)$} }, { :controller => 'uri_escaping' }) set.add_route(EchoApp, { :path_info => %r{^/files/(?.*)$} }, { :controller => 'files', :action => 'index' }) set.add_route(EchoApp, { :path_info => %r{^/files2/(?[A-Za-z0-9/]*)$} }, { :controller => 'files2', :action => 'show' }) set.add_route(EchoApp, { :path_info => %r{^/pages/(?[0-9]+)/(?[a-z0-9]+)(/(?[a-z0-9]+)(/(?[a-z0-9]+)(\.(?[a-z]+))?)?)?$} }, {}, :page) set.add_route(EchoApp, { :path_info => %r{^/params_with_defaults(/(?[a-z0-9]+))?$} }, { :params_with_defaults => true, :controller => 'foo' }) set.add_route(EchoApp, :path_info => %r{^/default/(?[a-z0-9]+)(/(?[a-z0-9]+)(/(?[a-z0-9]+)(\.(?[a-z]+))?)?)?$}) set.add_route(EchoApp, { :request_method => 'DELETE' }, { :controller => 'global', :action => 'destroy' }) set.add_route(lambda { |env| [404, {'X-Cascade' => 'pass'}, []] }, { :path_info => %r{^/prefix} }) set.add_route(DefaultSet, { :path_info => %r{^/prefix} }, {}, :prefix) set.add_route(EchoApp, { :path_info => %r{^/prefix2} }, {}, :prefix2) set.add_route(EchoApp, { :path_info => %r{^/(.*)/star$} }, { :controller => 'star' }) set.add_route(NestedSetA, { :path_info => %r{^/nested/(?[0-9]+)}}, { :set => 'A' }) set.add_route(NestedSetB, { :path_info => %r{^/nested/(?[0-9]+)}}, { :set => 'B' }) set.add_route(EchoApp, { :path_info => %r{^/nested/(?[0-9]+)}}, {}) end rack-mount-0.8.3/test/fixtures/echo_app.rb0000644000175000017500000000017212307336063017546 0ustar jonasjonasrequire 'yaml' class EchoApp def self.call(env) [200, {'Content-Type' => 'text/yaml'}, [YAML.dump(env)]] end end rack-mount-0.8.3/test/test_generatable_regexp.rb0000644000175000017500000002441112307336063021003 0ustar jonasjonasrequire 'abstract_unit' class TestGeneratableRegexp < Test::Unit::TestCase GeneratableRegexp = Rack::Mount::GeneratableRegexp DynamicSegment = GeneratableRegexp::DynamicSegment def test_static regexp = GeneratableRegexp.compile(%r{^GET$}) assert_equal(['GET'], regexp.segments) assert_equal [], regexp.captures assert_equal [], regexp.required_captures assert_equal 'GET', regexp.generate end def test_unescape_static regexp = GeneratableRegexp.compile(%r{^37s\.backpackit\.com$}) assert_equal [], regexp.captures assert_equal [], regexp.required_captures assert_equal(['37s.backpackit.com'], regexp.segments) assert_equal '37s.backpackit.com', regexp.generate end def test_without_capture_is_ungeneratable regexp = GeneratableRegexp.compile(%r{^GET|POST$}) assert !regexp.generatable? regexp = GeneratableRegexp.compile(%r{^.*$}) assert !regexp.generatable? end def test_slash regexp = GeneratableRegexp.compile(%r{^/$}) assert_equal [], regexp.captures assert_equal [], regexp.required_captures assert_equal ['/'], regexp.segments assert_equal '/', regexp.generate regexp = GeneratableRegexp.compile(%r{^/foo/bar$}) assert_equal ['/foo/bar'], regexp.segments assert_equal [], regexp.captures assert_equal [], regexp.required_captures assert_equal '/foo/bar', regexp.generate end def test_unanchored regexp = GeneratableRegexp.compile(%r{^/prefix}) assert_equal [], regexp.captures assert_equal [], regexp.required_captures assert_equal ['/prefix'], regexp.segments assert_equal '/prefix', regexp.generate end def test_capture if supports_named_captures? regexp = GeneratableRegexp.compile(eval('%r{^/foo/(?[0-9]+)$}')) else regexp = GeneratableRegexp.compile(%r{^/foo/(?:[0-9]+)$}) end assert_equal ['/foo/', DynamicSegment.new(:id, %r{\A[0-9]+\Z})], regexp.segments assert_equal [DynamicSegment.new(:id, %r{\A[0-9]+\Z})], regexp.captures assert_equal [DynamicSegment.new(:id, %r{\A[0-9]+\Z})], regexp.required_captures assert_equal '/foo/123', regexp.generate(:id => 123) assert_nil regexp.generate(:id => 'abc') end def test_leading_capture if supports_named_captures? regexp = GeneratableRegexp.compile(eval('%r{^/(?[a-z]+)/bar(\.(?[a-z]+))?$}')) else regexp = GeneratableRegexp.compile(%r{^/(?:[a-z]+)/bar(\.(?:[a-z]+))?$}) end assert_equal(['/', DynamicSegment.new(:foo, %r{\A[a-z]+\Z}), '/bar', ['.', DynamicSegment.new(:format, %r{\A[a-z]+\Z})]], regexp.segments) assert_equal [DynamicSegment.new(:foo, %r{\A[a-z]+\Z}), DynamicSegment.new(:format, %r{\A[a-z]+\Z})], regexp.captures assert_equal [DynamicSegment.new(:foo, %r{\A[a-z]+\Z})], regexp.required_captures assert_equal '/foo/bar.xml', regexp.generate(:foo => 'foo', :format => 'xml') assert_equal '/foo/bar', regexp.generate(:foo => 'foo') assert_nil regexp.generate(:format => 'xml') end def test_capture_inside_requirement if supports_named_captures? regexp = GeneratableRegexp.compile(eval('%r{^/msg/get/(?\d+(?:,\d+)*)$}')) else regexp = GeneratableRegexp.compile(%r{^/msg/get/(?:\d+(?:,\d+)*)$}) end assert_equal(['/msg/get/', DynamicSegment.new(:id, %r{\A\d+(?:,\d+)*\Z})], regexp.segments) assert_equal [DynamicSegment.new(:id, %r{\A\d+(?:,\d+)*\Z})], regexp.captures assert_equal [DynamicSegment.new(:id, %r{\A\d+(?:,\d+)*\Z})], regexp.required_captures assert_equal '/msg/get/123', regexp.generate(:id => 123) assert_nil regexp.generate(:id => 'abc') end def test_multiple_captures if supports_named_captures? regexp = GeneratableRegexp.compile(eval('%r{^/foo/(?[a-z]+)/(?[0-9]+)$}')) else regexp = GeneratableRegexp.compile(%r{^/foo/(?:[a-z]+)/(?:[0-9]+)$}) end assert_equal(['/foo/', DynamicSegment.new(:action, %r{\A[a-z]+\Z}), '/', DynamicSegment.new(:id, %r{\A[0-9]+\Z})], regexp.segments) assert_equal [DynamicSegment.new(:action, %r{\A[a-z]+\Z}), DynamicSegment.new(:id, %r{\A[0-9]+\Z})], regexp.captures assert_equal [DynamicSegment.new(:action, %r{\A[a-z]+\Z}), DynamicSegment.new(:id, %r{\A[0-9]+\Z})], regexp.required_captures assert_equal '/foo/show/1', regexp.generate(:action => 'show', :id => '1') assert_nil regexp.generate(:action => 'show') assert_nil regexp.generate(:id => '1') end def test_optional_capture if supports_named_captures? regexp = GeneratableRegexp.compile(eval('%r{^/foo/bar(\.(?[a-z]+))?$}')) else regexp = GeneratableRegexp.compile(%r{^/foo/bar(\.(?:[a-z]+))?$}) end assert_equal(['/foo/bar', ['.', DynamicSegment.new(:format, %r{\A[a-z]+\Z})]], regexp.segments) assert_equal [DynamicSegment.new(:format, %r{\A[a-z]+\Z})], regexp.captures assert_equal [], regexp.required_captures assert_equal({}, regexp.required_defaults) assert_equal '/foo/bar.xml', regexp.generate(:format => 'xml') assert_equal '/foo/bar', regexp.generate end def test_capture_with_default if supports_named_captures? regexp = GeneratableRegexp.compile(eval('%r{^/foo/bar\.(?[a-z]+)$}')) else regexp = GeneratableRegexp.compile(%r{^/foo/bar\.(?:[a-z]+)$}) end regexp.defaults[:format] = 'xml' assert_equal(['/foo/bar.', DynamicSegment.new(:format, %r{\A[a-z]+\Z})], regexp.segments) assert_equal [DynamicSegment.new(:format, %r{\A[a-z]+\Z})], regexp.captures assert_equal [], regexp.required_captures assert_equal({}, regexp.required_defaults) assert_equal '/foo/bar.json', regexp.generate(:format => 'json') assert_equal '/foo/bar.xml', regexp.generate(:format => 'xml') assert_equal '/foo/bar.xml', regexp.generate end def test_capture_with_required_default regexp = GeneratableRegexp.compile(%r{^/foo$}) regexp.defaults[:controller] = 'foo' regexp.defaults[:action] = 'index' assert_equal(['/foo'], regexp.segments) assert_equal [], regexp.captures assert_equal [], regexp.required_captures assert_equal({:controller => 'foo', :action => 'index'}, regexp.required_defaults) assert_equal nil, regexp.generate assert_equal '/foo', regexp.generate(:controller => 'foo', :action => 'index') end def test_multiple_optional_captures if supports_named_captures? regexp = GeneratableRegexp.compile(eval('%r{^/(?[a-z]+)(/(?[a-z]+))?(/(?[a-z]+))?$}')) else regexp = GeneratableRegexp.compile(%r{^/(?:[a-z]+)(/(?:[a-z]+))?(/(?:[a-z]+))?$}) end assert_equal(['/', DynamicSegment.new(:foo, %r{\A[a-z]+\Z}), ['/', DynamicSegment.new(:bar, %r{\A[a-z]+\Z})], ['/', DynamicSegment.new(:baz, %r{\A[a-z]+\Z})] ], regexp.segments) assert_equal [DynamicSegment.new(:foo, %r{\A[a-z]+\Z}), DynamicSegment.new(:bar, %r{\A[a-z]+\Z}), DynamicSegment.new(:baz, %r{\A[a-z]+\Z})], regexp.captures assert_equal [DynamicSegment.new(:foo, %r{\A[a-z]+\Z})], regexp.required_captures assert_equal '/foo/bar/baz', regexp.generate(:foo => 'foo', :bar => 'bar', :baz => 'baz') assert_equal '/foo/bar', regexp.generate(:foo => 'foo', :bar => 'bar') assert_equal '/foo', regexp.generate(:foo => 'foo') assert_nil regexp.generate end def test_capture_followed_by_an_optional_capture if supports_named_captures? regexp = GeneratableRegexp.compile(eval('%r{^/people/(?[0-9]+)(\.(?[a-z]+))?$}')) else regexp = GeneratableRegexp.compile(%r{^/people/(?:[0-9]+)(\.(?:[a-z]+))?$}) end assert_equal(['/people/', DynamicSegment.new(:id, %r{\A[0-9]+\Z}), ['.', DynamicSegment.new(:format, %r{\A[a-z]+\Z})]], regexp.segments) assert_equal [DynamicSegment.new(:id, %r{\A[0-9]+\Z}), DynamicSegment.new(:format, %r{\A[a-z]+\Z})], regexp.captures assert_equal [DynamicSegment.new(:id, %r{\A[0-9]+\Z})], regexp.required_captures assert_equal '/people/123.xml', regexp.generate(:id => '123', :format => 'xml') assert_equal '/people/123', regexp.generate(:id => '123') assert_nil regexp.generate end def test_period_seperator if supports_named_captures? regexp = GeneratableRegexp.compile(eval('%r{^/foo/(?[0-9]+)\.(?[a-z]+)$}')) else regexp = GeneratableRegexp.compile(%r{^/foo/(?:[0-9]+)\.(?:[a-z]+)$}) end assert_equal(['/foo/', DynamicSegment.new(:id, %r{\A[0-9]+\Z}), '.', DynamicSegment.new(:format, %r{\A[a-z]+\Z})], regexp.segments) assert_equal [DynamicSegment.new(:id, %r{\A[0-9]+\Z}), DynamicSegment.new(:format, %r{\A[a-z]+\Z})], regexp.captures assert_equal [DynamicSegment.new(:id, %r{\A[0-9]+\Z}), DynamicSegment.new(:format, %r{\A[a-z]+\Z})], regexp.required_captures assert_equal '/foo/123.xml', regexp.generate(:id => '123', :format => 'xml') assert_nil regexp.generate(:id => '123') end def test_escaped_capture regexp = GeneratableRegexp.compile(%r{^/foo/\(bar$}) assert_equal ['/foo/(bar'], regexp.segments assert_equal [], regexp.captures assert_equal [], regexp.required_captures assert_equal '/foo/(bar', regexp.generate end def test_seperators_inside_optional_captures if supports_named_captures? regexp = GeneratableRegexp.compile(eval('%r{^/foo(/(?[a-z]+))?$}')) else regexp = GeneratableRegexp.compile(%r{^/foo(/(?:[a-z]+))?$}) end assert_equal ['/foo', ['/', DynamicSegment.new(:action, %r{\A[a-z]+\Z})]], regexp.segments assert_equal [DynamicSegment.new(:action, %r{\A[a-z]+\Z})], regexp.captures assert_equal [], regexp.required_captures assert_equal '/foo/show', regexp.generate(:action => 'show') assert_equal '/foo', regexp.generate end def test_optional_capture_with_slash_and_dot if supports_named_captures? regexp = GeneratableRegexp.compile(eval('%r{^/foo(\.(?[a-z]+))?$}')) else regexp = GeneratableRegexp.compile(%r{^/foo(\.(?:[a-z]+))?$}) end assert_equal ['/foo', ['.', DynamicSegment.new(:format, %r{\A[a-z]+\Z})]], regexp.segments assert_equal [DynamicSegment.new(:format, %r{\A[a-z]+\Z})], regexp.captures assert_equal [], regexp.required_captures assert_equal '/foo.xml', regexp.generate(:format => 'xml') assert_equal '/foo', regexp.generate end end rack-mount-0.8.3/test/test_route_set.rb0000644000175000017500000000761412307336063017177 0ustar jonasjonasrequire 'abstract_unit' require 'fixtures/basic_set' class TestRouteSet < Test::Unit::TestCase def setup @env = Rack::MockRequest.env_for('/') @app = BasicSet assert !set_included_modules.include?(Rack::Mount::CodeGeneration) end def test_rehash_builds_graph set = new_route_set assert_raise(RuntimeError) { set.call({}) } assert_raise(RuntimeError) { set.url(@env, :foo) } set.rehash assert_nothing_raised(RuntimeError) { set.call({}) } assert_raise(Rack::Mount::RoutingError) { set.url(@env, :foo) } end def test_add_additional_routes_and_manually_rehash set = new_route_set set.add_route(EchoApp, :path_info => '/foo') set.rehash assert_nothing_raised(RuntimeError) { assert set.call({'PATH_INFO' => '/foo'}) } set.add_route(EchoApp, :path_info => '/bar') set.rehash assert_nothing_raised(RuntimeError) { assert set.call({'PATH_INFO' => '/bar'}) } end def test_ensure_routeset_needs_to_be_frozen set = new_route_set assert_raise(RuntimeError) { set.call({}) } assert_raise(RuntimeError) { set.url(@env, :foo) } set.freeze assert_nothing_raised(RuntimeError) { set.call({}) } assert_raise(Rack::Mount::RoutingError) { set.url(@env, :foo) } end def test_ensure_each_route_requires_a_valid_rack_app set = new_route_set assert_nothing_raised(ArgumentError) { set.add_route(EchoApp, :path_info => '/foo') } assert_raise(ArgumentError) { set.add_route({}) } assert_raise(ArgumentError) { set.add_route('invalid app') } end def test_ensure_route_has_valid_conditions set = new_route_set assert_nothing_raised(ArgumentError) { set.add_route(EchoApp, :path_info => '/foo') } assert_raise(ArgumentError) { set.add_route(EchoApp, nil) } assert_raise(ArgumentError) { set.add_route(EchoApp, :foo => '/bar') } end def test_dupping dupped = @app.dup assert_equal((class << @app; included_modules; end), (class << dupped; included_modules; end)) end def test_cloning cloned = @app.clone assert_equal((class << @app; included_modules; end), (class << cloned; included_modules; end)) end def test_worst_case # Make sure we aren't making the tree less efficient. Its okay if # this number gets smaller. However it may increase if the more # routes are added to the test fixture. assert_equal 11, @app.instance_variable_get('@recognition_graph').height assert_equal 13, @app.instance_variable_get('@generation_graph').height end def test_average_case # This will probably change wildly, but still an interesting # statistic to track assert_equal 7, @app.instance_variable_get('@recognition_graph').average_height.to_i assert_equal 10, @app.instance_variable_get('@generation_graph').average_height.to_i end private def new_route_set(*args, &block) Rack::Mount::RouteSet.new_without_optimizations(*args, &block) end end class TestOptimizedRouteSet < TestRouteSet def setup @env = Rack::MockRequest.env_for('/') @app = OptimizedBasicSet assert set_included_modules.include?(Rack::Mount::CodeGeneration) end def test_dupping # FIXME end private def new_route_set(*args, &block) Rack::Mount::RouteSet.new(*args, &block) end end class TestLinearRouteSet < TestRouteSet def setup @env = Rack::MockRequest.env_for('/') @app = LinearBasicSet end def test_dupping # TODO: Cleanup end def test_ensure_routeset_needs_to_be_frozen # TODO: Cleanup end def test_rehash_builds_graph # TODO: Cleanup end def test_worst_case assert_equal @app.length, @app.instance_variable_get('@recognition_graph').height end def test_average_case assert_equal @app.length, @app.instance_variable_get('@recognition_graph').average_height.to_i end private def new_route_set(*args, &block) Rack::Mount::RouteSet.new_with_linear_graph(*args, &block) end end rack-mount-0.8.3/test/fixtures.rb0000644000175000017500000000132512307336063015771 0ustar jonasjonasautoload :BasicSet, 'fixtures/basic_set' autoload :BasicSetMap, 'fixtures/basic_set_map' autoload :DefaultSet, 'fixtures/default_set' autoload :EchoApp, 'fixtures/echo_app' autoload :LinearBasicSet, 'fixtures/linear_basic_set' autoload :NestedSetA, 'fixtures/nested_set_a' autoload :NestedSetB, 'fixtures/nested_set_b' autoload :OptimizedBasicSet, 'fixtures/optimized_basic_set' module ControllerConstants def const_missing(name) if name.to_s =~ /Controller$/ const_set(name, EchoApp) else super end end end module Account extend ControllerConstants end Object.extend(ControllerConstants) def supports_named_captures? require 'rack/mount/utils' Regin.regexp_supports_named_captures? end rack-mount-0.8.3/test/test_generation.rb0000644000175000017500000001737012307336063017321 0ustar jonasjonas# encoding: utf-8 require 'abstract_unit' require 'fixtures/basic_set' class TestGeneration < Test::Unit::TestCase def setup @env = Rack::MockRequest.env_for('/') @app = BasicSet assert !set_included_modules.include?(Rack::Mount::CodeGeneration) end def test_url_with_named_route assert_equal '/login', @app.url(@env, :login, :only_path => true) assert_equal '/logout', @app.url(@env, :logout, :only_path => true) assert_equal '/geocode/60622', @app.url(@env, :geocode, :postalcode => '60622', :only_path => true) assert_equal '/', @app.url(@env, :root, :only_path => true) assert_equal '/people/1', @app.url(@env, :person, :id => '1', :only_path => true) assert_equal '/people/%231', @app.url(@env, :person, :id => '#1', :only_path => true) assert_equal '/people/number%20one', @app.url(@env, :person, :id => 'number one', :only_path => true) assert_equal '/global/export', @app.url(@env, :export_request, :only_path => true) assert_equal '/global/hide_notice', @app.url(@env, :hide_notice, :only_path => true) assert_equal '/export/1/file.txt', @app.url(@env, :export_download, :id => '1', :file => 'file.txt', :only_path => true) assert_equal '/pages/1/posts', @app.url(@env, :page, :page_id => '1', :controller => 'posts', :only_path => true) assert_equal '/pages/1/posts/show', @app.url(@env, :page, :page_id => '1', :controller => 'posts', :action => 'show', :only_path => true) assert_equal '/pages/1/posts/show/2', @app.url(@env, :page, :page_id => '1', :controller => 'posts', :action => 'show', :id => '2', :only_path => true) assert_equal '/pages/1/posts/show/2.xml', @app.url(@env, :page, :page_id => '1', :controller => 'posts', :action => 'show', :id => '2', :format => 'xml', :only_path => true) assert_equal nil, @app.url(@env, :page, :page_id => '1') assert_equal '/ignorecase/josh', @app.url(@env, :ignore, :name => 'josh', :only_path => true) assert_equal '/regexp/bar/abc/123', @app.url(@env, :complex_regexp, :action => 'abc', :id => '123', :only_path => true) assert_equal nil, @app.url(@env, :complex_regexp_fail, :only_path => true) assert_equal '/prefix', @app.url(@env, :prefix, :only_path => true) end def test_url_with_hash assert_equal '/login', @app.url(@env, :controller => 'sessions', :action => 'new', :only_path => true) assert_equal '/logout', @app.url(@env, :controller => 'sessions', :action => 'destroy', :only_path => true) assert_equal '/global/show', @app.url(@env, :controller => 'global', :action => 'show', :only_path => true) assert_equal '/global/export', @app.url(@env, :controller => 'global', :action => 'export', :only_path => true) assert_equal '/account2', @app.url(@env, :controller => 'account2', :action => 'subscription', :only_path => true) assert_equal '/account2/billing', @app.url(@env, :controller => 'account2', :action => 'billing', :only_path => true) assert_equal '/foo', @app.url(@env, :controller => 'foo', :action => 'index', :only_path => true) assert_equal '/foo/bar', @app.url(@env, :controller => 'foo_bar', :action => 'index', :only_path => true) assert_equal '/baz', @app.url(@env, :controller => 'baz', :action => 'index', :only_path => true) assert_equal '/xhr', @app.url(@env, :controller => 'xhr', :only_path => true) assert_equal '/ws/foo', @app.url(@env, :ws => true, :controller => 'foo', :only_path => true) assert_equal '/ws/foo/list', @app.url(@env, :ws => true, :controller => 'foo', :action => 'list', :only_path => true) assert_equal '/params_with_defaults', @app.url(@env, :params_with_defaults => true, :controller => 'foo', :only_path => true) assert_equal '/params_with_defaults/bar', @app.url(@env, :params_with_defaults => true, :controller => 'bar', :only_path => true) assert_equal ['/pages/1/users/show/2', {}], @app.generate(:path_info, :page_id => '1', :controller => 'users', :action => 'show', :id => '2') assert_equal ['/default/users/show/1', {}], @app.generate(:path_info, :controller => 'users', :action => 'show', :id => '1') assert_equal ['/default/users/show/1', {}], @app.generate(:path_info, {:action => 'show', :id => '1'}, {:controller => 'users'}) assert_equal ['/default/users/show/1', {}], @app.generate(:path_info, {:controller => 'users', :id => '1'}, {:action => 'show'}) assert_raise(Rack::Mount::RoutingError) { @app.url(@env, {}) } end def test_generate_host assert_equal ['josh.backpackit.com', {}], @app.generate(:host, :controller => 'account', :account => 'josh') assert_equal [{:host => 'josh.backpackit.com', :path_info => '/host'}, {}], @app.generate(:all, :controller => 'account', :account => 'josh') assert_equal [{:request_method => 'GET', :path_info => '/login'}, {}], @app.generate(:all, :login) assert_equal 'http://josh.backpackit.com/host', @app.url(@env, :controller => 'account', :account => 'josh') end def test_generate_full_url assert_equal ['http://example.com/full/bar', {}], @app.generate(:url, :full_url, :scheme => 'http', :host => 'example.com', :foo => 'bar') end def test_does_not_mutuate_params assert_equal 'http://example.org/login', @app.url(@env, {:controller => 'sessions', :action => 'new'}.freeze) assert_equal ['josh.backpackit.com', {}], @app.generate(:host, {:controller => 'account', :account => 'josh'}.freeze) end def test_url_with_query_string assert_equal '/login?token=1', @app.url(@env, :login, :token => '1', :only_path => true) assert_equal '/login?token=1', @app.url(@env, :controller => 'sessions', :action => 'new', :token => '1', :only_path => true) assert_equal '/login?token[]=1&token[]=2', @app.url(@env, :login, :token => ['1', '2'], :only_path => true) end def test_uses_default_parameters_when_non_are_passed assert_equal 'http://example.org/feed/atom', @app.url(@env, :feed, :kind => 'atom') assert_equal 'http://example.org/feed/rss', @app.url(@env, :feed, :kind => 'rss') assert_equal 'http://example.org/feed/rss', @app.url(@env, :feed) assert_equal 'http://example.org/feed2.atom', @app.url(@env, :feed2, :format => 'atom') assert_equal 'http://example.org/feed2', @app.url(@env, :feed2, :format => 'rss') assert_equal 'http://example.org/feed2', @app.url(@env, :feed2) end def test_uri_escaping assert_equal '/uri_escaping/foo', @app.url(@env, :controller => 'uri_escaping', :value => 'foo', :only_path => true) assert_equal '/uri_escaping/foo%20bar', @app.url(@env, :controller => 'uri_escaping', :value => 'foo bar', :only_path => true) assert_equal '/uri_escaping/foo%20bar', @app.url(@env, :controller => 'uri_escaping', :value => 'foo%20bar', :only_path => true) assert_equal '/uri_escaping/%E2%88%9E', @app.url(@env, :controller => 'uri_escaping', :value => '∞', :only_path => true) assert_equal '/uri_escaping/%E2%88%9E', @app.url(@env, :controller => 'uri_escaping', :value => '%E2%88%9E', :only_path => true) end def test_regexp_parse_caching @app = Rack::Mount::RouteSet.new do |set| set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/foo/:bar')) set.add_route(EchoApp, :path_info => Rack::Mount::Strexp.compile('/foo/:baz')) end assert_equal 'http://example.org/foo/1', @app.url(@env, :bar => '1') assert_equal 'http://example.org/foo/1', @app.url(@env, :baz => '1') end def test_generate_with_script_name @env['SCRIPT_NAME'] = '/blog' assert_equal 'http://example.org/blog/login', @app.url(@env, :login) end end class TestOptimizedGeneration < TestGeneration def setup @env = Rack::MockRequest.env_for('/') @app = OptimizedBasicSet assert set_included_modules.include?(Rack::Mount::CodeGeneration) end end class TestLinearGeneration < TestGeneration def setup @env = Rack::MockRequest.env_for('/') @app = LinearBasicSet end end