Class: Rack::JetRouter

Inherits:
Object
  • Object
show all
Defined in:
lib/rack/jet_router.rb

Overview

Jet-speed router class, derived from Keight.rb.

ex:

urlpath_mapping = [
    ['/'                       , welcome_app],
    ['/api', [
        ['/books', [
            [''                , books_api],
            ['/:id(.:format)'  , book_api],
            ['/:book_id/comments/:comment_id', comment_api],
        ]],
    ]],
    ['/admin', [
        ['/books'              , admin_books_app],
    ]],
]
router = Rack::JetRouter.new(urlpath_mapping)
router.lookup('/api/books/123.html')
    #=> [book_api, {"id"=>"123", "format"=>"html"}]
status, headers, body = router.call(env)

### or:
urlpath_mapping = [
    ['/'                       , {GET: welcome_app}],
    ['/api', [
        ['/books', [
            [''                , {GET: book_list_api, POST: book_create_api}],
            ['/:id(.:format)'  , {GET: book_show_api, PUT: book_update_api}],
            ['/:book_id/comments/:comment_id', {POST: comment_create_api}],
        ]],
    ]],
    ['/admin', [
        ['/books'              , {ANY: admin_books_app}],
    ]],
]
router = Rack::JetRouter.new(urlpath_mapping)
router.lookup('/api/books/123')
    #=> [{"GET"=>book_show_api, "PUT"=>book_update_api}, {"id"=>"123", "format"=>nil}]
status, headers, body = router.call(env)

Constant Summary collapse

RELEASE =
'$Release: 1.2.0 $'.split()[1]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(mapping, urlpath_cache_size: 0, enable_urlpath_param_range: true) ⇒ JetRouter

Returns a new instance of JetRouter.



60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/rack/jet_router.rb', line 60

def initialize(mapping, urlpath_cache_size: 0,
                        enable_urlpath_param_range: true)
  @enable_urlpath_param_range = enable_urlpath_param_range
  #; [!u2ff4] compiles urlpath mapping.
  (@urlpath_rexp,          # ex: {'/api/books'=>BooksApp}
   @fixed_urlpath_dict,    # ex: [[%r'\A/api/books/([^./]+)\z', ['id'], BookApp]]
   @variable_urlpath_list, # ex: %r'\A(?:/api(?:/books(?:/[^./]+(\z))))\z'
   @all_entrypoints,       # ex: [['/api/books', BooksAPI'], ['/api/orders', OrdersAPI]]
  ) = compile_mapping(mapping)
  ## cache for variable urlpath (= containg urlpath parameters)
  @urlpath_cache_size = urlpath_cache_size
  @variable_urlpath_cache = urlpath_cache_size > 0 ? {} : nil
end

Instance Attribute Details

#urlpath_rexpObject (readonly)

Returns the value of attribute urlpath_rexp.



74
75
76
# File 'lib/rack/jet_router.rb', line 74

def urlpath_rexp
  @urlpath_rexp
end

Instance Method Details

#call(env) ⇒ Object

Finds rack app according to PATH_INFO and REQUEST_METHOD and invokes it.



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
# File 'lib/rack/jet_router.rb', line 77

def call(env)
  #; [!fpw8x] finds mapped app according to env['PATH_INFO'].
  req_path = env['PATH_INFO']
  app, urlpath_params = lookup(req_path)
  #; [!wxt2g] guesses correct urlpath and redirects to it automaticaly when request path not found.
  #; [!3vsua] doesn't redict automatically when request path is '/'.
  if ! app && should_redirect?(env)
    location = req_path =~ /\/\z/ ? req_path[0..-2] : req_path + '/'
    app, urlpath_params = lookup(location)
    if app
      #; [!hyk62] adds QUERY_STRING to redirect location.
      qs = env['QUERY_STRING']
      location = "#{location}?#{qs}" if qs && ! qs.empty?
      return redirect_to(location)
    end
  end
  #; [!30x0k] returns 404 when request urlpath not found.
  return error_not_found(env) unless app
  #; [!gclbs] if mapped object is a Hash...
  if app.is_a?(Hash)
    #; [!p1fzn] invokes app mapped to request method.
    #; [!5m64a] returns 405 when request method is not allowed.
    #; [!ys1e2] uses GET method when HEAD is not mapped.
    #; [!2hx6j] try ANY method when request method is not mapped.
    dict = app
    req_meth = env['REQUEST_METHOD']
    app = dict[req_meth] || (req_meth == 'HEAD' ? dict['GET'] : nil) || dict['ANY']
    return error_not_allowed(env) unless app
  end
  #; [!2c32f] stores urlpath parameters as env['rack.urlpath_params'].
  store_urlpath_params(env, urlpath_params)
  #; [!hse47] invokes app mapped to request urlpath.
  return app.call(env)   # make body empty when HEAD?
end

#each(&block) ⇒ Object

Yields pair of urlpath pattern and app.



155
156
157
158
# File 'lib/rack/jet_router.rb', line 155

def each(&block)
  #; [!ep0pw] yields pair of urlpath pattern and app.
  @all_entrypoints.each(&block)
end

#lookup(req_path) ⇒ Object Also known as: find

Finds app or Hash mapped to request path.

ex:

lookup('/api/books/123')   #=> [BookApp, {"id"=>"123"}]


116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/rack/jet_router.rb', line 116

def lookup(req_path)
  #; [!24khb] finds in fixed urlpaths at first.
  #; [!iwyzd] urlpath param value is nil when found in fixed urlpaths.
  obj = @fixed_urlpath_dict[req_path]
  return obj, nil if obj
  #; [!upacd] finds in variable urlpath cache if it is enabled.
  #; [!1zx7t] variable urlpath cache is based on LRU.
  cache = @variable_urlpath_cache
  if cache && (pair = cache.delete(req_path))
    cache[req_path] = pair
    return pair
  end
  #; [!vpdzn] returns nil when urlpath not found.
  m = @urlpath_rexp.match(req_path)
  return nil unless m
  index = m.captures.find_index('')
  return nil unless index
  #; [!ijqws] returns mapped object and urlpath parameter values when urlpath found.
  full_urlpath_rexp, param_names, obj, range = @variable_urlpath_list[index]
  if range
    ## "/books/123"[7..-1] is faster than /\A\/books\/(\d+)\z/.match("/books/123")
    str = req_path[range]
    param_values = [str]
  else
    m = full_urlpath_rexp.match(req_path)
    param_values = m.captures
  end
  vars = build_urlpath_parameter_vars(param_names, param_values)
  #; [!84inr] caches result when variable urlpath cache enabled.
  if cache
    cache.shift() if cache.length >= @urlpath_cache_size
    cache[req_path] = [obj, vars]
  end
  return obj, vars
end