Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to do "/blogs/:year/:month/:day/:permalink" routes in Rails?

I've got a blogs_controller with a Blog resource, so I've got your typical routes right now as follows:

/blogs/new
/blogs/1
/blogs/1/edit #etc

But here's what I want:

/blogs/new
/blogs/2010/01/08/1-to_param-or-something
/blogs/2010/01/08/1-to_param-or-something/edit #etc
...
/blogs/2010/01 # all posts for January 2010, but how to specify custom action?

I know that I can do this with a combination of map.resources and map.connect, but I've got a lot of views that link to other pages via "new_blog_path" etc and I don't want to have to go and edit those. Is this possible with map.resources alone? It might not be easy, but I'm not against being clever. I was thinking of something like:

map.resources :blogs, :path_prefix => ':year/:month/:day', :requirements => {:year => /\d{4}/, :month => /\d{1,2}/, :day => /\d{1,2}/}

But I'm not sure how that works with actions like 'new' or 'create', and it also gives me a route like /2010/01/08/blogs/1-to_param-etc with blogs in the middle of the URL.

So, is there a clever solution that I'm missing, or do I need to go the map.connect route?

like image 626
carpeliam Avatar asked Jan 08 '10 19:01

carpeliam


2 Answers

I ran into the same issue recently, and, while this may not be what you're looking for, this is what I've done to take care of it:

config/routes.rb:

map.entry_permalink 'blog/:year/:month/:day/:slug',
                    :controller => 'blog_entries',
                    :action     => 'show',
                    :year       => /(19|20)\d{2}/,
                    :month      => /[01]?\d/,
                    :day        => /[0-3]?\d/

blog_entries_controller.rb:

def show
  @blog_entry = BlogEntry.find_by_permalink(params[:slug])
end

blog_entries_helper.rb:

def entry_permalink(e)
  d = e.created_at
  entry_permalink_path :year => d.year, :month => d.month, :day => d.day, :slug => e.permalink
end

_entry.html.erb:

<h2><%= link_to(entry.title, entry_permalink(entry)) %></h2>

and for the sake of completeness:

blog_entry.rb:

before_save :create_permalink

#...

private

def create_permalink
  self.permalink = title.to_url
end

The #to_url method comes from rsl's Stringex.

I'm still new to Rails (and programming) myself, but this is probably the simplest way to go about it. This isn't a RESTful way of going about things so you don't gain the benefit of map.resources, unfortunately.

I'm not sure (because I haven't tried it), but you might be able to create the appropriate helpers in application_helper.rb to override the default route helpers for blog_path, et al. If that works, then you won't have to change any of your view code.

If you're feeling adventurous, you might check out Routing Filter. I considered using it, but it seems like overkill for this task.

Also, if you're not aware, two things you can do to test your routes/paths from within script/console:

rs = ActionController::Routing::Routes
rs.recognize_path '/blog/2010/1/10/entry-title'

and

app.blog_entry_path(@entry)

Good luck!

like image 85
Jay Mendoza Avatar answered Sep 29 '22 08:09

Jay Mendoza


From the API Docs:

map.connect 'articles/:year/:month/:day',
            :controller => 'articles',
            :action     => 'find_by_date',
            :year       => /\d{4}/,
            :month      => /\d{1,2}/,
            :day        => /\d{1,2}/

Using the route above, the URL "localhost:3000/articles/2005/11/06" maps to

params = { :year => '2005', :month => '11', :day => '06' }

Looks like you want to do the same thing, but suffix a post slug. Your new and edit links would still be the "old school" links, like "localhost:3000/articles/1/edit" and "localhost:3000/articles/new". Just the "show" link should be updated with this.

like image 24
Jarrett Meyer Avatar answered Sep 29 '22 08:09

Jarrett Meyer