Rails #4: A real blog

This is the fourth article in my introduction to Rails. In the previous articles, we created a web application that let us edit articles, added support for comments to our articles, and added some nice AJAX effects. But a real blog needs feeds, a decent front page and some article formatting. In this article we will add all these. The article contains no groundbreaking features, but mostly dots a few i’s and crosses a few t’s both when it comes to the blog we’re building, and details in Rails.

My pretty, pretty articles

Our articles are currently not formatted. We want to support a simple markup language. As it turns out textile is perfect for the job. It transforms stuff “*like* _this_” to stuff “like this“. Let’s add textile support:

  1. Ruby support for textile is available in the RedCloth gem. Install it by typing gem install RedCloth on the command line.
  2. Import RedCloth in your Rails application. In config/environment.rb add the line require 'RedCloth' (after the other line that starts with ‘require’).
  3. Let’s add a helper for textile for all views. In app/helper/application_helper.rb, add the following:
  4. Update the views: In app/view/articles/show.html.erb change <%=h article.content %> (“h” is short for html_escape) and replace with <=textile article.content %>. Similarly change app/view/comments/_comment.hml.erb and replace <%=h comment.body %> with &l;%= textile comment.body %>. (We will deal with the article index later)
  5. Try adding some textile markup to your articles and test out the effect. You can put your test data in test/fixtures/articles.yml and load it with rake db:fixtures:load if you’re too lazy to type it in the forms.

A good first impression

If you’ve followed the article series, when you go to http://localhost:3000, you are presented with the default Rails-constructed start page. Lets make a more inviting web page and start at the articles page instead:

  1. In config/routes.rb, add a root route: map.root :controller => 'articles'. The comments in the file describes this further.
  2. Files in the public/ folder will take precedence over the routes, so you also need to delete public/index.html.
  3. If you go the http://localhost:3000, you will now be presented with the articles list. It looks horrible. Let’s clean up app/views/articles/index.html.erb. Here is my final version (the intro function was defined earlier in this blog post):
  4. We also need rss feeds for all our pages. Luckily, Rails comes with lots of built in support for this. First: We want to add auto discovery of the link in browsers that support this. app/view/layouts/articles.html.erb is applied around each page from the articles-controller, with the contents being displayed where the layout says <%= yield %>. Add the following to the <head> in app/view/layouts/articles.html.erb: <%= auto_discovery_link_tag :rss, { :controller => 'articles', :format => :rss } %>. If you refresh the page, you may notice a small RSS icon in the address bar of your browser! If you want to make the blog look nicer, the layout is also a good place to start.
  5. Clicking the RSS icon lets you go to the RSS action. However, this just takes you to a blank page. You have to go to app/controllers/articles_controller.rb add a line to the index action’s respond_to block with format.rss. Refreshing will now give you a proper error message: “Missing template articles/index.rss.erb”
  6. Create a file named app/views/articles/index.rss.builder. Files with “.builder” extensions are regular Ruby files, but in these files, a special object named xml is available (much like the “rjs” files we worked with earlier). We can use this to create RSS feeds. (w3schools have a useful RSS syntax reference). Here is mine:
  7. Refreshing the feed will now give you a complete RSS feed and the option to subscribe to it if your browser supports this.

Did they respond to my comment?

Many blogs lack a RSS for comments. This means that you’ll have a hard time keeping track of whether someone responded to our comment. We want to do better, and learn some more about routing at the same time:

  1. Add another auto discovery link to app/views/layouts/articles.html.erb after the first one: <%= auto_discovery_link_tag :rss, { :controller => 'comments', :article_id => @article, :format => :rss }, { :title => 'Comments' } %>.
  2. Go to an article that has some comments, and click the RSS icon by the address bar. In Firefox, this will how a menu of the two feeds, both labeled “Subscribe to ‘Comments'”. Click the link to view the comments feed.
  3. Creating the feed will be just like the article feed: You have to add format.rss to the respond_to block for the index action of app/controllers/comments_controller.rb and add app/view/comments/index.rss.builder. Go ahead and do that now.
  4. In app/view/comments/index.rss.builder, I made the item link point to xml.link comment_url(comment). You will have to define comment_url in app/helpers/comments_helper.rb (pay attention to the :only_path parameter):
  5. We have let comment_url and comment_path in both app/controllers/comments_controller.rb and app/helpers/comments_helper.rb point to the comments view. This is a pretty lame page, and we don’t want to make separate pages for the comments anyway. Instead: Hyperlink to an anchor to the comments: :controller => 'articles', :action => 'show', :id => comment.article, :anchor => dom_id(comment) in both the helper and the controller. Now all our links are fixed with one fell swoop! Delete app/views/comments/show.html.erb while you’re at it. This will break your tests in test/functional/comments_controller_test.rb so make sure you fix them. Both test_should_create_comment and test_should_update_comment should redirect to assert_redirected_to article_path(:id => assigns(:article), :anchor => @controller.dom_id(assigns(:comment))). test_should_show is no longer valid and can be deleted.

We now have a nice feed for the articles and comments, with hyperlinks that take us to the right place. However, we have opened a problem: If you try to subscribe to comments from the front page, you will get angry error messages about the fact that you can’t find the related article to search for comments for. We could avoid displaying this link when there’s no active article by doing something like <%= auto_discovery_link_tag .... if @article %>, but instead, we’ll use the chance to learn how to create a route for all comments.

  1. The problem starts with app/controllers/comments_controller.rb in the method find_article. Change the contents of the method to @article = Article.find(params[:article_id]) if params[:article_id]. This moves the problem to the index action. Change the first line to use Comment.find if there’s no article: @comments = @article ? @article.comments.find(:all) : Comment.find(:all) (For extremists, this could be written: (@article ? @article.comments : Comment).find(:all)).
  2. You will also have to update app/views/comments/index.rss.builder to deal with @article being nil. For the channel link, I did: xml.link @article ? article_url(@article) : all_comments_url. But what is all_comments_url?
  3. Enter config/routes.rb. Add the following route: map.resources :comments, :name_prefix => 'all_'. Your view now works. And what is more, you can even go directly to http://localhost:3000/comments to view all comments. But in order for this to work, you will have to update app/views/comments/index.html.erb and remove the link to create a new comment.

An issue of trust

We have created a blog with quite a few nice features: Comments, AJAX-support, RSS feeds and formatting. If you have been following along, I hope you have been impressed with the speed with which you can create an application in Ruby-on-Rails.

However, if you’ve been working with development for any time, you have probably seen quite a few examples of technology which starts of promising, but then falls apart when you try to create something that could actually be usable in the real world. So the next time, I plan to do the responsible thing and add security and authentication, so that not just anybody can create and edit articles! In the process, we will get to use sessions and more advanced active record relationships.

About Johannes Brodwall

Johannes is Principal Software Engineer in SopraSteria. In his spare time he likes to coach teams and developers on better coding, collaboration, planning and product understanding.
This entry was posted in Ruby-on-Rails. Bookmark the permalink.

Comments are closed.