File: route.rb

package info (click to toggle)
libinnate-ruby 2010.07-1
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 812 kB
  • ctags: 621
  • sloc: ruby: 4,242; makefile: 2
file content (114 lines) | stat: -rw-r--r-- 3,128 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
module Innate
  # Innate support simple routing using string, regex and lambda based routers.
  # Route are stored in a dictionary, which supports hash-like access but
  # preserves order, so routes are evaluated in the order they are added.
  #
  # This middleware should wrap Innate::DynaMap.
  #
  # Please note that Rack::File is put before Route and Rewrite, that means
  # that you cannot apply routes to static files unless you add your own route
  # middleware before.
  #
  # String routers are the simplest way to route in Innate. One path is
  # translated into another:
  #
  #   Innate::Route[ '/foo' ] = '/bar'
  #     '/foo'  =>  '/bar'
  #
  # Regex routers allow matching against paths using regex. Matches within
  # your regex using () are substituted in the new path using printf-like
  # syntax.
  #
  #   Innate::Route[ %r!^/(\d+)\.te?xt$! ] = "/text/%d"
  #     '/123.txt'  =>  '/text/123'
  #     '/789.text' =>  '/text/789'
  #
  # For more complex routing, lambda routers can be used. Lambda routers are
  # passed in the current path and request object, and must return either a new
  # path string, or nil.
  #
  #   Innate::Route[ 'name of route' ] = lambda{ |path, request|
  #     '/bar' if path == '/foo' and request[:bar] == '1'
  #   }
  #     '/foo'        =>  '/foo'
  #     '/foo?bar=1'  =>  '/bar'
  #
  # Lambda routers can also use this alternative syntax:
  #
  #   Innate::Route('name of route') do |path, request|
  #     '/bar' if path == '/foo' and request[:bar] == '1'
  #   end
  #
  # NOTE: Use self::ROUTES notation in singleton methods to force correct
  #       lookup.

  class Route
    ROUTES = []

    def self.[](key)
      found = self::ROUTES.assoc(key)
      found[1] if found
    end

    def self.[]=(key, value)
      self::ROUTES.delete_if{|route_key, route_value| route_key == key }
      self::ROUTES << [key, value]
    end

    def self.clear
      self::ROUTES.clear
    end

    def initialize(app = Innate::DynaMap)
      @app = app
    end

    def call(env)
      path = env['PATH_INFO']
      path << '/' if path.empty?

      if modified = resolve(path)
        Log.debug("%s routes %p to %p" % [self.class.name, path, modified])
        env['PATH_INFO'] = modified
      end

      @app.call(env)
    end

    def resolve(path)
      self.class::ROUTES.each do |key, value|
        if key.is_a?(Regexp)
          md = path.match(key)
          return value % md.to_a[1..-1] if md

        elsif value.respond_to?(:call)
          new_path = value.call(path, Current.request)
          return new_path if new_path

        elsif value.respond_to?(:to_str)
          return value.to_str if path == key

        else
          Log.error("Invalid route %p => %p" % [key, value])
        end
      end

      nil
    end
  end

  # Identical with Innate::Route, but is called before any Node::call is made
  class Rewrite < Route
    ROUTES = []
  end

  module SingletonMethods
    def Route(key, value = nil, &block)
      Route[key] = value || block
    end

    def Rewrite(key, value = nil, &block)
      Rewrite[key] = value || block
    end
  end
end