Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 3.2 friendly url routing by date

I want to implement blog\news application with ability to:

  1. show all posts at root: example.com/
  2. show all posts answering some year: example.com/2012/
  3. show all posts answering some year and month: example.com/2012/07/
  4. show some post by its date and slug: example.com/2012/07/slug-of-the-post

So I have created a mockup for routes.rb file:

# GET /?page=1
root :to => "posts#index"

match "/posts" => redirect("/")
match "/posts/" => redirect("/")

# Get /posts/2012/?page=1
match "/posts/:year", :to => "posts#index",
  :constraints => { :year => /\d{4}/ }

# Get /posts/2012/07/?page=1
match "/posts/:year/:month", :to => "posts#index",
  :constraints => { :year => /\d{4}/, :month => /\d{1,2}/ }

# Get /posts/2012/07/slug-of-the-post
match "/posts/:year/:month/:slug", :to => "posts#show", :as => :post,
  :constraints => { :year => /\d{4}/, :month => /\d{1,2}/, :slug => /[a-z0-9\-]+/ }

So I should work with params in index action and just get post by slug in show action (checking whether date is corect is an option):

# GET /posts?page=1
def index
  #render :text => "posts#index<br/><br/>#{params.to_s}"
  @posts = Post.order('created_at DESC').page(params[:page])
  # sould be more complicated in future
end

# GET /posts/2012/07/19/slug
def show
  #render :text => "posts#show<br/><br/>#{params.to_s}"
  @post = Post.find_by_slug(params[:slug])
end

Also I have to implement to_param for my model:

def to_param
  "#{created_at.year}/#{created_at.month}/#{slug}"
end

This is all I have learned from all night long searching in api/guides/SO.

But the problem is strange things keep happenning for me as new to rails:

  1. When I go to localhost/, the app breaks and says that it had invoked show action but first object in database had been recieved as :year (sic!):

    No route matches {:controller=>"posts", :action=>"show", :year=>#<Post id: 12, slug: "*", title: "*", content: "*", created_at: "2012-07-19 15:25:38", updated_at: "2012-07-19 15:25:38">}
    
  2. When I go to localhost/posts/2012/07/cut-test same thing happens:

    No route matches {:controller=>"posts", :action=>"show", :year=>#<Post id: 12, slug: "*", title: "*", content: "*", created_at: "2012-07-19 15:25:38", updated_at: "2012-07-19 15:25:38">}
    

I feel that there is something very easy that I haven't made, but I can't find what is it.

Anyway, this post will be helpful when it is solved, because there are solutions only for just slugs in url without date and similar but not useful questions.

like image 639
iEugene Avatar asked Jul 19 '12 21:07

iEugene


1 Answers

The problem was in post's path helper usage as post_path(post), because first parameter must be year since I use :as => :post in parametrized match in routes.rb.

Nevertheless, to make the entire solution clear here are some actions needed to make that all work proper:

  1. You must add proper path helpers names for each match, e.g.

    # Get /posts/2012/07/slug-of-the-post
    match "/posts/:year/:month/:slug", <...>,
      :as => :post_date
    

    Now you can use post_date_path("2012","12","end-of-the-world-is-near") in views.

    Same for posts_path, posts_year_path("2012"), posts_month_path("2012","12") if properly named.

    I advice not to use neither :as => :post in that match nor creating to_param in model file as it can break something you don't expect (as active_admin for me).

  2. Controller file posts-controller.rb should be filled with posts that needed extraction and checking of date's correctness before slug. Nevertheless in this state it is OK and breaks nothing.

  3. Model file posts.rb should be filled with year and month extraciton in proper format, e.g.:

    def year
      created_at.year
    end
    
    def month
      created_at.strftime("%m")
    end
    

    There is no to_param method really needed as I've noticed already.

like image 73
iEugene Avatar answered Oct 12 '22 09:10

iEugene