- Read Tutorial
- Watch Guide Video
In this guide we are going to render records on our Posts
page. And as usual, we'll start by building a test case. Open the features/post_spec.rb
file and add another test to our index
block.
# spec/features/post_spec.rb it 'has a list of posts' do post1 = Post.create(date: Date.today, rationale: "Post1") post2 = Post.create(date: Date.today, rationale: "Post2") visit posts_path expect(page).to have_content(/Post1|Post2/) end
In this code, we are checking to see if there are a list of posts. We are creating two posts, visiting the path and then we expect the page to have content from both the post variables. If you're thinking there's a lot of duplicate code here, you're right. We'll come back later and refactor the code. Traditionally you don't want to refactor code right away since it's hard to detect a failure. For example, if you perform a refactor, then create a test and it fails, we would have to waste time figuring out if the problem was from our implementation or the refactor we performed.
If you run rspec
you'll see that we have two failures.
One failure is related to the the post
logic. The second failure is because a user is not logged in. Let's add in a call to our login
test helper method to mimic a user login.
# spec/features/post_spec.rb describe 'navigate' do before do user = User.create(email: "test@test.com", password: "asdfasdf", password_confirmation: "asdfasdf", first_name: "Jon", last_name: "Snow") login_as(user, :scope => :user) end # ... rest of the file not shown for brevity sake
If you run rspec
, it will now only throw a single failure. To build out the initial feature, open the posts_controller.rb
file. In the index method, add this line of code:
# app/controllers/posts_controller.rb def index @posts = Post.all end
This code will run a database query and gather all the available records into the variable @posts
. As a result, the records will be available to the index
action and associated template. Next, open the index.html.erb
page and update the code to look like this:
<!-- app/views/posts/index.html.erb --> <%= @posts.inspect %>
If you run this, all of our tests will be green. Now, start the rails server and refresh the browser. You'll see all the records in their raw form (if you've never used inspect
before it prints out the raw object value and all of its metadata).
With that it working, let's get start refactoring the code.
First, let's eliminate the duplicate code in the post_spec.rb
test and place it in a before
block.
# spec/features/post_spec.rb describe 'index' do before do visit posts_path end it 'can be reached successfully' do expect(page.status_code).to eq(200) end it 'has a title of Posts' do expect(page).to have_content(/Posts/) end it 'has a list of posts' do post1 = Post.create(date: Date.today, rationale: "Post1") post2 = Post.create(date: Date.today, rationale: "Post2") visit posts_path expect(page).to have_content(/Post1|Post2/) end end
If you notice, we are not moving our call to visit posts_path
in "has a list of posts"
because we need to visit the page AFTER the posts are created. If we visit the page before the posts are created it won't find any posts.
Next, let's work on our index.html.erb
file. Right now we're printing out the raw object values to the screen and they're all running together. Let's polish this up a bit and have the records shown on their own lines. We'll iterate over each post and display the values like this:
<!-- overtime-app/app/views/posts/index.html.erb --> <h1>Posts</h1> <% @posts.each do |post| %> <%= post.inspect %> <hr> <% end %>
Run rspec and everything should work. Open the browser, and you'll see:
Now, each post is on a different line. However, we need to put it in a table form because that's what the client requested for the page.
Let's implement a Bootstrap table to clean this UI up. In order to make the table dynamic, inside the <tbody>
tag, we'll iterate over each post and display the three fields, namely: date
, rationale
and the user_id
. For user_id
, we are only going to display the last name for now. We'll later add the functionality to display the full name.
<!-- overtime-app/app/views/posts/index.html.erb --> <h1>Posts</h1> <table class="table table-striped table-hover"> <thead> <tr> <th> # </th> <th> Date </th> <th> User </th> <th> Rationale </th> </tr> </thead> <tbody> <% @posts.each do |post| %> <tr> <td> <%= post.id %> </td> <td> <%= post.date %> </td> <td> <%= post.user.last_name %> </td> <td> <%= post.rationale %> </td> </tr> <% end %> </tbody> </table>
Now, if we test this out it will fail because our seed data doesn't have users. So, go to seeds.rb
and add the user field.
# db/seeds.rb @user = User.create(email: "test@test.com", password: "asdfasdf", password_confirmation: "asdfasdf", first_name: "Jon", last_name: "Snow") puts "1 User created" 100.times do |post| Post.create!(date: Date.today, rationale: "#{post} rationale content", user_id: @user.id) end puts "100 Posts have been created"
Next, run bundle exec rake db:setup
to clean up our sample data and start the rails server. Hit refresh on your browser and this is how it will look:
This looks fine, but there's one more thing we need to do. If we go to the console real quick and update the rationale of the first record with a long name,
You'll see that the entire output on the browser gets skewed because the rationale
is too long.
To fix this, we are going to use a method called truncate
. Go to index.html.erb
and change the rationale
code, like this:
<!-- overtime-app/app/views/posts/index.html.erb --> <%= truncate(post.rationale) %>
That's it. We're good to go with this core functionality.