http_router 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +6 -5
- data/benchmarks/rack_mount.rb +16 -45
- data/benchmarks/rec2.rb +8 -8
- data/http_router.gemspec +5 -4
- data/lib/http_router/interface/sinatra.rb +7 -7
- data/lib/http_router/node.rb +106 -105
- data/lib/http_router/optional_compiler.rb +14 -5
- data/lib/http_router/path.rb +18 -28
- data/lib/http_router/root.rb +17 -29
- data/lib/http_router/route.rb +47 -16
- data/lib/http_router/static.rb +5 -0
- data/lib/http_router/variable.rb +2 -5
- data/lib/http_router/version.rb +1 -1
- data/lib/http_router.rb +38 -65
- data/test/helper.rb +74 -0
- data/test/rack/test_dispatch.rb +120 -0
- data/test/rack/test_route.rb +44 -0
- data/test/rack/test_urlmap.rb +12 -0
- data/{spec → test}/sinatra/recognize_spec.rb +0 -0
- data/test/sinatra/test_recognize.rb +150 -0
- data/test/test_arbitrary.rb +50 -0
- data/test/test_generate.rb +93 -0
- data/test/test_greedy.rb +24 -0
- data/test/test_interstitial.rb +47 -0
- data/test/test_misc.rb +30 -0
- data/test/test_mounting.rb +89 -0
- data/test/test_recognize.rb +56 -0
- data/test/test_request.rb +85 -0
- data/test/test_trailing_slash.rb +28 -0
- data/test/test_variable.rb +108 -0
- metadata +41 -32
- data/lib/http_router/response.rb +0 -46
- data/spec/generate_spec.rb +0 -234
- data/spec/misc_spec.rb +0 -65
- data/spec/mounting_spec.rb +0 -5
- data/spec/rack/dispatch_spec.rb +0 -119
- data/spec/rack/generate_spec.rb +0 -29
- data/spec/rack/middleware_spec.rb +0 -22
- data/spec/rack/route_spec.rb +0 -72
- data/spec/rack/urlmap_spec.rb +0 -13
- data/spec/recognize_spec.rb +0 -497
- data/spec/spec_helper.rb +0 -25
    
        data/lib/http_router/route.rb
    CHANGED
    
    | @@ -1,6 +1,8 @@ | |
| 1 | 
            +
            require 'strscan'
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            class HttpRouter
         | 
| 2 4 | 
             
              class Route
         | 
| 3 | 
            -
                attr_reader :dest, :paths, :path, :matches_with
         | 
| 5 | 
            +
                attr_reader :dest, :paths, :path, :matches_with, :original_path
         | 
| 4 6 | 
             
                attr_accessor :trailing_slash_ignore, :partially_match, :default_values
         | 
| 5 7 |  | 
| 6 8 | 
             
                def initialize(router, path)
         | 
| @@ -24,6 +26,10 @@ class HttpRouter | |
| 24 26 | 
             
                  end
         | 
| 25 27 | 
             
                end
         | 
| 26 28 |  | 
| 29 | 
            +
                def to_s
         | 
| 30 | 
            +
                  "#{@original_path} conditions: #{@conditions.inspect} default_values: #{@default_values.inspect} name: #{named.inspect}"
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 27 33 | 
             
                # Returns the options used to create this route.
         | 
| 28 34 | 
             
                def as_options
         | 
| 29 35 | 
             
                  {:matching => @matches_with, :conditions => @conditions, :default_values => @default_values, :name => @name, :partial => @partially_match, :arbitrary => @arbitrary}
         | 
| @@ -199,8 +205,14 @@ class HttpRouter | |
| 199 205 | 
             
                      working_set = current_node.add_request_methods(@conditions)
         | 
| 200 206 | 
             
                      working_set.map!{|node| node.add_arbitrary(@arbitrary)}
         | 
| 201 207 | 
             
                      working_set.each do |current_node|
         | 
| 202 | 
            -
                        current_node.value | 
| 208 | 
            +
                        case current_node.value
         | 
| 209 | 
            +
                        when nil
         | 
| 210 | 
            +
                          current_node.value = [path]
         | 
| 211 | 
            +
                        else
         | 
| 212 | 
            +
                          current_node.value << path
         | 
| 213 | 
            +
                        end
         | 
| 203 214 | 
             
                      end
         | 
| 215 | 
            +
                      path.variable_names.each{|vn| router.variable_names << vn} unless path.static?
         | 
| 204 216 | 
             
                    end
         | 
| 205 217 | 
             
                  end
         | 
| 206 218 | 
             
                  self
         | 
| @@ -240,6 +252,11 @@ class HttpRouter | |
| 240 252 |  | 
| 241 253 | 
             
                # Generates a URL for this route. See HttpRouter#url for how the arguments for this are structured.
         | 
| 242 254 | 
             
                def url(*args)
         | 
| 255 | 
            +
                  result, extra_params = url_with_params(*args)
         | 
| 256 | 
            +
                  router.append_querystring(result, extra_params)
         | 
| 257 | 
            +
                end
         | 
| 258 | 
            +
             | 
| 259 | 
            +
                def url_with_params(*args)
         | 
| 243 260 | 
             
                  options = args.last.is_a?(Hash) ? args.pop : nil
         | 
| 244 261 | 
             
                  options = options.nil? ? default_values.dup : default_values.merge(options) if default_values
         | 
| 245 262 | 
             
                  options.delete_if{ |k,v| v.nil? } if options
         | 
| @@ -249,19 +266,15 @@ class HttpRouter | |
| 249 266 | 
             
                    matching_path(args, options)
         | 
| 250 267 | 
             
                  end
         | 
| 251 268 | 
             
                  raise UngeneratableRouteException unless path
         | 
| 269 | 
            +
                  result, params = path.url(args, options)
         | 
| 252 270 | 
             
                  mount_point = router.url_mount && router.url_mount.url(options)
         | 
| 253 | 
            -
                   | 
| 254 | 
            -
                  mount_point ? File.join(mount_point, result) : result
         | 
| 271 | 
            +
                  mount_point ? [File.join(mount_point, result), params] : [result, params]
         | 
| 255 272 | 
             
                end
         | 
| 256 273 |  | 
| 257 274 | 
             
                def significant_variable_names
         | 
| 258 275 | 
             
                  @significant_variable_names ||= @path.scan(/(^|[^\\])[:\*]([a-zA-Z0-9_]+)/).map{|p| p.last.to_sym}
         | 
| 259 276 | 
             
                end
         | 
| 260 277 |  | 
| 261 | 
            -
                private
         | 
| 262 | 
            -
             | 
| 263 | 
            -
                attr_reader :router
         | 
| 264 | 
            -
             | 
| 265 278 | 
             
                def matching_path(params, other_hash = nil)
         | 
| 266 279 | 
             
                  if @paths.size == 1
         | 
| 267 280 | 
             
                    @paths.first
         | 
| @@ -285,6 +298,10 @@ class HttpRouter | |
| 285 298 | 
             
                  end
         | 
| 286 299 | 
             
                end
         | 
| 287 300 |  | 
| 301 | 
            +
                private
         | 
| 302 | 
            +
             | 
| 303 | 
            +
                attr_reader :router
         | 
| 304 | 
            +
             | 
| 288 305 | 
             
                def extract_partial_match(path)
         | 
| 289 306 | 
             
                  path[-1] == ?* && path.slice!(-1)
         | 
| 290 307 | 
             
                end
         | 
| @@ -337,32 +354,46 @@ class HttpRouter | |
| 337 354 | 
             
                end
         | 
| 338 355 |  | 
| 339 356 | 
             
                def generate_interstitial_parts(part)
         | 
| 340 | 
            -
                  part_segments =  | 
| 357 | 
            +
                  part_segments = []
         | 
| 358 | 
            +
                  scanner = StringScanner.new(part)
         | 
| 359 | 
            +
                  while !scanner.eos?
         | 
| 360 | 
            +
                    if scanner.scan(/\\[:\*]/)
         | 
| 361 | 
            +
                      part_segments << [:static, ''] if part_segments.last.nil? or part_segments.last.first == :variable
         | 
| 362 | 
            +
                      part_segments.last.last << scanner.matched[-1].chr
         | 
| 363 | 
            +
                    elsif scanner.scan(/\\/)
         | 
| 364 | 
            +
                      # do nothing
         | 
| 365 | 
            +
                    elsif scanner.scan(/:[a-zA_Z0-9_]+/)
         | 
| 366 | 
            +
                      part_segments << [:variable, scanner.matched]
         | 
| 367 | 
            +
                    elsif scanner.scan(/./)
         | 
| 368 | 
            +
                      part_segments << [:static, ''] if part_segments.last.nil? or part_segments.last.first == :variable
         | 
| 369 | 
            +
                      part_segments.last.last << scanner.matched
         | 
| 370 | 
            +
                    end
         | 
| 371 | 
            +
                  end
         | 
| 341 372 | 
             
                  priority = 0
         | 
| 342 373 | 
             
                  if part_segments.size > 1
         | 
| 343 374 | 
             
                    index = 0
         | 
| 344 | 
            -
                    segs = part_segments.map do |seg|
         | 
| 345 | 
            -
                      new_seg = if  | 
| 375 | 
            +
                    segs = part_segments.map do |(type, seg)|
         | 
| 376 | 
            +
                      new_seg = if type == :variable
         | 
| 346 377 | 
             
                        next_index = index + 1
         | 
| 347 378 | 
             
                        v_name = seg[1, seg.size].to_sym
         | 
| 348 379 | 
             
                        matcher = @matches_with[v_name]
         | 
| 349 380 | 
             
                        scan_regex = if next_index == part_segments.size
         | 
| 350 381 | 
             
                          matcher || /^[^\/]+/
         | 
| 351 382 | 
             
                        else
         | 
| 352 | 
            -
                          /^#{matcher || '[^\/]+?'}(?=#{Regexp.quote(part_segments[next_index])})/
         | 
| 383 | 
            +
                          /^#{matcher || '[^\/]+?'}(?=#{Regexp.quote(part_segments[next_index].last)})/
         | 
| 353 384 | 
             
                        end
         | 
| 354 | 
            -
                        priority += 1
         | 
| 355 385 | 
             
                        router.variable(v_name, scan_regex)
         | 
| 356 386 | 
             
                      else
         | 
| 357 | 
            -
                         | 
| 387 | 
            +
                        priority += seg.size
         | 
| 388 | 
            +
                        Static.new("^#{Regexp.quote(seg)}")
         | 
| 358 389 | 
             
                      end
         | 
| 359 390 | 
             
                      index += 1
         | 
| 360 391 | 
             
                      new_seg
         | 
| 361 392 | 
             
                    end
         | 
| 362 | 
            -
                    segs.each { | 
| 393 | 
            +
                    segs.each {|seg| seg.priority = priority}
         | 
| 363 394 | 
             
                    segs
         | 
| 364 395 | 
             
                  else
         | 
| 365 | 
            -
                     | 
| 396 | 
            +
                    [part_segments.last.last]
         | 
| 366 397 | 
             
                  end
         | 
| 367 398 | 
             
                end
         | 
| 368 399 |  | 
    
        data/lib/http_router/variable.rb
    CHANGED
    
    | @@ -3,11 +3,8 @@ class HttpRouter | |
| 3 3 | 
             
                attr_reader :name, :matches_with
         | 
| 4 4 | 
             
                attr_accessor :priority
         | 
| 5 5 |  | 
| 6 | 
            -
                def initialize(router, name, matches_with = nil)
         | 
| 7 | 
            -
                  @router = router
         | 
| 8 | 
            -
                  @name = name
         | 
| 9 | 
            -
                  @matches_with = matches_with
         | 
| 10 | 
            -
                  @priority = 0
         | 
| 6 | 
            +
                def initialize(router, name, matches_with = nil, priority = 0)
         | 
| 7 | 
            +
                  @router, @name, @matches_with, @priority = router, name, matches_with, priority
         | 
| 11 8 | 
             
                end
         | 
| 12 9 |  | 
| 13 10 | 
             
                def matches?(parts)
         | 
    
        data/lib/http_router/version.rb
    CHANGED
    
    
    
        data/lib/http_router.rb
    CHANGED
    
    | @@ -4,9 +4,9 @@ require 'url_mount' | |
| 4 4 | 
             
            require 'http_router/node'
         | 
| 5 5 | 
             
            require 'http_router/root'
         | 
| 6 6 | 
             
            require 'http_router/variable'
         | 
| 7 | 
            +
            require 'http_router/static'
         | 
| 7 8 | 
             
            require 'http_router/glob'
         | 
| 8 9 | 
             
            require 'http_router/route'
         | 
| 9 | 
            -
            require 'http_router/response'
         | 
| 10 10 | 
             
            require 'http_router/path'
         | 
| 11 11 | 
             
            require 'http_router/optional_compiler'
         | 
| 12 12 | 
             
            require 'http_router/parts'
         | 
| @@ -31,7 +31,7 @@ class HttpRouter | |
| 31 31 | 
             
              # Raised when there is a potential conflict of variable names within your Route.
         | 
| 32 32 | 
             
              AmbiguousVariableException       = Class.new(RuntimeError)
         | 
| 33 33 |  | 
| 34 | 
            -
              attr_reader :named_routes, :routes, :root, :request_methods_specified
         | 
| 34 | 
            +
              attr_reader :named_routes, :routes, :root, :request_methods_specified, :variable_names
         | 
| 35 35 | 
             
              attr_accessor :url_mount
         | 
| 36 36 |  | 
| 37 37 | 
             
              # Creates a new HttpRouter.
         | 
| @@ -41,23 +41,16 @@ class HttpRouter | |
| 41 41 | 
             
              # * :default_app -- Default application used if there is a non-match on #call. Defaults to 404 generator.
         | 
| 42 42 | 
             
              # * :ignore_trailing_slash -- Ignore a trailing / when attempting to match. Defaults to +true+.
         | 
| 43 43 | 
             
              # * :redirect_trailing_slash -- On trailing /, redirect to the same path without the /. Defaults to +false+.
         | 
| 44 | 
            -
              # * :middleware -- On recognition, store the route Response in env['router.response'] and always call the default app. Defaults to +false+.
         | 
| 45 44 | 
             
              def initialize(*args, &block)
         | 
| 46 45 | 
             
                default_app, options = args.first.is_a?(Hash) ? [nil, args.first] : [args.first, args[1]]
         | 
| 47 46 | 
             
                @options                   = options
         | 
| 48 47 | 
             
                @default_app               = default_app || options && options[:default_app] || proc{|env| ::Rack::Response.new("Not Found", 404).finish }
         | 
| 49 48 | 
             
                @ignore_trailing_slash     = options && options.key?(:ignore_trailing_slash) ? options[:ignore_trailing_slash] : true
         | 
| 50 49 | 
             
                @redirect_trailing_slash   = options && options.key?(:redirect_trailing_slash) ? options[:redirect_trailing_slash] : false
         | 
| 51 | 
            -
                @middleware                = options && options.key?(:middleware) ? options[:middleware] : false
         | 
| 52 | 
            -
                @request_methods_specified = Set.new
         | 
| 53 | 
            -
                @routes                    = []
         | 
| 54 | 
            -
                @named_routes              = {}
         | 
| 55 50 | 
             
                @init_block                = block
         | 
| 51 | 
            +
                @handle_unavailable_route  = Proc.new{ raise UngeneratableRouteException }
         | 
| 56 52 | 
             
                reset!
         | 
| 57 | 
            -
                if block
         | 
| 58 | 
            -
                  instance_eval(&block)
         | 
| 59 | 
            -
                  @routes.each {|r| r.compile}
         | 
| 60 | 
            -
                end
         | 
| 53 | 
            +
                instance_eval(&block) if block
         | 
| 61 54 | 
             
              end
         | 
| 62 55 |  | 
| 63 56 | 
             
              # Ignore trailing slash feature enabled? See #initialize for details.
         | 
| @@ -73,8 +66,10 @@ class HttpRouter | |
| 73 66 | 
             
              # Resets the router to a clean state.
         | 
| 74 67 | 
             
              def reset!
         | 
| 75 68 | 
             
                @root = Root.new(self)
         | 
| 76 | 
            -
                @ | 
| 77 | 
            -
                @ | 
| 69 | 
            +
                @request_methods_specified = Set.new
         | 
| 70 | 
            +
                @routes = []
         | 
| 71 | 
            +
                @named_routes = {}
         | 
| 72 | 
            +
                @variable_names = Set.new
         | 
| 78 73 | 
             
              end
         | 
| 79 74 |  | 
| 80 75 | 
             
              # Assigns the default application.
         | 
| @@ -135,18 +130,6 @@ class HttpRouter | |
| 135 130 | 
             
                add(path, options).delete
         | 
| 136 131 | 
             
              end
         | 
| 137 132 |  | 
| 138 | 
            -
              # Returns the HttpRouter::Response object if the env is matched, otherwise, returns +nil+.
         | 
| 139 | 
            -
              def recognize(env)
         | 
| 140 | 
            -
                response = recognize_full(env)
         | 
| 141 | 
            -
                response.is_a?(Array) ? response.first : response
         | 
| 142 | 
            -
              end
         | 
| 143 | 
            -
             | 
| 144 | 
            -
              # Returns the HttpRouter::Response object if the env is matched, an array of HttpRouter::Response objects or otherwise, returns +nil+. If it
         | 
| 145 | 
            -
              # returns an array, this represents a set of possible matches.
         | 
| 146 | 
            -
              def recognize_full(env)
         | 
| 147 | 
            -
                @root.find(env.is_a?(Hash) ? ::Rack::Request.new(env) : env)
         | 
| 148 | 
            -
              end
         | 
| 149 | 
            -
             | 
| 150 133 | 
             
              # Generate a URL for a specified route. This will accept a list of variable values plus any other variable names named as a hash.
         | 
| 151 134 | 
             
              # This first value must be either the Route object or the name of the route.
         | 
| 152 135 | 
             
              #
         | 
| @@ -164,8 +147,18 @@ class HttpRouter | |
| 164 147 | 
             
              def url(route, *args)
         | 
| 165 148 | 
             
                case route
         | 
| 166 149 | 
             
                when Symbol then url(@named_routes[route], *args)
         | 
| 167 | 
            -
                when  | 
| 168 | 
            -
                 | 
| 150 | 
            +
                when Route  then route.url(*args)
         | 
| 151 | 
            +
                when nil    then @handle_unavailable_route.call(:url, *args)
         | 
| 152 | 
            +
                else             
         | 
| 153 | 
            +
                end
         | 
| 154 | 
            +
              end
         | 
| 155 | 
            +
             | 
| 156 | 
            +
              def url_with_params(route, *args)
         | 
| 157 | 
            +
                case route
         | 
| 158 | 
            +
                when Symbol then url_with_params(@named_routes[route], *args)
         | 
| 159 | 
            +
                when Route  then route.url_with_params(*args)
         | 
| 160 | 
            +
                when nil    then @handle_unavailable_route.call(:url_with_params, *args)
         | 
| 161 | 
            +
                else             
         | 
| 169 162 | 
             
                end
         | 
| 170 163 | 
             
              end
         | 
| 171 164 |  | 
| @@ -180,33 +173,14 @@ class HttpRouter | |
| 180 173 | 
             
                  response.redirect(request.path_info[0, request.path_info.size - 1], 302)
         | 
| 181 174 | 
             
                  response.finish
         | 
| 182 175 | 
             
                else
         | 
| 183 | 
            -
                   | 
| 184 | 
            -
                  if response = recognize_full(request) and !@middleware
         | 
| 185 | 
            -
                    if response.is_a?(Array)
         | 
| 186 | 
            -
                      call_env = env.dup
         | 
| 187 | 
            -
                      response.each do |match|
         | 
| 188 | 
            -
                        if match.route.dest.respond_to?(:call)
         | 
| 189 | 
            -
                          process_params(call_env, match)
         | 
| 190 | 
            -
                          consume_path!(call_env, match)
         | 
| 191 | 
            -
                          app_response = match.route.dest.call(call_env)
         | 
| 192 | 
            -
                          return app_response unless app_response.first == 404 or app_response.first == 410
         | 
| 193 | 
            -
                        else
         | 
| 194 | 
            -
                          return response
         | 
| 195 | 
            -
                        end
         | 
| 196 | 
            -
                      end
         | 
| 197 | 
            -
                    elsif response.matched? && response.route.dest
         | 
| 198 | 
            -
                      process_params(env, response)
         | 
| 199 | 
            -
                      return response.route.dest.call(env) if response.route.dest.respond_to?(:call)
         | 
| 200 | 
            -
                    elsif !response.matched?
         | 
| 201 | 
            -
                      return [response.status, response.headers, []]
         | 
| 202 | 
            -
                    end
         | 
| 203 | 
            -
                    process_params(env, response)
         | 
| 204 | 
            -
                  end
         | 
| 205 | 
            -
                  env['router.response'] = response
         | 
| 206 | 
            -
                  @default_app.call(env)
         | 
| 176 | 
            +
                  @root.call(request) || @default_app.call(request.env)
         | 
| 207 177 | 
             
                end
         | 
| 208 178 | 
             
              end
         | 
| 209 179 |  | 
| 180 | 
            +
              def recognize(env)
         | 
| 181 | 
            +
                @root.recognize(env)
         | 
| 182 | 
            +
              end
         | 
| 183 | 
            +
             | 
| 210 184 | 
             
              # Returns a new node
         | 
| 211 185 | 
             
              def node(*args)
         | 
| 212 186 | 
             
                Node.new(self, *args)
         | 
| @@ -266,20 +240,19 @@ class HttpRouter | |
| 266 240 | 
             
                s.to_s.gsub!(/((?:%[0-9a-fA-F]{2})+)/n){ [$1.delete('%')].pack('H*') }
         | 
| 267 241 | 
             
              end
         | 
| 268 242 |  | 
| 269 | 
            -
               | 
| 270 | 
            -
             | 
| 271 | 
            -
             | 
| 272 | 
            -
             | 
| 273 | 
            -
             | 
| 274 | 
            -
             | 
| 275 | 
            -
             | 
| 276 | 
            -
             | 
| 277 | 
            -
             | 
| 278 | 
            -
             | 
| 279 | 
            -
                   | 
| 280 | 
            -
             | 
| 281 | 
            -
                  env['router.params'] = response.route.default_values ? response.route.default_values.merge(response.params_as_hash) : response.params_as_hash
         | 
| 243 | 
            +
              def append_querystring(uri, params)
         | 
| 244 | 
            +
                if params && !params.empty?
         | 
| 245 | 
            +
                  uri_size = uri.size
         | 
| 246 | 
            +
                  params.each do |k,v|
         | 
| 247 | 
            +
                    case v
         | 
| 248 | 
            +
                    when Array
         | 
| 249 | 
            +
                      v.each { |v_part| uri << '&' << ::Rack::Utils.escape(k.to_s) << '%5B%5D=' << ::Rack::Utils.escape(v_part.to_s) }
         | 
| 250 | 
            +
                    else
         | 
| 251 | 
            +
                      uri << '&' << ::Rack::Utils.escape(k.to_s) << '=' << ::Rack::Utils.escape(v.to_s)
         | 
| 252 | 
            +
                    end
         | 
| 253 | 
            +
                  end
         | 
| 254 | 
            +
                  uri[uri_size] = ??
         | 
| 282 255 | 
             
                end
         | 
| 256 | 
            +
                uri
         | 
| 283 257 | 
             
              end
         | 
| 284 | 
            -
             | 
| 285 258 | 
             
            end
         | 
    
        data/test/helper.rb
    ADDED
    
    | @@ -0,0 +1,74 @@ | |
| 1 | 
            +
            require 'minitest/autorun'
         | 
| 2 | 
            +
            require 'phocus'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class HttpRouter::Route
         | 
| 5 | 
            +
              def default_destination
         | 
| 6 | 
            +
                to{|env| Rack::Response.new("Routing to #{to_s}").finish}
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
            end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            class MiniTest::Unit::TestCase
         | 
| 11 | 
            +
              def router(*args, &blk)
         | 
| 12 | 
            +
                @router ||= HttpRouter.new(*args, &blk)
         | 
| 13 | 
            +
                if blk
         | 
| 14 | 
            +
                  @router.routes.each do |route|
         | 
| 15 | 
            +
                    route.default_destination if route.dest.nil?
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                  @router.routes.size > 1 ? @router.routes : @router.routes.first
         | 
| 18 | 
            +
                else
         | 
| 19 | 
            +
                  @router
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              def assert_body(expect, response)
         | 
| 24 | 
            +
                response = router.call(response) if response.is_a?(Hash)
         | 
| 25 | 
            +
                body = case expect
         | 
| 26 | 
            +
                when Array  then []
         | 
| 27 | 
            +
                when String then ""
         | 
| 28 | 
            +
                else             raise
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
                response.last.each {|p| body << p}
         | 
| 31 | 
            +
                assert_equal expect, body
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
              
         | 
| 34 | 
            +
              def assert_header(header, response)
         | 
| 35 | 
            +
                response = router.call(response) if response.is_a?(Hash)
         | 
| 36 | 
            +
                header.each{|k, v| assert_equal v, response[1][k]}
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              def assert_status(status, response)
         | 
| 40 | 
            +
                response = router.call(response) if response.is_a?(Hash)
         | 
| 41 | 
            +
                assert_equal status, response.first
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              def assert_route(route, request, params = nil, &blk)
         | 
| 45 | 
            +
                if route.is_a?(String)
         | 
| 46 | 
            +
                  router.reset!
         | 
| 47 | 
            +
                  route = router.add(route)
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
                route.to{|env| Rack::Response.new("Routing to #{route.to_s}").finish} if route && !route.compiled?
         | 
| 50 | 
            +
                request = Rack::MockRequest.env_for(request) if request.is_a?(String)
         | 
| 51 | 
            +
                response = @router.call(request)
         | 
| 52 | 
            +
                if route
         | 
| 53 | 
            +
                  dest = "Routing to #{route.to_s}"
         | 
| 54 | 
            +
                  assert_equal [dest], response.last.body
         | 
| 55 | 
            +
                  if params
         | 
| 56 | 
            +
                    assert_equal params.size, request['router.params'].size
         | 
| 57 | 
            +
                    params.each { |k, v| assert_equal v, request['router.params'][k] }
         | 
| 58 | 
            +
                  elsif !request['router.params'].nil? and !request['router.params'].empty?
         | 
| 59 | 
            +
                    raise "Wasn't expecting any parameters, got #{request['router.params'].inspect}"
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
                else
         | 
| 62 | 
            +
                  assert_equal 404, response.first
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
              
         | 
| 66 | 
            +
              def assert_generate(path, route, *args)
         | 
| 67 | 
            +
                if route.is_a?(String)
         | 
| 68 | 
            +
                  router.reset!
         | 
| 69 | 
            +
                  route = router.add(route).to(path.to_sym)
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
                route.to{|env| Rack::Response.new("Routing to #{route.to_s}").finish} if route.respond_to?(:compiled?) && !route.compiled?
         | 
| 72 | 
            +
                assert_equal path, router.url(route, *args)
         | 
| 73 | 
            +
              end
         | 
| 74 | 
            +
            end
         | 
| @@ -0,0 +1,120 @@ | |
| 1 | 
            +
            #describe "HttpRouter route dispatching with redirect_on_trailing_delimiters" do
         | 
| 2 | 
            +
            #  before(:each) do
         | 
| 3 | 
            +
            #    @route_set = HttpRouter.new(:redirect_trailing_slash => true)
         | 
| 4 | 
            +
            #    @route_set.extend(CallWithMockRequestMixin)
         | 
| 5 | 
            +
            #    @app = MockApp.new("Hello World!")
         | 
| 6 | 
            +
            #    @route_set.add('/sample').to(@app)
         | 
| 7 | 
            +
            #  end
         | 
| 8 | 
            +
            #
         | 
| 9 | 
            +
            #  it "should dispatch a request" do
         | 
| 10 | 
            +
            #    response = @route_set.call_with_mock_request('/sample/')
         | 
| 11 | 
            +
            #    response.headers["Location"].should == "/sample"
         | 
| 12 | 
            +
            #  end
         | 
| 13 | 
            +
            #
         | 
| 14 | 
            +
            #end
         | 
| 15 | 
            +
            #
         | 
| 16 | 
            +
            #describe "HttpRouter route dispatching" do
         | 
| 17 | 
            +
            #  before(:each) do
         | 
| 18 | 
            +
            #    @route_set = HttpRouter.new(:redirect_trailing_slash => true)
         | 
| 19 | 
            +
            #    @route_set.extend(CallWithMockRequestMixin)
         | 
| 20 | 
            +
            #    @app = MockApp.new("Hello World!")
         | 
| 21 | 
            +
            #  end
         | 
| 22 | 
            +
            #
         | 
| 23 | 
            +
            #  describe "HTTP GET" do
         | 
| 24 | 
            +
            #    before(:each) do
         | 
| 25 | 
            +
            #      @route_set.reset!
         | 
| 26 | 
            +
            #      @route_set.add('/sample').request_method('GET').to(@app)
         | 
| 27 | 
            +
            #    end
         | 
| 28 | 
            +
            #
         | 
| 29 | 
            +
            #    it "should dispatch a request" do
         | 
| 30 | 
            +
            #      response = @route_set.call_with_mock_request
         | 
| 31 | 
            +
            #      response.body.should eql("Hello World!")
         | 
| 32 | 
            +
            #    end
         | 
| 33 | 
            +
            #
         | 
| 34 | 
            +
            #    it "should write router.params" do
         | 
| 35 | 
            +
            #      response = @route_set.call_with_mock_request
         | 
| 36 | 
            +
            #      @app.env["router.params"].should == {}
         | 
| 37 | 
            +
            #    end
         | 
| 38 | 
            +
            #
         | 
| 39 | 
            +
            #    it "should write router.params for default values" do
         | 
| 40 | 
            +
            #      @route_set.add("/foobar", :default_values => {:hi => :there}).compile
         | 
| 41 | 
            +
            #      response = @route_set.call_with_mock_request("/foobar")
         | 
| 42 | 
            +
            #      env = Rack::MockRequest.env_for("/foobar")
         | 
| 43 | 
            +
            #      @route_set.call(env)
         | 
| 44 | 
            +
            #      env['router.params'].should == {:hi => :there}
         | 
| 45 | 
            +
            #    end
         | 
| 46 | 
            +
            #  end
         | 
| 47 | 
            +
            #
         | 
| 48 | 
            +
            #  describe "HTTP POST" do
         | 
| 49 | 
            +
            #    before(:each) do
         | 
| 50 | 
            +
            #      @route_set.reset!
         | 
| 51 | 
            +
            #      @route_set.add('/sample').post.to(@app)
         | 
| 52 | 
            +
            #      @route_set.add('/sample').to(MockApp.new("You shouldn't get here if you are using POST"))
         | 
| 53 | 
            +
            #    end
         | 
| 54 | 
            +
            #
         | 
| 55 | 
            +
            #    it "should dispatch a POST request" do
         | 
| 56 | 
            +
            #      response = @route_set.call_with_mock_request('/sample', 'POST')
         | 
| 57 | 
            +
            #      response.body.should eql("Hello World!")
         | 
| 58 | 
            +
            #    end
         | 
| 59 | 
            +
            #
         | 
| 60 | 
            +
            #    it "shouldn't dispatch a GET request" do
         | 
| 61 | 
            +
            #      response = @route_set.call_with_mock_request('/sample', 'GET')
         | 
| 62 | 
            +
            #      response.body.should eql("You shouldn't get here if you are using POST")
         | 
| 63 | 
            +
            #    end
         | 
| 64 | 
            +
            #
         | 
| 65 | 
            +
            #    it "should write router.params" do
         | 
| 66 | 
            +
            #      response = @route_set.call_with_mock_request("/sample", 'POST')
         | 
| 67 | 
            +
            #      @app.env["router.params"].should == {}
         | 
| 68 | 
            +
            #    end
         | 
| 69 | 
            +
            #  end
         | 
| 70 | 
            +
            #
         | 
| 71 | 
            +
            #  it "should returns HTTP 405 if the method mis-matches" do
         | 
| 72 | 
            +
            #    @route_set.reset!
         | 
| 73 | 
            +
            #    @route_set.post('/sample').to(@app)
         | 
| 74 | 
            +
            #    @route_set.put('/sample').to(@app)
         | 
| 75 | 
            +
            #    response = @route_set.call_with_mock_request('/sample', 'GET')
         | 
| 76 | 
            +
            #    response.status.should eql(405)
         | 
| 77 | 
            +
            #    response['Allow'].should == 'POST, PUT'
         | 
| 78 | 
            +
            #  end
         | 
| 79 | 
            +
            #
         | 
| 80 | 
            +
            #  it "should returns HTTP 404 if route doesn't exist" do
         | 
| 81 | 
            +
            #    response = @route_set.call_with_mock_request("/not-existing-url")
         | 
| 82 | 
            +
            #    response.status.should eql(404)
         | 
| 83 | 
            +
            #  end
         | 
| 84 | 
            +
            #
         | 
| 85 | 
            +
            #  describe "shortcuts" do
         | 
| 86 | 
            +
            #    describe "get" do
         | 
| 87 | 
            +
            #      before(:each) do
         | 
| 88 | 
            +
            #        @route_set.reset!
         | 
| 89 | 
            +
            #        @route_set.get('/sample').head.to(@app)
         | 
| 90 | 
            +
            #      end
         | 
| 91 | 
            +
            #
         | 
| 92 | 
            +
            #      it "should dispatch a GET request" do
         | 
| 93 | 
            +
            #        response = @route_set.call_with_mock_request("/sample", "GET")
         | 
| 94 | 
            +
            #        response.body.should eql("Hello World!")
         | 
| 95 | 
            +
            #      end
         | 
| 96 | 
            +
            #
         | 
| 97 | 
            +
            #      it "should dispatch a HEAD request" do
         | 
| 98 | 
            +
            #        response = @route_set.call_with_mock_request("/sample", "HEAD")
         | 
| 99 | 
            +
            #        response.body.should eql("Hello World!")
         | 
| 100 | 
            +
            #      end
         | 
| 101 | 
            +
            #    end
         | 
| 102 | 
            +
            #  end
         | 
| 103 | 
            +
            #
         | 
| 104 | 
            +
            #  describe "non rack app destinations" do
         | 
| 105 | 
            +
            #    it "should route to a default application when using a hash" do
         | 
| 106 | 
            +
            #      $captures = []
         | 
| 107 | 
            +
            #      @default_app = lambda do |e|
         | 
| 108 | 
            +
            #        $captures << :default
         | 
| 109 | 
            +
            #        Rack::Response.new("Default").finish
         | 
| 110 | 
            +
            #      end
         | 
| 111 | 
            +
            #      @router = HttpRouter.new
         | 
| 112 | 
            +
            #      @router.default(@default_app)
         | 
| 113 | 
            +
            #      @router.add("/default").to(:action => "default")
         | 
| 114 | 
            +
            #      response = @router.call(Rack::MockRequest.env_for("/default"))
         | 
| 115 | 
            +
            #      $captures.should == [:default]
         | 
| 116 | 
            +
            #    end
         | 
| 117 | 
            +
            #  end
         | 
| 118 | 
            +
            #
         | 
| 119 | 
            +
            #end
         | 
| 120 | 
            +
            #
         | 
| @@ -0,0 +1,44 @@ | |
| 1 | 
            +
            class TestRouteExtensions < MiniTest::Unit::TestCase
         | 
| 2 | 
            +
              
         | 
| 3 | 
            +
              def test_redirect
         | 
| 4 | 
            +
                router.get("/index.html").redirect("/")
         | 
| 5 | 
            +
                response = router.call(Rack::MockRequest.env_for("/index.html"))
         | 
| 6 | 
            +
                assert_header({'Location' => '/'}, response)
         | 
| 7 | 
            +
                assert_status 302, response
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              def test_redirect_with_params
         | 
| 11 | 
            +
                router.get("/:id.html").redirect('/#{params[:id]}')
         | 
| 12 | 
            +
                response = router.call(Rack::MockRequest.env_for("/123.html"))
         | 
| 13 | 
            +
                assert_header({'Location' => '/123'}, response)
         | 
| 14 | 
            +
                assert_status 302, response
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              def test_static_directory
         | 
| 18 | 
            +
                router.get("/static").static(File.dirname(__FILE__))
         | 
| 19 | 
            +
                status, headers, body = router.call(Rack::MockRequest.env_for("/static/#{File.basename(__FILE__)}"))
         | 
| 20 | 
            +
                assert_equal File.join(File.dirname(__FILE__), File.basename(__FILE__)), body.path
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              def test_static_file
         | 
| 24 | 
            +
                router.get("/static-file").static(__FILE__)
         | 
| 25 | 
            +
                status, headers, body = router.call(Rack::MockRequest.env_for("/static-file"))
         | 
| 26 | 
            +
                assert_equal __FILE__, body.path
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              def test_chainable
         | 
| 30 | 
            +
                router.get("/index.html").redirect("/").name(:root)
         | 
| 31 | 
            +
                assert_equal "/index.html", router.url(:root)
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              def test_custom_status
         | 
| 35 | 
            +
                router.get("/index.html").redirect("/", 303)
         | 
| 36 | 
            +
                response = router.call(Rack::MockRequest.env_for("/index.html"))
         | 
| 37 | 
            +
                assert_header({'Location' => '/'}, response)
         | 
| 38 | 
            +
                assert_status 303, response
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              def test_raise_error_on_invalid_status
         | 
| 42 | 
            +
                assert_raises(ArgumentError) { router.get("/index.html").redirect("/", 200) }
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
            end
         | 
| @@ -0,0 +1,12 @@ | |
| 1 | 
            +
            class TestRackUrlmap < MiniTest::Unit::TestCase
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              def test_map_urls
         | 
| 4 | 
            +
                HttpRouter::Rack.override_rack_urlmap!
         | 
| 5 | 
            +
                map = Rack::URLMap.new(
         | 
| 6 | 
            +
                  "http://www.example.org/test" => proc {|env| [200, {}, ['test']]},
         | 
| 7 | 
            +
                  "http://www.example.org/:test" => proc {|env| [200, {}, ['variable']]}
         | 
| 8 | 
            +
                )
         | 
| 9 | 
            +
                assert_equal 'test',     map.call(Rack::MockRequest.env_for('http://www.example.org/test')).last.join
         | 
| 10 | 
            +
                assert_equal 'variable', map.call(Rack::MockRequest.env_for('http://www.example.org/whhhaaa')).last.join
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
            end
         | 
| 
            File without changes
         |