Agile Web Development with Rails, Edition 4

6.2 Iteration A2: Making Prettier Listings 2 Instant Gratification

6.1 Iteration A1: Creating the Products Maintenance Application

Ticket 628 : Turbolinks: why is v3.0 not out yet?

This section mostly covers database configuration options for those users that insist on using MySQL. SQLite3 users will skip most of it.

Create the application.

bundle exec /home/rubys/git/rails/railties/exe/rails new depot --skip-bundle
      create  
      create  README.md
      create  Rakefile
      create  config.ru
      create  .gitignore
      create  Gemfile
      create  app
      create  app/assets/config/manifest.js
      create  app/assets/javascripts/application.js
      create  app/assets/javascripts/cable.coffee
      create  app/assets/stylesheets/application.css
      create  app/channels/application_cable/channel.rb
      create  app/channels/application_cable/connection.rb
      create  app/controllers/application_controller.rb
      create  app/helpers/application_helper.rb
      create  app/jobs/application_job.rb
      create  app/mailers/application_mailer.rb
      create  app/models/application_record.rb
      create  app/views/layouts/application.html.erb
      create  app/views/layouts/mailer.html.erb
      create  app/views/layouts/mailer.text.erb
      create  app/assets/images/.keep
      create  app/assets/javascripts/channels
      create  app/assets/javascripts/channels/.keep
      create  app/controllers/concerns/.keep
      create  app/models/concerns/.keep
      create  bin
      create  bin/bundle
      create  bin/rails
      create  bin/rake
      create  bin/setup
      create  bin/update
      create  config
      create  config/routes.rb
      create  config/application.rb
      create  config/environment.rb
      create  config/secrets.yml
      create  config/cable.yml
      create  config/environments
      create  config/environments/development.rb
      create  config/environments/production.rb
      create  config/environments/test.rb
      create  config/initializers
      create  config/initializers/active_record_belongs_to_required_by_default.rb
      create  config/initializers/application_controller_renderer.rb
      create  config/initializers/assets.rb
      create  config/initializers/backtrace_silencers.rb
      create  config/initializers/callback_terminator.rb
      create  config/initializers/cookies_serializer.rb
      create  config/initializers/cors.rb
      create  config/initializers/filter_parameter_logging.rb
      create  config/initializers/inflections.rb
      create  config/initializers/mime_types.rb
      create  config/initializers/per_form_csrf_tokens.rb
      create  config/initializers/request_forgery_protection.rb
      create  config/initializers/session_store.rb
      create  config/initializers/wrap_parameters.rb
      create  config/locales
      create  config/locales/en.yml
      create  config/boot.rb
      create  config/database.yml
      create  db
      create  db/seeds.rb
      create  lib
      create  lib/tasks
      create  lib/tasks/.keep
      create  lib/assets
      create  lib/assets/.keep
      create  log
      create  log/.keep
      create  public
      create  public/404.html
      create  public/422.html
      create  public/500.html
      create  public/favicon.ico
      create  public/robots.txt
      create  test/fixtures
      create  test/fixtures/.keep
      create  test/fixtures/files
      create  test/fixtures/files/.keep
      create  test/controllers
      create  test/controllers/.keep
      create  test/mailers
      create  test/mailers/.keep
      create  test/models
      create  test/models/.keep
      create  test/helpers
      create  test/helpers/.keep
      create  test/integration
      create  test/integration/.keep
      create  test/test_helper.rb
      create  tmp
      create  tmp/.keep
      create  tmp/cache
      create  tmp/cache/assets
      create  vendor/assets/javascripts
      create  vendor/assets/javascripts/.keep
      create  vendor/assets/stylesheets
      create  vendor/assets/stylesheets/.keep
      remove  config/initializers/cors.rb
bundle install --local
Resolving dependencies...
Using rake 10.5.0
Using concurrent-ruby 1.0.0
Using i18n 0.7.0
Using json 1.8.3
Using method_source 0.8.2
Using minitest 5.8.3
Using thread_safe 0.3.5
Using builder 3.2.2
Using erubis 2.7.0
Using mini_portile2 2.0.0
Using coffee-script-source 1.10.0
Using execjs 2.6.0
Using thor 0.19.1
Using eventmachine 1.0.9.1
Using websocket-extensions 0.1.2
Using mime-types 2.99
Using arel 7.0.0
Using bundler 1.11.2
Using byebug 8.2.1
Using debug_inspector 0.0.2
Using hiredis 0.5.2
Using multi_json 1.11.2
Using pg 0.18.4
Using puma 2.15.3
Using qu 0.2.0 from source at `/home/rubys/git/qu-rails`
Using redis 3.2.2
Using sass 3.4.21 from source at `/home/rubys/git/sass`
Using tilt 2.0.2
Using spring 1.6.2
Using sqlite3 1.3.11
Using rack 2.0.0.alpha
Using tzinfo 1.2.2
Using nokogiri 1.6.7.2
Using coffee-script 2.4.1
Using uglifier 2.7.2
Using websocket-driver 0.6.3
Using mail 2.6.3
Using em-hiredis 0.3.0
Using queue_classic 3.2.0.RC1 from source at `/home/rubys/git/queue_classic`
Using rack-test 0.6.3
Using sprockets 3.5.2
Using activesupport 5.0.0.beta1.1 from source at `/home/rubys/git/rails`
Using loofah 2.0.3
Using faye-websocket 0.10.2
Using rails-deprecated_sanitizer 1.0.3
Using globalid 0.3.6
Using activemodel 5.0.0.beta1.1 from source at `/home/rubys/git/rails`
Using jbuilder 2.4.0
Using rails-html-sanitizer 1.0.3
Using rails-dom-testing 1.0.7
Using activejob 5.0.0.beta1.1 from source at `/home/rubys/git/rails`
Using activerecord 5.0.0.beta1.1 from source at `/home/rubys/git/rails`
Using actionview 5.0.0.beta1.1 from source at `/home/rubys/git/rails`
Using actionpack 5.0.0.beta1.1 from source at `/home/rubys/git/rails`
Using railties 5.0.0.beta1.1 from source at `/home/rubys/git/rails`
Using actionmailer 5.0.0.beta1.1 from source at `/home/rubys/git/rails`
Using sprockets-rails 3.0.0
Using coffee-rails 4.1.1 from source at `/home/rubys/git/coffee-rails`
Using jquery-rails 4.1.0
Using qu-rails 0.2.0 from source at `/home/rubys/git/qu-rails`
Using web-console 3.1.0 from source at `/home/rubys/git/web-console`
Using sass-rails 5.0.4
Using actioncable 5.0.0.beta1.1 from source at `/home/rubys/git/rails`
Using turbolinks 2.5.3
Using rails 5.0.0.beta1.1 from source at `/home/rubys/git/rails`
Bundle complete! 18 Gemfile dependencies, 65 gems now installed.
Use `bundle show [gemname]` to see where a bundled gem is installed.

Look at the files created.

ls -p
app/  config/	 db/	  Gemfile.lock	log/	 Rakefile   test/  vendor/
bin/  config.ru  Gemfile  lib/		public/  README.md  tmp/

Generate scaffolding for a real model, modify a template, and do our first bit of data entry.

Generating our first model and associated scaffolding

rails generate scaffold Product title:string description:text image_url:string price:decimal
      invoke  active_record
      create    db/migrate/20160127205330_create_products.rb
      create    app/models/product.rb
      invoke    test_unit
      create      test/models/product_test.rb
      create      test/fixtures/products.yml
      invoke  resource_route
       route    resources :products
      invoke  scaffold_controller
      create    app/controllers/products_controller.rb
      invoke    erb
      create      app/views/products
      create      app/views/products/index.html.erb
      create      app/views/products/edit.html.erb
      create      app/views/products/show.html.erb
      create      app/views/products/new.html.erb
      create      app/views/products/_form.html.erb
      invoke    test_unit
      create      test/controllers/products_controller_test.rb
      invoke    helper
      create      app/helpers/products_helper.rb
      invoke      test_unit
      invoke    jbuilder
      create      app/views/products/index.json.jbuilder
      create      app/views/products/show.json.jbuilder
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/products.coffee
      invoke    scss
      create      app/assets/stylesheets/products.scss
      invoke  scss
      create    app/assets/stylesheets/scaffolds.scss

Break lines for formatting reasons

edit app/controllers/products_controller.rb
class ProductsController < ApplicationController
  before_action :set_product, only: [:show, :edit, :update, :destroy]
 
  # GET /products
  # GET /products.json
  def index
    @products = Product.all
  end
 
  # GET /products/1
  # GET /products/1.json
  def show
  end
 
  # GET /products/new
  def new
    @product = Product.new
  end
 
  # GET /products/1/edit
  def edit
  end
 
  # POST /products
  # POST /products.json
  def create
    @product = Product.new(product_params)
 
    respond_to do |format|
      if @product.save
        format.html { redirect_to @product,
          notice: 'Product was successfully created.' }
        format.json { render :show, status: :created,
          location: @product }
      else
        format.html { render :new }
        format.json { render json: @product.errors,
          status: :unprocessable_entity }
      end
    end
  end
 
  # PATCH/PUT /products/1
  # PATCH/PUT /products/1.json
  def update
    respond_to do |format|
      if @product.update(product_params)
        format.html { redirect_to @product,
          notice: 'Product was successfully updated.' }
        format.json { render :show, status: :ok, location: @product }
      else
        format.html { render :edit }
        format.json { render json: @product.errors,
          status: :unprocessable_entity }
      end
    end
  end
 
  # DELETE /products/1
  # DELETE /products/1.json
  def destroy
    @product.destroy
    respond_to do |format|
      format.html { redirect_to products_url,
          notice: 'Product was successfully destroyed.' }
      format.json { head :no_content }
    end
  end
 
  private
    # Use callbacks to share common setup or constraints between actions.
    def set_product
      @product = Product.find(params[:id])
    end
 
    # Never trust parameters from the scary internet, only allow the white
    # list through.
    def product_params
      params.require(:product).permit(:title, :description, :image_url, :price)
    end
end
edit app/views/products/index.html.erb
<p id="notice"><%= notice %></p>
 
<h1>Products</h1>
 
<table>
  <thead>
    <tr>
      <th>Title</th>
      <th>Description</th>
      <th>Image url</th>
      <th>Price</th>
      <th colspan="3"></th>
    </tr>
  </thead>
 
  <tbody>
    <% @products.each do |product| %>
      <tr>
        <td><%= product.title %></td>
        <td><%= product.description %></td>
        <td><%= product.image_url %></td>
        <td><%= product.price %></td>
        <td><%= link_to 'Show', product %></td>
        <td><%= link_to 'Edit', edit_product_path(product) %></td>
        <td><%= link_to 'Destroy', product,
            method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>
 
<br>
 
<%= link_to 'New Product', new_product_path %>

Add precision and scale to the price

edit db/migrate/20160127205330_create_products.rb
class CreateProducts < ActiveRecord::Migration[5.0]
  def change
    create_table :products do |t|
      t.string :title
      t.text :description
      t.string :image_url
      t.decimal :price, precision: 8, scale: 2
 
      t.timestamps
    end
  end
end

Apply the migration

rails db:migrate
== 20160127205330 CreateProducts: migrating ===================================
-- create_table(:products)
   -> 0.0009s
== 20160127205330 CreateProducts: migrated (0.0009s) ==========================
 

Restart the server.

Get an (empty) list of products

get /products

Products

Title Description Image url Price

New Product

Show (and modify) one of the templates produced

edit app/views/products/_form.html.erb
<%= form_for(product) do |f| %>
  <% if product.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(product.errors.count, "error") %>
      prohibited this product from being saved:</h2>
 
      <ul>
      <% product.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>
 
  <div class="field">
    <%= f.label :title %>
    <%= f.text_field :title %>
  </div>
 
  <div class="field">
    <%= f.label :description %>
    <%= f.text_area :description, rows: 6 %>
  </div>
 
  <div class="field">
    <%= f.label :image_url %>
    <%= f.text_field :image_url %>
  </div>
 
  <div class="field">
    <%= f.label :price %>
    <%= f.text_field :price %>
  </div>
 
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Create a product

get /products/new

New Product

Back
post /products
You are being redirected.
get http://localhost:3000/products/1

Product was successfully created.

Title: CoffeeScript

Description: <p> CoffeeScript is JavaScript done right. It provides all of JavaScript's functionality wrapped in a cleaner, more succinct syntax. In the first book on this exciting new language, CoffeeScript guru Trevor Burnham shows you how to hold onto all the power and flexibility of JavaScript while writing clearer, cleaner, and safer code. </p>

Image url: cs.jpg

Price: 29.0

Edit | Back

Verify that the product has been added

get /products

Products

Title Description Image url Price
CoffeeScript <p> CoffeeScript is JavaScript done right. It provides all of JavaScript's functionality wrapped in a cleaner, more succinct syntax. In the first book on this exciting new language, CoffeeScript guru Trevor Burnham shows you how to hold onto all the power and flexibility of JavaScript while writing clearer, cleaner, and safer code. </p> cs.jpg 29.0 Show Edit Destroy

New Product

And, just to verify that we haven't broken anything

rails test
DEPRECATION WARNING: alias_method_chain is deprecated. Please, use Module#prepend instead. From module, you can access the original method using super. (called from included at /home/rubys/.rvm/gems/ruby-2.3.0/gems/turbolinks-2.5.3/lib/turbolinks/xhr_url_for.rb:7)
DEPRECATION WARNING: alias_method_chain is deprecated. Please, use Module#prepend instead. From module, you can access the original method using super. (called from included at /home/rubys/.rvm/gems/ruby-2.3.0/gems/turbolinks-2.5.3/lib/turbolinks/xhr_url_for.rb:7)
Run options: --seed 52180
 
# Running:
 
DEPRECATION WARNING: before_filter is deprecated and will be removed in Rails 5.1. Use before_action instead. (called from block (3 levels) in <class:Engine> at /home/rubys/.rvm/gems/ruby-2.3.0/gems/turbolinks-2.5.3/lib/turbolinks.rb:14)
DEPRECATION WARNING: after_filter is deprecated and will be removed in Rails 5.1. Use after_action instead. (called from block (3 levels) in <class:Engine> at /home/rubys/.rvm/gems/ruby-2.3.0/gems/turbolinks-2.5.3/lib/turbolinks.rb:15)
.......
 
Finished in 0.239610s, 29.2141 runs/s, 50.0813 assertions/s.
 
7 runs, 12 assertions, 0 failures, 0 errors, 0 skips

6.2 Iteration A2: Making Prettier Listings 2 Instant Gratification