merb pluming - the router
TRANSCRIPT
Merb PlumbingThe Router
Who am I?
Some simple routes
Matching
Merb::Router.prepare do match("/signup").to( :controller => "users", :action => "new" ).name(:signup) end
Generating
url(:signup) => /signup
Routes with variables
Matching
match("/articles/:year/:month/:day/:title").to( :controller => "articles", :action => "show" ).name(:article)
Generating
url(:article, :year => 2008, :month => 07, :day => 22, :title => "Awesomeness")
/articles/2008/07/22/Awesomeness
Routes with conditions
Matching
match("/articles") do with(:controller => "articles") do match("/:title", :title => /[a-z]+/). to(:action => "with_title") match("/:id").to(:action => "with_id") end end
/articles/Hello_world => with_title /articles/123 => with_id
More conditions
Matching
Merb::Router.prepare do match(:domain => "blogofawesomeness.com") do match("/the-daily-awesome", :method => "get"). to(:articles) # Any other route... end end
The subdomain problem
match(:subdomain => /(.*)/). to(:account => ":subdomain[1]") do # application routes here... end
Optional path segments
Matching
match("/articles(/:year(/:month(/:day)))/:title"). to(:controller => "articles", :action => "show"). name(:article)
Generating
/articles/Hello => { :title => “Hello” }
/articles/2008/07/Hello => { :year => 2008, :month => “07” :title => “Hello” }
... and generation still works!
url(:article, :title => "Hello")
url(:article, :year => "2008", :title => "Hello")
url(:article, :year => "2008", :month => "07", :title => "Hello")
url(:article, :year => "2008", :month => "07", :day => "22", :title => "Hello")
Getting Fancy
Matching
match("(:pref)/:file(.:format)", :pref => ".*"). to(:controller => "cms").name(:page)
/hi => { :file => “hi” } /hi.xml => { :file => “hi”, :format => “xml”} /path/to/hi => { :pref => “/path/to”, :file => “hi }
And you can still generate it url(:page, :file => "hi")
/hi
url(:page, :pref => "/path/to", :file => "hi")
/path/to/hi
url(:page, :pref => "/hello", :file => "world", :format => "mp3")
/hello/world.mp3
The default route
def default_route(params = {}, &block) match("/:controller(/:action(/:id))(.:format)"). to(params, &block).name(:default) end
AWESOME!
Resource routes
Matching
resources :articles
/articles GET => index /articles POST => create /articles/new GET => new /articles/:id GET => show /articles/:id PUT => update /articles/:id DELETE => destroy /articles/:id/edit GET => edit /articles/:id/delete GET => delete
and you can nest them
Matching
resources :articles do resources :comments end
Generating
link_to "view comment", url(:article_comment, comment.article_id, comment.id)
Nested multiple times
resources :articles do resources :comments end resources :songs do resources :comments end resources :photos do resources :comments end
and... generating?
link_to "view comment", comment.commentable.is_a?(Article) ? url(:article_comment, comment.commentable.id, comment.id) : comment.commentable.is_a?(Song) ? url(:song_comment, comment.commentable.id, comment.id) : url(:photo_comment, comment.commentable.id, comment.id)
wha...?
Nah... how about this?
link_to "view comment", resource(comment.commentable, comment)
Some more examples resources :users do
resources :comments end
Generating
resource(@user) resource(@user, :comments) vs. vs. url(:user, @user) url(:user_comments, @user)
resource @user, @comment vs. url(:user_comment, @user, @comment)
Oh yeah...
match("/:project") do resources :tasks end
- While currently at /awesome/tasks
resource(@task) => /awesome/tasks/123
resource(@task, :project => "woot") => /woot/tasks/123
AWESOME!
Friendly URLs
Matching resources :users, :identify => :name
Generating
resource(@user) # => /users/carl
It works as a block too
Matching
identify(Article=>:permalink, User=>:login) do resources :users do resources :articles end end
Generating
resource(@user, @article)
/users/carl/articles/hello-world
AWESOME!
Redirecting legacy URLs
Old: /articles/123-hello-world
New: /articles/Hello_world
Handle it in the controller?
match("/articles/:id"). to(:controller => "articles", :action => "show")
Try to do it with Regexps?
match("/articles/:id", :id => /^\d+-/). to(:controller => "articles", :action => "legacy")
match("/articles/:url"). to(:controller => "articles", :action => "show")
Or, add your own logic!
match("/articles/:url").defer_to do |request, params| if article = Article.first(:url => params[:url]) params.merge(:article => article) elsif article = Article.first(:id => params[:url]) redirect url(:article, article.url) else false end end
Authentication works too
match("/secret").defer_to do |request, params| if request.session.authenticated? params end end
Works great for plugins
def protect(&block) protection = Proc.new do |request, params| if request.session.authenticated? params else redirect "/login" end end
defer(protection, &block) end
Awesome!
protect.match("/admin") do # ... admin routes here end
Thank you(Awesome!)