I'm really hoping someone can help this Rails n00b with this issue. I've been researching, trying, crashing(-and-burning) over the past few days on how to implement the standard /YYYY/MM/Title-Slug URL structure for a blog I'm putting together. I've discovered and successfully implemented Friendly_Id to handle the sluggification (along with history tracking), but for the life of me I can't get the Year/Month part of the routing problem resolved.
Before I forget: I'm using Rails 4.2.3 and Ruby 2.2.1p85 (because, yes, I leveraged a bunch of stuff from RailsTutorial.org) :-)
To minimize confusion (or collateral damage), I've scaffolded a super-simple blog app to try to get it all working:
$ rails new blog
[...]
$ cd blog
# (Add friendly_id to Gemfile & install)
$ rails generate friendly_id
$ rails generate scaffold post title content slug:string:uniq
[...]
$ rake db:migrate
Made the following changes to post.rb:
class Post < ActiveRecord::Base
extend FriendlyId
friendly_id :title, use: :slugged
def year
created_at.localtime.year
end
def month
created_at.localtime.strftime("%m")
end
end
posts_controller.rb:
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
def index
@posts = Post.order('created_at DESC').all
end
def show
end
def new
@post = Post.new
end
def edit
end
def create
@post = Post.new(post_params)
respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: @post }
else
format.html { render :new }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if @post.update(post_params)
format.html { redirect_to @post, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: @post }
else
format.html { render :edit }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
def destroy
@post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_post
@post = Post.friendly.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :content, :published_at, :slug)
end
end
index.html.erb
<p id="notice"><%= notice %></p>
<h1>Listing Posts</h1>
<table>
<thead>
<tr>
<th>Title</th>
<th>Content</th>
<th>Slug</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @posts.each do |post| %>
<tr>
<td><%= post.title %></td>
<td><%= post.content %></td>
<td><%= post.slug %></td>
<td><%= link_to 'Show', post_date_path(post) %></td>
<td><%= link_to 'Edit', edit_post_path(post) %></td>
<td><%= link_to 'Destroy', post, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Post', new_post_path %>
routes.rb:
Rails.application.routes.draw do
get '/posts', to: 'posts#index', as: :posts_path
get '/posts/:year', to: 'posts#index', as: :posts_year,
constraints: { year: /\d{4}/ }
get '/posts/:year/:month', to: 'posts#index', as: :posts_month,
constraints: { year: /\d{4}/, month: /\d{1,2}/ }
get '/posts/:year/:month/:slug', to: 'posts#show', as: :post_date,
constraints: { year: /\d{4}/, month: /\d{1,2}/, slug: /[a-z0-9\-]+/ }
resources :posts
end
These changes are mostly from updating the Rails3 code from this Stackoverflow Q&A as that has gotten me the furthest out of other options I've discovered. I'm currently running into the following Controller Exception:
Showing […]/app/views/posts/index.html.erb where line #24 raised:
No route matches {:action=>"show", :controller=>"posts", :month=>nil, :slug=>nil, :year=>#<Post id: 23, title: "test", content: "", slug: "test-4", created_at: "2015-09-01 21:05:48", updated_at: "2015-09-01 21:05:48">} missing required keys: [:month, :slug, :year]
Other solutions that have failed in other slightly soul-crushing ways:
- "Rails 4 Blog /:year/:month/:title with clean routing" (see comments for link) - this appears to not work because of a 4.1.2 bug that appears to never having been fixed)
- "Rails 4.1.2 - to_param escapes slashes (and breaks app)" (see comments for link) - this may work, but I wasn't able to translate the answer for my purposes
- "Friendly_Id slugs with ids or dates separated by slashes" (see comments for link)
To be clear: I'm not wedded to this approach - I'm more than happy to go an entirely different way. I would just like my final blog to function as:
http://www.example.com/blog/
(for the index)http://www.example.com/2015/
(for an index of 2015 posts)http://www.example.com/2015/09/
(for an index of posts from Sept'15)http://www.example.com/2015/09/pleeze-help-me
(for an individual post)
Many thanks in advance!
EDIT
In going down some additional rabbit holes to get a solution to this, I'm wondering if using URL rewriting would be the only? approach for this issue. My gut says that it's pounding a round peg into a square hole (especially given that the blog isn't live yet, so there's no chance that there are links out in the wild pointing to the current URL structure), but I'm failing to find a better alternative.
I've found two options that might help with the rewrite approach: refraction (see comments for link) and rack-rewrite (see comments for link)
Does anyone have any input on this alternative approach and/or these plugins?
Thanks!
PS - There appears to be an update to SO permissions that now require at least 10 reputation to post more than 2 links, so I had to remove all the links in order to post this edit. I've moved them to the comments so I can get the edit saved.