Agile Web Development with Rails, Edition 4

Table of Contents 12.2 Iteration G2: Atom Feeds

12.3 Iteration G3: Downloading an eBook

demonstrate streaming with ActionController::Live

Switch to puma as a server

edit Gemfile
gem 'puma'

Restart the server.

add a route for downloading a product

edit config/routes.rb
Depot::Application.routes.draw do
  resources :orders
 
  resources :line_items
 
  resources :carts
 
  get "store/index"
  resources :products do
    get :download, :on => :member
    get :who_bought, on: :member
  end
 
  # The priority is based upon order of creation:
  # first created -> highest priority.
  # See how all your routes lay out with "rake routes".
 
  # You can have the root of your site routed with "root"
  root 'store#index', as: 'store'
 
  # Example of regular route:
  #   get 'products/:id' => 'catalog#view'
 
  # Example of named route that can be invoked with purchase_url(id: product.id)
  #   get 'products/:id/purchase' => 'catalog#purchase', as: :purchase
 
  # Example resource route (maps HTTP verbs to controller actions automatically):
  #   resources :products
 
  # Example resource route with options:
  #   resources :products do
  #     member do
  #       get 'short'
  #       post 'toggle'
  #     end
  #
  #     collection do
  #       get 'sold'
  #     end
  #   end
 
  # Example resource route with sub-resources:
  #   resources :products do
  #     resources :comments, :sales
  #     resource :seller
  #   end
 
  # Example resource route with more complex sub-resources:
  #   resources :products do
  #     resources :comments
  #     resources :sales do
  #       get 'recent', on: :collection
  #     end
  #   end
 
  # Example resource route with concerns:
  #   concern :toggleable do
  #     post 'toggle'
  #   end
  #   resources :posts, concerns: :toggleable
  #   resources :photos, concerns: :toggleable
 
  # Example resource route within a namespace:
  #   namespace :admin do
  #     # Directs /admin/products/* to Admin::ProductsController
  #     # (app/controllers/admin/products_controller.rb)
  #     resources :products
  #   end
end

mock streaming implementation for download

edit app/controllers/products_controller.rb
  include ActionController::Live
  def download
    response.headers['Content-Type'] = 'text/plain'
    40.times do |i|
      response.stream.write "Line #{i}\n\n"
      sleep 0.10
    end
    response.stream.write "Fini.\n"
  ensure
    response.stream.close
  end

add order to the session

edit app/controllers/orders_controller.rb
  def create
    @order = Order.new(order_params)
    @order.add_line_items_from_cart(@cart)
 
    respond_to do |format|
      if @order.save
        Cart.destroy(session[:cart_id])
        session[:cart_id] = nil
        session[:order_id] = @order.id
        format.html { redirect_to store_url, notice: 
          'Thank you for your order.' }
        format.json { render action: 'show', status: :created,
          location: @order }
      else
        format.html { render action: 'new' }
        format.json { render json: @order.errors,
          status: :unprocessable_entity }
      end
    end
  end

render order in the side bar

edit app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
  <title>Pragprog Books Online Store</title>
  <%= stylesheet_link_tag    "application", media: "all",
    "data-turbolinks-track" => true %>
  <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
  <%= csrf_meta_tags %>
</head>
<body class="<%= controller.controller_name %>">
  <div id="banner">
    <%= image_tag("logo.png") %>
    <span class="title"><%= @page_title || "Pragmatic Bookshelf" %></span>
  </div>
  <div id="columns">
    <div id="side">
      <% if @cart %>
        <%= hidden_div_if(@cart.line_items.empty?, id: 'cart') do %>
          <%= render @cart %>
        <% end %>
      <% end %>
 
      <%= render Order.find(session[:order_id]) if session[:order_id] -%>
 
      <ul>
        <li><a href="http://www....">Home</a></li>
        <li><a href="http://www..../faq">Questions</a></li>
        <li><a href="http://www..../news">News</a></li>
        <li><a href="http://www..../contact">Contact</a></li>
      </ul>
    </div>
    <div id="main">
      <%= yield %>
    </div>
  </div>
</body>
</html>

implement order partial

edit app/views/orders/_order.html.erb
<div id="order">
<h2>Your Downloads</h2>
<table data-no-turbolink>
  <% order.line_items.each do |item| %>
  <tr>
    <td><%= link_to item.product.title, download_product_path(item.product) %></td>
  </tr>
  <% end %>
</table>
</div>

css tweaks

edit app/assets/stylesheets/application.css.scss
  #side {
    padding: 1em 2em;
    background: #141;
 
    form, div {
      display: inline;
    }  
 
    input {
      font-size: small;
    }
 
    #cart, #order {
      font-size: smaller;
      color:     white;
 
      a, a:hover {
        color: white;
        background-color: #141;
      }
 
      table {
        border-top:    1px dotted #595;
        border-bottom: 1px dotted #595;
        margin-bottom: 10px;
      }
    }
 
    ul {
      padding: 0;
 
      li {
        list-style: none;
 
        a {
          color: #bfb;
          font-size: small;
        }
      }
    }
  }

place an order

get /
#<Errno::ECONNREFUSED: Connection refused - connect(2)>
  /home/rubys/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/net/http.rb:763:in `initialize'
  /home/rubys/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/net/http.rb:763:in `open'
  /home/rubys/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/net/http.rb:763:in `block in connect'
  /home/rubys/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/timeout.rb:55:in `timeout'
  /home/rubys/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/timeout.rb:100:in `timeout'
  /home/rubys/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/net/http.rb:763:in `connect'
  /home/rubys/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/net/http.rb:756:in `do_start'
  /home/rubys/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/net/http.rb:745:in `start'
  /home/rubys/.rvm/rubies/ruby-1.9.3-p551/lib/ruby/1.9.1/net/http.rb:557:in `start'
  /home/rubys/git/gorp/lib/gorp/net.rb:132:in `post'
  makedepot.rb:2568:in `block in <main>'
  /home/rubys/git/gorp/lib/gorp/output.rb:59:in `call'
  /home/rubys/git/gorp/lib/gorp/output.rb:59:in `block (4 levels) in <top (required)>'
  /home/rubys/git/gorp/lib/gorp/output.rb:49:in `each'
  /home/rubys/git/gorp/lib/gorp/output.rb:49:in `block (3 levels) in <top (required)>'
  /home/rubys/.rvm/gems/ruby-1.9.3-p551/gems/builder-3.1.4/lib/builder/xmlbase.rb:170:in `call'
  /home/rubys/.rvm/gems/ruby-1.9.3-p551/gems/builder-3.1.4/lib/builder/xmlbase.rb:170:in `_nested_structures'
  /home/rubys/.rvm/gems/ruby-1.9.3-p551/gems/builder-3.1.4/lib/builder/xmlbase.rb:63:in `tag!'
  /home/rubys/.rvm/gems/ruby-1.9.3-p551/gems/builder-3.1.4/lib/builder/xmlbase.rb:88:in `method_missing'
  /home/rubys/git/gorp/lib/gorp/output.rb:22:in `block (2 levels) in <top (required)>'
  /home/rubys/.rvm/gems/ruby-1.9.3-p551/gems/builder-3.1.4/lib/builder/xmlbase.rb:170:in `call'
  /home/rubys/.rvm/gems/ruby-1.9.3-p551/gems/builder-3.1.4/lib/builder/xmlbase.rb:170:in `_nested_structures'
  /home/rubys/.rvm/gems/ruby-1.9.3-p551/gems/builder-3.1.4/lib/builder/xmlbase.rb:63:in `tag!'
  /home/rubys/.rvm/gems/ruby-1.9.3-p551/gems/builder-3.1.4/lib/builder/xmlbase.rb:88:in `method_missing'
  /home/rubys/git/gorp/lib/gorp/output.rb:11:in `block in <top (required)>'
    

Table of Contents 12.2 Iteration G2: Atom Feeds