josh-rack-mount 0.0.1
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/MIT-LICENSE +20 -0
- data/README.rdoc +28 -0
- data/lib/rack/mount.rb +18 -0
- data/lib/rack/mount/const.rb +39 -0
- data/lib/rack/mount/exceptions.rb +5 -0
- data/lib/rack/mount/generation.rb +9 -0
- data/lib/rack/mount/generation/optimizations.rb +83 -0
- data/lib/rack/mount/generation/route.rb +108 -0
- data/lib/rack/mount/generation/route_set.rb +60 -0
- data/lib/rack/mount/mappers/merb.rb +143 -0
- data/lib/rack/mount/mappers/rails_classic.rb +164 -0
- data/lib/rack/mount/mappers/rails_draft.rb +179 -0
- data/lib/rack/mount/mappers/simple.rb +37 -0
- data/lib/rack/mount/nested_set.rb +106 -0
- data/lib/rack/mount/path_prefix.rb +20 -0
- data/lib/rack/mount/recognition.rb +8 -0
- data/lib/rack/mount/recognition/route.rb +94 -0
- data/lib/rack/mount/recognition/route_set.rb +54 -0
- data/lib/rack/mount/regexp_with_named_groups.rb +38 -0
- data/lib/rack/mount/request.rb +30 -0
- data/lib/rack/mount/route.rb +63 -0
- data/lib/rack/mount/route_set.rb +34 -0
- data/lib/rack/mount/utils.rb +149 -0
- data/rails/init.rb +2 -0
- metadata +86 -0
    
        data/MIT-LICENSE
    ADDED
    
    | @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            Copyright (c) 2009 Joshua Peek
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining
         | 
| 4 | 
            +
            a copy of this software and associated documentation files (the
         | 
| 5 | 
            +
            "Software"), to deal in the Software without restriction, including
         | 
| 6 | 
            +
            without limitation the rights to use, copy, modify, merge, publish,
         | 
| 7 | 
            +
            distribute, sublicense, and/or sell copies of the Software, and to
         | 
| 8 | 
            +
            permit persons to whom the Software is furnished to do so, subject to
         | 
| 9 | 
            +
            the following conditions:
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            The above copyright notice and this permission notice shall be
         | 
| 12 | 
            +
            included in all copies or substantial portions of the Software.
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         | 
| 15 | 
            +
            EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         | 
| 16 | 
            +
            MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         | 
| 17 | 
            +
            NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         | 
| 18 | 
            +
            LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         | 
| 19 | 
            +
            OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         | 
| 20 | 
            +
            WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
    
        data/README.rdoc
    ADDED
    
    | @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            = Rack::Mount
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            A stackable dynamic tree based Rack router.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            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 pulled from a database.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            === Usage
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            Rack::Mount provides a plugin API to build custom DSLs on top of.
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            The API is extremely minimal and only 3 methods are exposed as the public API.
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            <tt>Rack::Mount::RouteSet#add_route</tt>:: builder method for adding routes to the set
         | 
| 14 | 
            +
            <tt>Rack::Mount::RouteSet#call</tt>:: Rack compatible recognition and dispatching method
         | 
| 15 | 
            +
            <tt>Rack::Mount::RouteSet#url_for</tt>:: generatess path from identifiers or significant keys
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            === Example
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              require 'rack/mount'
         | 
| 20 | 
            +
              Routes = Rack::Mount::RouteSet.new do |set|
         | 
| 21 | 
            +
                # add_route takes a rack application and conditions to match with
         | 
| 22 | 
            +
                # conditions may be strings or regexps
         | 
| 23 | 
            +
                # See Rack::Mount::RouteSet#add_route for more options.
         | 
| 24 | 
            +
                set.add_route FooApp, :method => 'get' :path => %{/foo}
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              # The route set itself is a simple rack app you mount
         | 
| 28 | 
            +
              run Routes
         | 
    
        data/lib/rack/mount.rb
    ADDED
    
    | @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            require 'rack/utils'
         | 
| 2 | 
            +
            require 'rack/mount/exceptions'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Rack #:nodoc:
         | 
| 5 | 
            +
              module Mount #:nodoc:
         | 
| 6 | 
            +
                autoload :Const, 'rack/mount/const'
         | 
| 7 | 
            +
                autoload :Generation, 'rack/mount/generation'
         | 
| 8 | 
            +
                autoload :NestedSet, 'rack/mount/nested_set'
         | 
| 9 | 
            +
                autoload :NestedSetExt, 'rack/mount/nested_set_ext'
         | 
| 10 | 
            +
                autoload :PathPrefix, 'rack/mount/path_prefix'
         | 
| 11 | 
            +
                autoload :Recognition, 'rack/mount/recognition'
         | 
| 12 | 
            +
                autoload :RegexpWithNamedGroups, 'rack/mount/regexp_with_named_groups'
         | 
| 13 | 
            +
                autoload :Request, 'rack/mount/request'
         | 
| 14 | 
            +
                autoload :Route, 'rack/mount/route'
         | 
| 15 | 
            +
                autoload :RouteSet, 'rack/mount/route_set'
         | 
| 16 | 
            +
                autoload :Utils, 'rack/mount/utils'
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         | 
| @@ -0,0 +1,39 @@ | |
| 1 | 
            +
            module Rack
         | 
| 2 | 
            +
              module Mount
         | 
| 3 | 
            +
                module Const #:nodoc:
         | 
| 4 | 
            +
                  RACK_ROUTING_ARGS = 'rack.routing_args'.freeze
         | 
| 5 | 
            +
                  RACK_MOUNT_DEBUG  = 'RACKMOUNT_DEBUG'.freeze
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  begin
         | 
| 8 | 
            +
                    eval('/(?<foo>.*)/').named_captures
         | 
| 9 | 
            +
                    SUPPORTS_NAMED_CAPTURES = true
         | 
| 10 | 
            +
                    REGEXP_NAMED_CAPTURE = '(?<%s>%s)'.freeze
         | 
| 11 | 
            +
                  rescue SyntaxError, NoMethodError
         | 
| 12 | 
            +
                    SUPPORTS_NAMED_CAPTURES = false
         | 
| 13 | 
            +
                    REGEXP_NAMED_CAPTURE = '(?:<%s>%s)'.freeze
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  EOS_KEY = '$'.freeze
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  CONTENT_TYPE    = 'Content-Type'.freeze
         | 
| 19 | 
            +
                  DELETE          = 'PUT'.freeze
         | 
| 20 | 
            +
                  EMPTY_STRING    = ''.freeze
         | 
| 21 | 
            +
                  GET             = 'GET'.freeze
         | 
| 22 | 
            +
                  HEAD            = 'HEAD'.freeze
         | 
| 23 | 
            +
                  PATH_INFO       = 'PATH_INFO'.freeze
         | 
| 24 | 
            +
                  POST            = 'POST'.freeze
         | 
| 25 | 
            +
                  PUT             = 'PUT'.freeze
         | 
| 26 | 
            +
                  REQUEST_METHOD  = 'REQUEST_METHOD'.freeze
         | 
| 27 | 
            +
                  SLASH           = '/'.freeze
         | 
| 28 | 
            +
                  TEXT_SLASH_HTML = 'text/html'.freeze
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  DEFAULT_CONTENT_TYPE_HEADERS = {CONTENT_TYPE => TEXT_SLASH_HTML}.freeze
         | 
| 31 | 
            +
                  HTTP_METHODS = [GET, HEAD, POST, PUT, DELETE].freeze
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  OK = 'OK'.freeze
         | 
| 34 | 
            +
                  NOT_FOUND = 'Not Found'.freeze
         | 
| 35 | 
            +
                  OK_RESPONSE = [200, DEFAULT_CONTENT_TYPE_HEADERS, [OK].freeze].freeze
         | 
| 36 | 
            +
                  NOT_FOUND_RESPONSE = [404, DEFAULT_CONTENT_TYPE_HEADERS, [NOT_FOUND].freeze].freeze
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
            end
         | 
| @@ -0,0 +1,83 @@ | |
| 1 | 
            +
            module Rack
         | 
| 2 | 
            +
              module Mount
         | 
| 3 | 
            +
                module Generation
         | 
| 4 | 
            +
                  module Optimizations #:nodoc:
         | 
| 5 | 
            +
                    def freeze
         | 
| 6 | 
            +
                      optimize_call! unless frozen?
         | 
| 7 | 
            +
                      super
         | 
| 8 | 
            +
                    end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    if ENV[Const::RACK_MOUNT_DEBUG]
         | 
| 11 | 
            +
                      def instance_eval(*args)
         | 
| 12 | 
            +
                        puts
         | 
| 13 | 
            +
                        puts "#{args[1]}##{args[2]}"
         | 
| 14 | 
            +
                        puts args[0]
         | 
| 15 | 
            +
                        puts
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                        super
         | 
| 18 | 
            +
                      end
         | 
| 19 | 
            +
                    end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                    private
         | 
| 22 | 
            +
                      def optimize_call!
         | 
| 23 | 
            +
                        @recognition_graph.lists.each do |list|
         | 
| 24 | 
            +
                          body = (0...list.length).zip(list).map { |i, route|
         | 
| 25 | 
            +
                            assign_index_params = assign_index_params(route)
         | 
| 26 | 
            +
                            <<-EOS
         | 
| 27 | 
            +
                              if #{route.method ? "method == #{route.method.inspect} && " : ''}path =~ #{route.path.inspect}
         | 
| 28 | 
            +
                                route = self[#{i}]
         | 
| 29 | 
            +
                                #{if assign_index_params.any?
         | 
| 30 | 
            +
                                  'routing_args, param_matches = route.defaults.dup, $~.captures'
         | 
| 31 | 
            +
                                else
         | 
| 32 | 
            +
                                  'routing_args = route.defaults.dup'
         | 
| 33 | 
            +
                                end}
         | 
| 34 | 
            +
                                #{assign_index_params.join("\n                  ")}
         | 
| 35 | 
            +
                                env[Const::RACK_ROUTING_ARGS] = routing_args
         | 
| 36 | 
            +
                                result = route.app.call(env)
         | 
| 37 | 
            +
                                return result unless result[0] == #{@catch}
         | 
| 38 | 
            +
                              end
         | 
| 39 | 
            +
                            EOS
         | 
| 40 | 
            +
                          }.join
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                          method = <<-EOS, __FILE__, __LINE__
         | 
| 43 | 
            +
                            def optimized_each(env)
         | 
| 44 | 
            +
                              method = env[Const::REQUEST_METHOD]
         | 
| 45 | 
            +
                              path = env[Const::PATH_INFO]
         | 
| 46 | 
            +
            #{body}
         | 
| 47 | 
            +
                              nil
         | 
| 48 | 
            +
                            end
         | 
| 49 | 
            +
                          EOS
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                          puts method if ENV[Const::RACK_MOUNT_DEBUG]
         | 
| 52 | 
            +
                          list.instance_eval(*method)
         | 
| 53 | 
            +
                        end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                        instance_eval(<<-EOS, __FILE__, __LINE__)
         | 
| 56 | 
            +
                          def call(env)
         | 
| 57 | 
            +
                            req = Request.new(env)
         | 
| 58 | 
            +
                            keys = [#{convert_keys_to_method_calls}]
         | 
| 59 | 
            +
                            @recognition_graph[*keys].optimized_each(env) || @throw
         | 
| 60 | 
            +
                          end
         | 
| 61 | 
            +
                        EOS
         | 
| 62 | 
            +
                      end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                      def convert_keys_to_method_calls
         | 
| 65 | 
            +
                        @recognition_keys.map { |key|
         | 
| 66 | 
            +
                          if key.is_a?(Array)
         | 
| 67 | 
            +
                            key = key.dup
         | 
| 68 | 
            +
                            "req.#{key.shift}(#{key.join(',')})"
         | 
| 69 | 
            +
                          else
         | 
| 70 | 
            +
                            "req.#{key}"
         | 
| 71 | 
            +
                          end
         | 
| 72 | 
            +
                        }.join(', ')
         | 
| 73 | 
            +
                      end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                      def assign_index_params(route)
         | 
| 76 | 
            +
                        route.instance_variable_get("@named_captures").map { |k, index|
         | 
| 77 | 
            +
                          "routing_args[#{k.inspect}] = param_matches[#{index}] if param_matches[#{index}]"
         | 
| 78 | 
            +
                        }
         | 
| 79 | 
            +
                      end
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
                end
         | 
| 82 | 
            +
              end
         | 
| 83 | 
            +
            end
         | 
| @@ -0,0 +1,108 @@ | |
| 1 | 
            +
            module Rack
         | 
| 2 | 
            +
              module Mount
         | 
| 3 | 
            +
                module Generation
         | 
| 4 | 
            +
                  module Route #:nodoc:
         | 
| 5 | 
            +
                    class DynamicSegment #:nodoc:
         | 
| 6 | 
            +
                      attr_reader :name, :requirement
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                      def initialize(name, requirement)
         | 
| 9 | 
            +
                        @name, @requirement = name.to_sym, requirement
         | 
| 10 | 
            +
                      end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                      def ==(obj)
         | 
| 13 | 
            +
                        @name == obj.name && @requirement == obj.requirement
         | 
| 14 | 
            +
                      end
         | 
| 15 | 
            +
                    end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    def initialize(*args)
         | 
| 18 | 
            +
                      super
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                      @segments = segments(@path).freeze
         | 
| 21 | 
            +
                      @required_params = @segments.find_all { |s|
         | 
| 22 | 
            +
                        s.is_a?(DynamicSegment)
         | 
| 23 | 
            +
                      }.map { |s| s.name }.freeze
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    def url_for(params = {})
         | 
| 27 | 
            +
                      params = (params || {}).dup
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                      return nil if @segments.empty?
         | 
| 30 | 
            +
                      return nil unless @required_params.all? { |p| params.include?(p) }
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                      path = generate_from_segments(@segments, params, @defaults)
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                      @defaults.each do |key, value|
         | 
| 35 | 
            +
                        params.delete(key)
         | 
| 36 | 
            +
                      end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                      if params.any?
         | 
| 39 | 
            +
                        path << "?#{Rack::Utils.build_query(params)}"
         | 
| 40 | 
            +
                      end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                      path
         | 
| 43 | 
            +
                    end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    private
         | 
| 46 | 
            +
                      # Segment data structure used for generations
         | 
| 47 | 
            +
                      # => ['/people', ['.', :format]]
         | 
| 48 | 
            +
                      def segments(regexp)
         | 
| 49 | 
            +
                        parse_segments(Utils.extract_regexp_parts(regexp))
         | 
| 50 | 
            +
                      rescue ArgumentError
         | 
| 51 | 
            +
                        []
         | 
| 52 | 
            +
                      end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                      def parse_segments(segments)
         | 
| 55 | 
            +
                        s = []
         | 
| 56 | 
            +
                        segments.each do |part|
         | 
| 57 | 
            +
                          if part.is_a?(Utils::Capture)
         | 
| 58 | 
            +
                            if part.named?
         | 
| 59 | 
            +
                              source = part.map { |p| p.is_a?(Array) ? "(#{p.join})?" : p }.join
         | 
| 60 | 
            +
                              requirement = Regexp.compile(source)
         | 
| 61 | 
            +
                              s << DynamicSegment.new(part.name, requirement)
         | 
| 62 | 
            +
                            else
         | 
| 63 | 
            +
                              s << parse_segments(part)
         | 
| 64 | 
            +
                            end
         | 
| 65 | 
            +
                          else
         | 
| 66 | 
            +
                            source = part.gsub('\\.', '.').gsub('\\/', '/')
         | 
| 67 | 
            +
                            if Regexp.compile("^(#{part})$") =~ source
         | 
| 68 | 
            +
                              s << source
         | 
| 69 | 
            +
                            else
         | 
| 70 | 
            +
                              raise ArgumentError, "failed to parse #{part.inspect}"
         | 
| 71 | 
            +
                            end
         | 
| 72 | 
            +
                          end
         | 
| 73 | 
            +
                        end
         | 
| 74 | 
            +
                        s
         | 
| 75 | 
            +
                      end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                      def generate_from_segments(segments, params, defaults, optional = false)
         | 
| 78 | 
            +
                        if optional
         | 
| 79 | 
            +
                          return Const::EMPTY_STRING if segments.all? { |s| s.is_a?(String) }
         | 
| 80 | 
            +
                          return Const::EMPTY_STRING if segments.flatten.all? { |s|
         | 
| 81 | 
            +
                            if s.is_a?(DynamicSegment) && params[s.name]
         | 
| 82 | 
            +
                              params[s.name].to_s !~ s.requirement
         | 
| 83 | 
            +
                            else
         | 
| 84 | 
            +
                              true
         | 
| 85 | 
            +
                            end
         | 
| 86 | 
            +
                          }
         | 
| 87 | 
            +
                        end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                        generated = segments.map do |segment|
         | 
| 90 | 
            +
                          case segment
         | 
| 91 | 
            +
                          when String
         | 
| 92 | 
            +
                            segment
         | 
| 93 | 
            +
                          when DynamicSegment
         | 
| 94 | 
            +
                            params[segment.name] || defaults[segment.name]
         | 
| 95 | 
            +
                          when Array
         | 
| 96 | 
            +
                            generate_from_segments(segment, params, defaults, true) || Const::EMPTY_STRING
         | 
| 97 | 
            +
                          end
         | 
| 98 | 
            +
                        end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                        # Delete any used items from the params
         | 
| 101 | 
            +
                        segments.each { |s| params.delete(s.name) if s.is_a?(DynamicSegment) }
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                        generated.join
         | 
| 104 | 
            +
                      end
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
              end
         | 
| 108 | 
            +
            end
         | 
| @@ -0,0 +1,60 @@ | |
| 1 | 
            +
            module Rack
         | 
| 2 | 
            +
              module Mount
         | 
| 3 | 
            +
                module Generation
         | 
| 4 | 
            +
                  module RouteSet
         | 
| 5 | 
            +
                    DEFAULT_KEYS = [] # [:controller, :action].freeze
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                    def initialize(options = {})
         | 
| 8 | 
            +
                      @named_routes = {}
         | 
| 9 | 
            +
                      @generation_keys = DEFAULT_KEYS
         | 
| 10 | 
            +
                      @generation_graph = NestedSet.new
         | 
| 11 | 
            +
                      super
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    def add_route(*args)
         | 
| 15 | 
            +
                      route = super
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                      @named_routes[route.name] = route if route.name
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                      keys = @generation_keys.map { |key| route.defaults[key] }
         | 
| 20 | 
            +
                      @generation_graph[*keys] = route
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                      route
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    def url_for(*args)
         | 
| 26 | 
            +
                      params = args.last.is_a?(Hash) ? args.pop : {}
         | 
| 27 | 
            +
                      named_route = args.shift
         | 
| 28 | 
            +
                      route = nil
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                      if named_route
         | 
| 31 | 
            +
                        unless route = @named_routes[named_route.to_sym]
         | 
| 32 | 
            +
                          raise RoutingError, "#{named_route} failed to generate from #{params.inspect}"
         | 
| 33 | 
            +
                        end
         | 
| 34 | 
            +
                      else
         | 
| 35 | 
            +
                        keys = @generation_keys.map { |key| params[key] }
         | 
| 36 | 
            +
                        @generation_graph[*keys].each do |r|
         | 
| 37 | 
            +
                          if r.defaults.all? { |k, v| params[k] == v }
         | 
| 38 | 
            +
                            route = r
         | 
| 39 | 
            +
                            break
         | 
| 40 | 
            +
                          end
         | 
| 41 | 
            +
                        end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                        unless route
         | 
| 44 | 
            +
                          raise RoutingError, "No route matches #{params.inspect}"
         | 
| 45 | 
            +
                        end
         | 
| 46 | 
            +
                      end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                      route.url_for(params)
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                    def freeze
         | 
| 52 | 
            +
                      @named_routes.freeze
         | 
| 53 | 
            +
                      @generation_keys.freeze
         | 
| 54 | 
            +
                      @generation_graph.freeze
         | 
| 55 | 
            +
                      super
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
            end
         | 
| @@ -0,0 +1,143 @@ | |
| 1 | 
            +
            require 'active_support/inflector'
         | 
| 2 | 
            +
            require 'merb-core/dispatch/router'
         | 
| 3 | 
            +
            require 'rack/request'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Rack
         | 
| 6 | 
            +
              module Mount
         | 
| 7 | 
            +
                class RouteSet
         | 
| 8 | 
            +
                  def prepare(*args, &block)
         | 
| 9 | 
            +
                    Mappers::Merb.new(self).prepare(*args, &block)
         | 
| 10 | 
            +
                    freeze
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                module Mappers
         | 
| 15 | 
            +
                  class Merb
         | 
| 16 | 
            +
                    class ::Merb::Router::Behavior
         | 
| 17 | 
            +
                      def to_route
         | 
| 18 | 
            +
                        raise Error, 'The route has already been committed.' if @route
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                        controller = @params[:controller]
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                        if prefixes = @options[:controller_prefix]
         | 
| 23 | 
            +
                          controller ||= ':controller'
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                          prefixes.reverse_each do |prefix|
         | 
| 26 | 
            +
                            break if controller =~ %r{^/(.*)} && controller = $1
         | 
| 27 | 
            +
                            controller = "#{prefix}/#{controller}"
         | 
| 28 | 
            +
                          end
         | 
| 29 | 
            +
                        end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                        @params.merge!(:controller => controller.to_s.gsub(%r{^/}, '')) if controller
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                        identifiers = @identifiers.sort { |(first,_),(sec,_)| first <=> sec || 1 }
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                        Thread.current[:merb_routes] << [
         | 
| 36 | 
            +
                          @conditions.dup,
         | 
| 37 | 
            +
                          @params,
         | 
| 38 | 
            +
                          @blocks,
         | 
| 39 | 
            +
                          { :defaults => @defaults.dup, :identifiers => identifiers }
         | 
| 40 | 
            +
                        ]
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                        self
         | 
| 43 | 
            +
                      end
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    class DeferredProc
         | 
| 47 | 
            +
                      def initialize(app, deferred_procs)
         | 
| 48 | 
            +
                        @app, @proc = app, deferred_procs.cache
         | 
| 49 | 
            +
                      end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                      def call(env)
         | 
| 52 | 
            +
                        # TODO: Change this to a Merb request
         | 
| 53 | 
            +
                        request = Rack::Request.new(env)
         | 
| 54 | 
            +
                        params  = env[Const::RACK_ROUTING_ARGS]
         | 
| 55 | 
            +
                        result  = @proc.call(request, params)
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                        if result
         | 
| 58 | 
            +
                          @app.call(env)
         | 
| 59 | 
            +
                        else
         | 
| 60 | 
            +
                          Const::NOT_FOUND_RESPONSE
         | 
| 61 | 
            +
                        end
         | 
| 62 | 
            +
                      end
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    class RequestConditions
         | 
| 66 | 
            +
                      def initialize(app, conditions)
         | 
| 67 | 
            +
                        @app, @conditions = app, conditions
         | 
| 68 | 
            +
                      end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                      def call(env)
         | 
| 71 | 
            +
                        # TODO: Change this to a Merb request
         | 
| 72 | 
            +
                        request = Rack::Request.new(env)
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                        @conditions.each do |method, expected|
         | 
| 75 | 
            +
                          unless request.send(method) == expected
         | 
| 76 | 
            +
                            return Const::NOT_FOUND_RESPONSE
         | 
| 77 | 
            +
                          end
         | 
| 78 | 
            +
                        end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                        @app.call(env)
         | 
| 81 | 
            +
                      end
         | 
| 82 | 
            +
                    end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    DynamicController = lambda { |env|
         | 
| 85 | 
            +
                      app = ActiveSupport::Inflector.camelize("#{env[Const::RACK_ROUTING_ARGS][:controller]}Controller")
         | 
| 86 | 
            +
                      app = ActiveSupport::Inflector.constantize(app)
         | 
| 87 | 
            +
                      app.call(env)
         | 
| 88 | 
            +
                    }
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                    attr_accessor :root_behavior
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    def initialize(set)
         | 
| 93 | 
            +
                      @set = set
         | 
| 94 | 
            +
                      @root_behavior = ::Merb::Router::Behavior.new.defaults(:action => 'index')
         | 
| 95 | 
            +
                    end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                    def prepare(first = [], last = [], &block)
         | 
| 98 | 
            +
                      Thread.current[:merb_routes] = []
         | 
| 99 | 
            +
                      begin
         | 
| 100 | 
            +
                        root_behavior._with_proxy(&block)
         | 
| 101 | 
            +
                        routes = Thread.current[:merb_routes]
         | 
| 102 | 
            +
                        routes.each { |route| add_route(*route) }
         | 
| 103 | 
            +
                        self
         | 
| 104 | 
            +
                      ensure
         | 
| 105 | 
            +
                        Thread.current[:merb_routes] = nil
         | 
| 106 | 
            +
                      end
         | 
| 107 | 
            +
                    end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                    def add_route(conditions, params, deferred_procs, options = {})
         | 
| 110 | 
            +
                      new_conditions = {}
         | 
| 111 | 
            +
                      new_conditions[:path] = conditions.delete(:path)[0]
         | 
| 112 | 
            +
                      new_conditions[:method] = conditions.delete(:method)
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                      requirements = {}
         | 
| 115 | 
            +
                      conditions.each do |k, v|
         | 
| 116 | 
            +
                        if v.is_a?(Regexp)
         | 
| 117 | 
            +
                          requirements[k.to_sym] = conditions.delete(k)
         | 
| 118 | 
            +
                        end
         | 
| 119 | 
            +
                      end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                      if new_conditions[:path].is_a?(String)
         | 
| 122 | 
            +
                        new_conditions[:path] = Utils.convert_segment_string_to_regexp(
         | 
| 123 | 
            +
                          new_conditions[:path], requirements, %w( / . ? ))
         | 
| 124 | 
            +
                      end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                      app = params.has_key?(:controller) ?
         | 
| 127 | 
            +
                        ActiveSupport::Inflector.constantize(ActiveSupport::Inflector.camelize("#{params[:controller]}Controller")) :
         | 
| 128 | 
            +
                        DynamicController
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                      if deferred_procs.any?
         | 
| 131 | 
            +
                        app = DeferredProc.new(app, deferred_procs.first)
         | 
| 132 | 
            +
                      end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                      if conditions.any?
         | 
| 135 | 
            +
                        app = RequestConditions.new(app, conditions)
         | 
| 136 | 
            +
                      end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                      @set.add_route(app, new_conditions, params)
         | 
| 139 | 
            +
                    end
         | 
| 140 | 
            +
                  end
         | 
| 141 | 
            +
                end
         | 
| 142 | 
            +
              end
         | 
| 143 | 
            +
            end
         |