By now you should have a blog with users, administrators and even a tag system as of part 3 in this series, so now we’re onto something for greater aesthetics. In this case, custom URLs for posts and tags.
We’ll start by running:
rails g migration add_url_to_tags_and_users
The above generates a migration that we can open up (usually in db/migrate
) and add the following inside the change
method:
add_column :tags, :url, :string
add_column :posts, :url, :string
This will add columns for the url component to both the Tag and Post model and we can send these changes to the database with rake db:migrate
. Next add the following to both app/models/post.rb and app/models/tag.rb before the end
keyword:
validates :url, uniqueness: true
def to_param
url
end
def self.find_by_param(input)
find_by_url(input)
end
These tell Rails to use the :url
parameter when making and using URLs such as localhost:3000/post/welcome instead of localhost:3000/post/1 and also makes sure that the URL parameter is unique in the table. Lastly it also creates a method usable by the Tag and Post classes that allows a user to find by the url parameter with find_by_param
which will be used later.. Next edit the create
and update
methods in app/controllers/posts_controller.rb and put the following near the top, directly above @post.tags = ready_tags
:
@post.url = post_params[:title].squish.downcase.tr(" ","_").gsub(/[^0-9A-Za-z_]/, '')
Now we need to find every instance of .find(params[:id])
and change it to .find_by_param(params[:id])
allowing us to use the new :id
parameter (which will be the :url
variable) as the identifier. Do that in both app/controllers/posts_controller.rb and app/controllers/tags_controller.rb.
Next we’ll want to modify the ready_tags
private function to look like this:
def ready_tags
tags = tag_params[:tag_ids].split(/,\s*/)
tags_ready = []
tags.each do |tag|
temp = Tag.find_by_url(tag.squish.downcase.tr(" ","_").gsub(/[^0-9A-Za-z_]/, ''))
if temp == nil
temp = Tag.create(name: tag, url: tag.squish.downcase.tr(" ","_").gsub(/[^0-9A-Za-z_]/, ''))
end
tags_ready.push(temp)
end
tags_ready
end
This makes sure it’s searching for tags by unique URL and also creates the tag with a unique URL as well.
You will also need to modify your app/controllers/posts_controller.rb create
method, wrapping everything in the method in the following block:
if post_params[:title].squish.downcase.tr(" ","_").gsub(/[^0-9A-Za-z_]/, '') == 'new'
redirect_to posts_path, notice: 'Post cannot be titled "new".'
else
[EXISTING CODE]
end
Now do similar with the update
method, wrapping it all in the following:
if post_params[:title].squish.downcase.tr(" ","_").gsub(/[^0-9A-Za-z_]/, '') == 'new'
render :edit
else
[EXISTING CODE]
end
This prevents someone creating a post that would have the URL ‘new’, which would break the resourceful routing rails uses. You’ll also need to modify config/routes.rb, removing the line resources :tags
and replacing it with:
get '/tags', to: 'tags#index', as 'tags'
get '/tags/:id', to: 'tags#show'
delete '/tags/:id', to: 'tags#destroy'
This non-resourcefully defines the routes used for tags (index
, show
, destroy
) and also allows us to create a tag labelled “new” without breaking anything.
You should now have a working, aesthetically pleasing URL system for your blog. This will likely be the last of the tutorials in this series on building a blog in Rails, the implementation you’ve created up till now should have a number of standard and useful features for blogs and you should have enough expertise to start developing your own features and branching out further.