首先,我們將引進 match
match 會將參數記憶起來,然後去 match URL
# rainbow/lib/rainbow/routing.rb
module Rainbow
class RouteObject
def match(url, *args)
options = {}
options = args.pop if args[-1].is_a?(Hash)
options[:default] ||= {}
dest = nil
dest = args.pop if args.size > 0
raise "Too many args!" if args.size > 0
parts = url.split("/")
parts.select! { |p| !p.empty? }
vars = []
regexp_parts = parts.map do |part|
if part[0] == ":"
vars << part[1..-1]
"([a-zA-Z0-9]+)"
elsif part[0] == "*"
vars << part[1..-1]
"(.*)"
else
part
end
end
regexp = regexp_parts.join("/")
@rainbow.push({
:regexp => Regexp.new("^/#{regexp}$"),
:vars => vars,
:dest => dest,
:options => options,
})
end
end
end
第一行會去抓 option 這個 hash 並執行 args.pop
然後,我們會去抓 URL
並且用 / 去拆開
使用正則表達式去比對運達式,並抓出對應的值
並把他們放到一個陣列中
最終,我們將所有 index 值集結,放到一個 hash 中當作 params
# rainbow/lib/rainbow/routing.rb
module Rainbow
class RouteObject
def check_url(url)
@rainbow.each do |r|
m = r[:regexp].match(url)
if m
options = r[:options]
params = options[:default].dup
r[:vars].each_with_index do |v, i|
params[v] = m.captures[i]
end
if r[:dest]
return get_dest(r[:dest], params)
else
controller = params["controller"]
action = params["action"]
return get_dest("#{controller}" + "##{action}", params)
end
end
end
nil
end
def get_dest(dest, routing_params = {})
return dest
if dest.respond_to?(:call)
if dest =~ /^([^#]+)#([^#]+)$/
name = $1.capitalize
con = Object.const_get("#{name}Controller")
return con.action($2, routing_params)
end
raise "No destination: #{dest.inspect}!"
end
end
end
check_url 會透過依照這個規範找到第一個符合條件的值
假如 match 到,他會做一個 params 物件,並且去比對正則表達式,抓出符合條件的變數
假如這個條件有 controller#action,會呼叫 get_dest 並加入到 Rack 應用程式中
沒有的話就會去抓 :controller and :action 這兩個變數
同樣呼叫 get_dest 加入到 Rack 應用程式中
不過究竟是怎麼加入到 Rack 應用程式?
首先,確認 call() 方法是否有回應,有的話就返回符合條件的值
如果已經存在在 Rack 應用程式中,我們可以直接使用它
除此之外,我們會將值放上 #
讓他去尋找 controller 中的 action
當我們抓到,我們可以傳遞 routing 中的參數
這就是為什麼我們會在 controller 的 action() 中放參數的原因