For a demo to some colleagues, I created a Ruby on Rails demonstration. This document shows a scenario to build a very simple webshop. See http://changelos.com/2010/06/08/demoing-ruby-on-rails/ for the full story.
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Demoing rails
1. Demoing Rails
Stuff in keynote in black on white
Stuff to show in white on black
Ruby recap
Keynote: Short Ruby recap
My first app
Create an empty Ruby application
Start it, and show in a browser (localhost:3000)
Keynote: Our case: a webshop
Generate the Articles controller
Create & migrate the database
Create some article by going to localhost:3000/articles
Keynote: What has rails built for us? (M/V/C) (plus: config, db, public, scripts, test)
Keynote: Rails & Rest
p.1
2. Layouts
Keynote: Layouts
Build the default layout based on the existing layout, set it as default in application.rb, and
add some stylings
app/views/layouts/default.html.erb
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<title>Articles: <%= controller.action_name %></title>
<%= stylesheet_link_tag 'scaffold', 'webshop' %>
</head>
<body>
<div id="header">
<h1>Welcome to our webshop!</h1>
</div>
<div id="menubar">
<h3>Shopping cart</h3>
</div>
<p style="color: green"><%= flash[:notice] %></p>
<div id="body">
<%= yield %>
</div>
</body>
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
layout 'default'
p.2
3. public/stylesheets/webshop.css
#header {
background-color: green;
padding: 10px;
}
#menubar {
float: left;
background-color: green;
width: 200px;
}
#body {
float: left;
padding: 10px;
}
Simple security
Keynote: Filters
Create authentication filter for the articles controller, except ʻshowʼ
app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
before_filter :authenticate, :except => 'show'
...
private
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == 'admin' && password == 'secret'
@admin = true
end
end
end
Show that we now need to log in; log in in one browser, and donʼt in another
Also, show that there is now a ʻfilter chain haltedʼ in the server log
Remove the ʻeditʼ link form the show.html.
app/views/articles/show.html.erb
delete: <%= link_to 'Edit', edit_article_path(@article) %> |
p.3
4. The shop controller
Keynote: Shop controller
Generate the shop controller
Put some basic index in the shop controller , noting that we want to show the
app/controllers/shop_controller.rb
class ShopController < ApplicationController
def index
@articles = Article.all
end
end
Create some contents for the shop/index
app/views/shop/index.html.erb
<marquee><b>Our thingies are priced to move!</b></marquee>
<%= render :partial => @articles %>
Keynote: Partials (2 slides)
Build the _article partial (point out that div_for takes a code block, and we will see this
pattern more often.
app/views/articles/_article.html.erb
<% div_for article do%>
<%= link_to_unless_current h(article.title), article %>
<%= number_to_currency article.price, :unit => '€'%>
<% end %>
Show that we now have an operation webshop at localhost:3000/shop
Edit routes.rb to make this is default starting point (also, delete public/index.html !)
config/routes.rb
# You can have the root of your site routed with map.root -- just remember to
delete public/index.html.
map.root :controller => 'shop'
p.4
5. Buying stuff
In article/show, add a new button to buy stuff
app/views/articles/show.html.erb
<%= button_to 'Buy', :controller => 'shop', :action => 'add_to_cart', :id =>
@article %>
We need an add_to_cart in the shop controller. Explain the ʻparamsʼ and ʻredirectʼ
app/controllers/shop_controller.rb
class ShopController < ApplicationController
def index
@articles = Article.all
end
def add_to_cart
@cart.add_item(Article.find(params[:id]))
redirect_to :action => :index
end
end
Start building the cart.
Keynote: The cart (slide 1).
Create a cart and a cart_item. We donʼt use scaffolding, since they are in memory only.
p.5
6. app/models/cart.rb
class Cart
attr_reader :items
def initialize
@items = []
end
def add_item(article)
current_item = @items.find {|cart_item| cart_item.article == article}
if current_item
current_item.increment
else
@items << CartItem.new(article)
end
end
end
app/models/cart_item.rb
class CartItem
attr_accessor :article, :amount
def initialize(article)
@article = article
@amount = 1
end
def increment
@amount += 1
end
end
Use a before_filter in the application to find the cart when needed
app/controllers/application.rb
class ApplicationController < ActionController::Base
before_filter :find_cart
...
private
def find_cart
session[:cart] ||= Cart.new
@cart = session[:cart]
end
end
p.6
7. The shopping cart
Keynote: The cart (slide 2)
Add the shopping cart to the layout
app/views/layouts/default.html.erb
<div id="menubar">
<h3>Shopping cart</h3>
<% if @cart %>
<%= render :partial => @cart%>
<% end %>
</div>
Add the shopping cart partial
app/views/carts/_cart.html.erb
<div id="shoppingcart">
<table>
<%= render :partial => 'carts/cart_item', :collection => cart.items %>
</table>
</div>
Create the cart_item partial: show that we have something now
app/views/carts/_cart_item.html.erb
<tr>
<td><%= link_to cart_item.article.title, cart_item.article %></td>
<td><%= cart_item.amount %>x</td>
</tr>
Add some styling for the shopping cart
public/stylesheets/webshop.css
#shoppingcart {
background-color: white;
margin: 2px;
}
Add totals line to _cart
app/views/carts/_cart.html.erb
<div id="shoppingcart">
<table>
<%= render :partial => 'carts/cart_item', :collection => cart.items %>
<tr>
<td/>
<td><%= number_to_currency cart.price, :unit => '€'
%></td>
</tr>
</table>
</div>
p.7
8. Add necessary methods to cart & cart_item
app/models/cart.rb
def price
@items.inject(0) {|total, cart_item| total + cart_item.price }
end
app/models/cart_item.rb
def price
@article.price * @amount
end
Ordering
Keynote: Recap Rails & rest
Add an order button
app/views/carts/_cart.html.erb
<%= link_to('Order!', :controller => :orders, :action => :new) %>
Keynote: Line & Order
Create (scaffold) Order & OrderLine, remember rake db:migrate. Remember to remove the
new layouts.
Show the ʻbelongs_toʼ in OrderLine, and add a has_many to Order
p.8
9. app/models/order.rb
class Order < ActiveRecord::Base
has_many :order_lines
end
Add basic security to the orders_controller
app/controllers/orders_controller.rb
class OrdersController < ApplicationController
before_filter :authenticate, :except => ['new', 'create']
...
end
And to make that work correctly, move ʻauthenticateʼ to the application controller.
Update the order/new to show our cart, and look more like we want it to.
app/views/orders/new.html.erb
<h3>Here's the cool stuff you bought!</h3>
<%= render :partial => @cart %>
<h3>Who are you, anyway?</h3>
<% form_for(@order) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :customer %><br />
<%= f.text_field :customer %>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
When placing an order, it tells us to log in; thatʼs because of the redirect. Update the
ʻcreateʼ method to look like this.
app/controllers/orders_controller.rb
def create
@order = Order.new(params[:order])
@order.fill_from_cart(session[:cart])
if @order.save
session[:cart].clear
flash[:notice] = 'Thank you, come again!'
redirect_to :controller => 'shop'
else
render 'new'
end
end
p.9
10. Build the ʻfill_from_cartʼ method
app/models/order.rb
def fill_from_cart(cart)
cart.items.each do |item|
order_lines << OrderLine.new(:article => item.article, :amount =>
item.amount)
end
end
Add the ʻclearʼ method to the cart
app/models/cart.rb
def clear
@items = []
end
The wrapup
Keynote: what did we just see?
p.10