Rails 3 – Building a Blog – Part 1: Test Setup & Generators

by woody2shoes on June 22, 2010


Every good project needs a good setup. In this episode, I set up a github repo, create a new rails application, hook in Cucumber and Rspec, write a Cucumber feature, and write the code to make it pass.
Download 142 MB
Download (iphone & ipod) 59 MB

  • Robert

    Interesting. I have a rather basic question: why do you use the post[:title] = title style, instead of post.title = title?

    • http://teachmetocode.com Charles Max Wood

      To be honest, that’s the convention at work. It doesn’t make that much difference.

      post[:title] = title is the same as post.write_attribute(:title, title) which commits the change to the database.

      post.title = title doesn’t post the change to the database. You can reduce round trips by doing it this way if you’re assigning more than one attribute.

      • Frappez_2000

        Hi. I just found this website. Thank you so much for doing this. About post[:title], so if I use this, does that mean that
        @post.save is not necessary? When I open up Rails console, it seems that I still have to do @post.save .

        Googling write_attribute, it seems like the main purpose of this is to use it in model definitions to overwrite default accessors.

        • http://teachmetocode.com Charles Max Wood

          Ok, I’ll admit being wrong. I did some research and write_attribute does NOT commit to the database, which means that @ar_object[:title] = “some title” doesn’t commit to the database either.

          In other words the @post.save is necessary.

          @post[:title] doesn’t make as much difference as @post[:id] where every object has an identifier in Ruby. This clearly reads the attribute from the @post’s attributes, rather than from the object’s own identifiers in Ruby.

  • Robert

    Interesting. I have a rather basic question: why do you use the post[:title] = title style, instead of post.title = title?

  • woody2shoes

    To be honest, that's the convention at work. It doesn't make that much difference.

    post[:title] = title is the same as post.write_attribute(:title, title) which commits the change to the database.

    post.title = title doesn't post the change to the database. You can reduce round trips by doing it this way if you're assigning more than one attribute.

  • Jeremy1217

    Get tutorial, but I’m getting this error
    “Can’t find mapping from “the post creation page” to a path.
    Now, go and add a mapping in C:/rails/rails_apps/Rpress/features/support/paths.r”

    Can anyone help?

    • Jeremy1217

      never mind- I just added this to the path.rb

      when /the post creation page/
      ‘/posts/create’

      • http://teachmetocode.com Charles Max Wood

        You can definitely do that. You can also create a step in which you run this:

        visit ‘/posts/create’

  • Jeremy1217

    Get tutorial, but I'm getting this error
    “Can't find mapping from “the post creation page” to a path.
    Now, go and add a mapping in C:/rails/rails_apps/Rpress/features/support/paths.r”

    Can anyone help?

  • Jeremy1217

    never mind- I just added this to the path.rb

    when /the post creation page/
    '/posts/create'

  • Stephen Orr

    I’ve run into 2 issues with this, both related to the format passed to strftime. You won’t have seen them because of how your date was specified. You used May 22, I used today – July 9.

    The original format you used was %b %d, %Y. That’s fine for May 22, as it comes out May 22, 2010 as expected. However, my July 9 came out as Jul 09, 2010, causing the test to fail.

    I tried using %b %e, %Y – that gave me Jul 9, 2010 – there’s 2 spaces there, as Ruby pads a single digit day with a space. There doesn’t appear to be an option to not pad it, so my solution was this:

    %B %d, %Y – and make sure in the test that I’m looking for July 09, 2010.

    Just caught me out and I thought I should mention it!

  • Stephen Orr

    I've run into 2 issues with this, both related to the format passed to strftime. You won't have seen them because of how your date was specified. You used May 22, I used today – July 9.

    The original format you used was %b %d, %Y. That's fine for May 22, as it comes out May 22, 2010 as expected. However, my July 9 came out as Jul 09, 2010, causing the test to fail.

    I tried using %b %e, %Y – that gave me Jul 9, 2010 – there's 2 spaces there, as Ruby pads a single digit day with a space. There doesn't appear to be an option to not pad it, so my solution was this:

    %B %d, %Y – and make sure in the test that I'm looking for July 09, 2010.

    Just caught me out and I thought I should mention it!

  • Frappez_2000

    Here are some notes from the screencast:

    # 7/27/2010
    # Notes for TMTC: Rails3-Building a Blog-Part1
    # The following contains some modifications from the screencast
    # Haml is used in these notes instead of Erb

    # start episode

    # Rails 3 app already created (‘rpress’)

    # Setting up Github
    # Create new repository (follow instruction on Github)
    # Terminal
    git init
    git remote add origin (…)
    git add .
    git commit -m ‘initial commit’
    git push origin master

    #GemFile
    # if using MySQL
    gem ‘mysql’

    # if using Haml
    gem ‘haml’ # (not in screencast)

    # group :development, :test do # avoid these gems in production (not in screencast)
    gem ‘capybara’
    gem ‘database_cleaner’
    gem ‘cucumber-rails’
    gem ‘cucumber’
    gem “rspec-rails”, “>= 2.0.0.beta.17″ # gem ‘rspec’ not needed
    gem ‘spork’
    gem ‘launchy’ # So you can do Then show me the page
    # end

    # Terminal
    # gem package is deprecated
    bundle install

    # if getting error installing mysql see
    # http://www.andhapp.com/blog/2010/02/26/install-mysql-pg-gems-with-bundler-09x/
    # basically type into Terminal ‘ export PATH=/usr/local/mysql/bin:${PATH} ‘

    rails g cucumber:install –rspec –capybara

    # rails g model post
    # rails destroy model post

    # database.yml (for MySQL)
    development:
    adapter: mysql
    database: rpress
    username: root
    pool: 5
    timeout: 5000
    # repeat for test and production except database name must be different
    # ie. rpress_test

    #Terminal
    rake db:create

    # ok to use ‘rake db:migrate’ instead?

    # development.rb
    # setup global variable after the final end
    APP_URL = “http://localhost:3000/”

    # create new file
    # features/front_page.features
    Feature: Front Page
    In order to see the most relevant posts
    As a reader
    I want the 5 most recent posts on the front page

    Scenario: 1 post in blog
    Given I have a post
    And my post has title “My Post”
    And my post has body “This is my post. Back off!!!”
    And my post has author with name “Charles Max Wood”
    And my post was published on “May 22, 2010″
    When I go to the homepage
    Then I should see “My Post”
    And I should see “This is my post. Back off!!!”
    And I should see “Charles Max Wood”
    And I should see “May 22, 2010″

    # Terminal
    rake db:migrate
    rake cucumber

    # rake features deprecated

    # Copy the terminal output snippets and paste into
    # features/step_definitions/front_page_steps.rb
    # modify section:
    Given /^I have a post$/ do
    @post = Post.create
    end

    # Terminal
    rake features
    rails g model post title:string body:text author_id:integer published_on:date
    rails g model user name:string
    rake db:migrate
    rake cucumber

    # features/step_definitions/front_page_steps.rb
    Given /^my post has title “([^"]*)”$/ do |title|
    @post.title = title # ‘ @post[:title] = ‘ is used in the screencast
    @post.save
    end

    Given /^my post has body “([^"]*)”$/ do |body|
    @post.body = body # ‘ @post[:body] = ‘ is used in the screencast
    @post.save
    end

    Given /^my post has author with name “([^"]*)”$/ do |name|
    @post.author = User.create(:name => name) # ‘ @post[:author] = ‘ is used in the screencast
    @post.save
    end

    # if using ‘@post.author =’ instead of ‘@post[:author] =’,
    # you must jump ahead to create the author association in
    # post.rb
    # belongs_to :author, :class_name => “User”

    Given /^my post was published on “([^"]*)”$/ do |date|
    @post.published_on = Date.parse(date) # ‘ @post[:published_on] = ‘ is used in the screencast
    @post.save
    end

    # Terminal
    rake cucumber
    rails g controller posts index

    # routes.rb
    root :to => “posts#index”

    # delete public/index.html

    # posts_controller.rb
    def index
    @posts = Post.all
    end

    # views/posts/_post.html.haml
    %h2= post.title

    # views/posts/index.html.haml
    %h1 Posts
    = render :partial => “post”, :collection => @posts

    # troubleshooting
    # features/front_page_features
    # after ‘When I go to the homepage’
    Then show me the page

    # Terminal
    rake cucumber

    # features/front_page_features
    # remove ‘Then show me the page’
    # remove h2 tag from line
    Then I should see “My Post”

    # alternatively use ‘ Then I should see “My Post” within “h2″ ‘
    # to test the h2 tag

    # _post.html.haml
    %p.post_body= post.body
    %div
    %span.author
    by
    = post.author.name

    # The following step was done above out of order from the screencast
    # post.rb
    # belongs_to :author, :class_name => “User”

    # front_page_stebs.rb
    # change line to
    @post[:author_id] = User.create(:name => name)

    # the above line is not needed if using ‘@post.author =’

    #_post.html.haml
    %div
    %span.author
    by
    = post.author.name
    %span.published_on
    on
    = post.published_on.strftime(“%b %d, %Y”)

    # Terminal
    rake cucumber

    # all tests pass
    # end episode

  • Frappez_2000

    Hi. I just found this website. Thank you so much for doing this. About post[:title], so if I use this, does that mean that
    @post.save is not necessary? When I open up Rails console, it seems that I still have to do @post.save .

    Googling write_attribute, it seems like the main purpose of this is to use it in model definitions to overwrite default accessors.

  • Frappez_2000

    Here are some notes from the screencast:

    # 7/27/2010
    # Notes for TMTC: Rails3-Building a Blog-Part1
    # The following contains some modifications from the screencast
    # Haml is used in these notes instead of Erb

    # start episode

    # Rails 3 app already created ('rpress')

    # Setting up Github
    # Create new repository (follow instruction on Github)
    # Terminal
    git init
    git remote add origin (…)
    git add .
    git commit -m 'initial commit'
    git push origin master

    #GemFile
    # if using MySQL
    gem 'mysql'

    # if using Haml
    gem 'haml' # (not in screencast)

    # group :development, :test do # avoid these gems in production (not in screencast)
    gem 'capybara'
    gem 'database_cleaner'
    gem 'cucumber-rails'
    gem 'cucumber'
    gem “rspec-rails”, “>= 2.0.0.beta.17″ # gem 'rspec' not needed
    gem 'spork'
    gem 'launchy' # So you can do Then show me the page
    # end

    # Terminal
    # gem package is deprecated
    bundle install

    # if getting error installing mysql see
    # http://www.andhapp.com/blog/2010/02/26/install-…
    # basically type into Terminal ' export PATH=/usr/local/mysql/bin:${PATH} '

    rails g cucumber:install –rspec –capybara

    # rails g model post
    # rails destroy model post

    # database.yml (for MySQL)
    development:
    adapter: mysql
    database: rpress
    username: root
    pool: 5
    timeout: 5000
    # repeat for test and production except database name must be different
    # ie. rpress_test

    #Terminal
    rake db:create

    # ok to use 'rake db:migrate' instead?

    # development.rb
    # setup global variable after the final end
    APP_URL = “http://localhost:3000/”

    # create new file
    # features/front_page.features
    Feature: Front Page
    In order to see the most relevant posts
    As a reader
    I want the 5 most recent posts on the front page

    Scenario: 1 post in blog
    Given I have a post
    And my post has title “My Post”
    And my post has body “This is my post. Back off!!!”
    And my post has author with name “Charles Max Wood”
    And my post was published on “May 22, 2010″
    When I go to the homepage
    Then I should see “

    My Post


    And I should see “This is my post. Back off!!!”
    And I should see “Charles Max Wood”
    And I should see “May 22, 2010″

    # Terminal
    rake db:migrate
    rake cucumber

    # rake features deprecated

    # Copy the terminal output snippets and paste into
    # features/step_definitions/front_page_steps.rb
    # modify section:
    Given /^I have a post$/ do
    @post = Post.create
    end

    # Terminal
    rake features
    rails g model post title:string body:text author_id:integer published_on:date
    rails g model user name:string
    rake db:migrate
    rake cucumber

    # features/step_definitions/front_page_steps.rb
    Given /^my post has title “([^"]*)”$/ do |title|
    @post.title = title # ' @post[:title] = ' is used in the screencast
    @post.save
    end

    Given /^my post has body “([^"]*)”$/ do |body|
    @post.body = body # ' @post[:body] = ' is used in the screencast
    @post.save
    end

    Given /^my post has author with name “([^"]*)”$/ do |name|
    @post.author = User.create(:name => name) # ' @post[:author] = ' is used in the screencast
    @post.save
    end

    # if using '@post.author =' instead of '@post[:author] =',
    # you must jump ahead to create the author association in
    # post.rb
    # belongs_to :author, :class_name => “User”

    Given /^my post was published on “([^"]*)”$/ do |date|
    @post.published_on = Date.parse(date) # ' @post[:published_on] = ' is used in the screencast
    @post.save
    end

    # Terminal
    rake cucumber
    rails g controller posts index

    # routes.rb
    root :to => “posts#index”

    # delete public/index.html

    # posts_controller.rb
    def index
    @posts = Post.all
    end

    # views/posts/_post.html.haml
    %h2= post.title

    # views/posts/index.html.haml
    %h1 Posts
    = render :partial => “post”, :collection => @posts

    # troubleshooting
    # features/front_page_features
    # after 'When I go to the homepage'
    Then show me the page

    # Terminal
    rake cucumber

    # features/front_page_features
    # remove 'Then show me the page'
    # remove h2 tag from line
    Then I should see “My Post”

    # alternatively use ' Then I should see “My Post” within “h2″ '
    # to test the h2 tag

    # _post.html.haml
    %p.post_body= post.body
    %div
    %span.author
    by
    = post.author.name

    # The following step was done above out of order from the screencast
    # post.rb
    # belongs_to :author, :class_name => “User”

    # front_page_stebs.rb
    # change line to
    @post[:author_id] = User.create(:name => name)

    # the above line is not needed if using '@post.author ='

    #_post.html.haml
    %div
    %span.author
    by
    = post.author.name
    %span.published_on
    on
    = post.published_on.strftime(“%b %d, %Y”)

    # Terminal
    rake cucumber

    # all tests pass
    # end episode

  • http://coachradio.tv/ Justin Lukasavige

    Wow, even I can understand this Chuck. Great tutorial!

    • http://teachmetocode.com Charles Max Wood

      Thanks Justin. The idea behind these tutorials is to make it simple enough for people of varying technical backgrounds to get started.If someone doesn’t understand something, I hope they’ll ask.

  • http://coachradio.tv/ Justin Lukasavige

    Wow, even I can understand this Chuck. Great tutorial!

    • admin

      Thanks Justin. The idea behind these tutorials is to make it simple enough for people of varying technical backgrounds to get started.

      If someone doesn’t understand something, I hope they’ll ask.

  • Anonymous

    Thanks Justin. The idea behind these tutorials is to make it simple enough for people of varying technical backgrounds to get started.

    If someone doesn’t understand something, I hope they’ll ask.

  • Anonymous

    You can definitely do that. You can also create a step in which you run this:

    visit ‘/posts/create’

  • Anonymous

    Thanks for the heads up. %b does abbreviate the month.

    For the full documentation on formatting dates as strings, go here: http://ruby-doc.org/core-1.9/classes/Time.html#M000314

  • Anonymous

    Ok, I’ll admit being wrong. I did some research and write_attribute does NOT commit to the database, which means that @ar_object[:title] = “some title” doesn’t commit to the database either.

    In other words the @post.save is necessary.

    @post[:title] doesn’t make as much difference as @post[:id] where every object has an identifier in Ruby. This clearly reads the attribute from the @post’s attributes, rather than from the object’s own identifiers in Ruby.

  • http://twitter.com/akashmanohar Akash Manohar

    Someone around 2:00 Charles mentions that the bundler stuff is already in the .gitignore

    IMO the .bundle in the gitignore isn’t for bundler. It’s for the git-bundles

  • http://twitter.com/akashmanohar Akash Manohar

    Someone around 2:00 Charles mentions that the bundler stuff is already in the .gitignore

    IMO the .bundle in the gitignore isn’t for bundler. It’s for the git-bundles

Previous post:

Next post: