In the previous part of our Building a Ruby on Rails app series we completed the back end of our application, by fetching the book sales from Amazon.com. In this post we will look at the front end: showing the book list on the page together with their price movement.

The Controller

In the the first part of the series we created the Book model, that holds the book details Amazon supplied to us. Now we create the controller:

sh
bundle exec rails g controller books index --skip-javascripts

This command generated the BooksController with only one view index.html.erb. We only need to list the books, so the index view is sufficient. We also skip creating any JavaScript files that Rails would otherwise generate for us by default. Rails has generated a stylesheet file, books.scss, which we'll use later to style the page.

Let's now update the routes:

ruby
# config/routes.rb

resources :books, :only => :index

It's important that we limit the routes generated by resources method, so they match our real views.

Our controller will show a list of books and also a date of when the book sales when were last updated:

ruby
# app/controllers/books_controller.rb

class BooksController < ::ApplicationController
  def index
    @books = Book.order(:last_sales_rank)
    @rank_updated = BookSale.last_updated
  end
end

The last_updated method could look like this:

ruby
# app/models/book_sales.rb

class BookSale < ActiveRecord::Base
  belongs_to :book

  def self.last_updated
    last.created_at.strftime('%d %B %Y')
  end
end

The View

The view needs to have a list and a placeholder for the updated date:

html
<div class="app-content-main">
  <ul class="books">
    <% @books.each do |book| %>

    <li class="book">
      <div class="sale col">
        <div class="cf">
          <div class="rank">
            <%= book.last_sales_rank %>
            <span class="pr"><%= book.previous_sales_rank %></span>
          </div>
          <%= movement_class book %>
        </div>
      </div>

      <div class="img col">
        <%= image_tag book.image_url, alt: book.title %>
      </div>

      <div class="col det">
        <h3><%= book.title.html_safe %></h3>
        <p class="details">
          <span><%= book.author %></span>
          <span><%= book.published_at.strftime('%d %B %Y') %></span>
        </p>
      </div>
    </li>

    <% end %>
  </ul>

  <div class="updated">
    Last updated <%= @rank_updated %>
  </div>
</div>

We navigate through books and show their image, title, author, and publication date. We also show their previous and current sales ranks, and an arrow, whose direction indicates the sales movement:

ruby
# app/helpers/books_helper.rb

module BooksHelper
  def movement_class(book)
    class_name = 'none'
    title = 'No movement'
    movement = book.sales_rank_movement || 0
    if movement > 0
      class_name = 'down'
      title = 'Sales rank is going down'
    end
    if movement < 0
      class_name = 'up'
      title = 'Sales rank is going up'
    end
    class_name = "move move-#{class_name}"
    content_tag :i, '', class: class_name, title: title
  end
end

This method is simple, it looks at the sales_rank_movement we added to the book in the previous part, and generates the corresponding markup.

Let's now style the arrow and other parts of the page.

The CSS

We add the styles to the books.scss file that Rails generated for us:

css
.app-content-main {
  width: 650px;
  background: #fff;
  padding: 1em 0;
  border-radius: 3px;
  float: left;
}

.books {
  .book {
    position: relative;
    padding-left: 90px;
    min-height: 90px;
    display: table-row;
  }

  .col {
    display: table-cell;
    padding: 1.5em .5em;
    vertical-align: top;
    border-bottom: 1px solid #f2f5f8;

    &.sale {
      vertical-align: middle;
      position: relative;
      padding-right: 1em;
      padding-left: 1.5em;
      text-align: right;
    }

    &.det {
      padding-right: 2.5em;
    }
  }

  .book:last-child .col {
    border-bottom: none;
  }

  .img {
    width: 70px;
    img {
      max-width: 100%;
    }
  }

  .rank {
    display: block;
    color: #999;
    margin-right: 1em;
    line-height: 1.05;
    text-align: right;

    .pr {
      color: #bbb;
      font-size: .9em;
      display: block;
    }
  }

  .move {
    position: absolute;
    right: .5em;
    top: 50%;
    margin-top: -6px;

    &.move-none {
      width: 15px;
      height: 3px;
      background: #ECC70D;
    }

    &.move-down {
      border-top: 8px solid #f99;
      border-left: 6px solid transparent;
      border-right: 6px solid transparent;
    }

    &.move-up {
      border-bottom: 8px solid #98DA8E;
      border-left: 6px solid transparent;
      border-right: 6px solid transparent;
    }
  }

  h3 {
    font-size: 1.15em;
    margin-bottom: .3em;
    line-height: 1.1;
    color: #333;
  }

  .details {
    font-size: .9em;
    color: #999;

    span {
      display: inline-block;
      margin-right: 1em;
    }
  }
}

The arrows are created by using borders as explained in our caret generator.

Conclusion

In this part of Building a Ruby on Rails app series, we showed the list of books with their details and visual cues of their sales rank movement. At the bottom of the page we showed the date, when the sales ranks were last fetched from Amazon.com.

This complete our series. You can check out the previous posts in this series below: