1. Trang chủ
  2. » Công Nghệ Thông Tin

Agile Web Development with Rails phần 10 docx

60 355 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Source Code and Application Files for Depot Application
Trường học Pragmatic Program, Inc.
Chuyên ngành Web Development
Thể loại sách hướng dẫn
Định dạng
Số trang 60
Dung lượng 1,72 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

File 106 drop table if exists users; drop table if exists line_items; drop table if exists orders; drop table if exists products; create table products image_url varchar200 not null, da

Trang 1

Source Code This appendix contains three things.

• Full listings for the files we created, and the generated files that we modified, for the final Depot application.

• The source for an e-mail exception notifier starts on page 511

• A cross-reference listing for all the code samples in the book starts

on page 512 All code is available for download from our website at http://pragmaticprogrammer.com/titles/railscode.html

C.1 The Full Depot Application

Trang 2

File 106 drop table if exists users;

drop table if exists line_items;

drop table if exists orders;

drop table if exists products;

create table products (

image_url varchar(200) not null,

date_available datetime not null,

primary key (id)

);

create table orders (

name varchar(100) not null,

primary key (id)

);

create table line_items (

unit_price decimal(10,2) not null,

constraint fk_items_product foreign key (product_id) references products(id),

constraint fk_items_order foreign key (order_id) references orders(id),

primary key (id)

);

create table users (

name varchar(100) not null,

hashed_password char(40) null,

primary key (id)

# Also establishes Cart, LineItem, and User as models This

# is necessary because these classes appear in sessions and

# hence have to be preloaded

class ApplicationController < ActionController::Base

Trang 3

# to the current controller's +index+ action

def redirect_to_index(msg = nil) #:doc:

flash[:notice] = msg if msgredirect_to(:action => 'index')

end

# The #authorize method is used as a <tt>before_hook</tt> in

# controllers that contain administration actions If the

# session does not contain a valid user, the method

# redirects to the LoginController.login.

unless session[:user_id]

flash[:notice] = "Please log in"

redirect_to(:controller => "login", :action => "login")

end end

end

depot_final/app/controllers/admin_controller.rb:

File 83 # The administration functions allow authorized users

# to add, delete, list, and edit products The class

# was initially generated from a scaffold but has since been

# modified, so do not regenerate.

#

# Only logged-in administrators can use the actions here See

# Application.authorize for details.

#

# See also: Product

class AdminController < ApplicationController

before_filter :authorize

# An alias for #list, listing all current products.

def index

listrender_action 'list'

# Initiate the creation of a new product.

# The work is completed in #create.

def new

@product = Product.new

end

# Get information on a new product and

# attempt to create a row in the database.

def create

@product = Product.new(@params[:product])

if @product.saveflash['notice'] = 'Product was successfully created.'

redirect_to :action => 'list'

else

render_action 'new'

end end

# Initiate the editing of an existing product.

Trang 4

# The work is completed in #update.

def edit

@product = Product.find(@params[:id])

end

# Update an existing product based on values

# from the form.

def update

@product = Product.find(@params[:id])

if @product.update_attributes(@params[:product])

flash['notice'] = 'Product was successfully updated.'

redirect_to :action => 'show', :id => @product

# Ship a number of products This action normally dispatches

# back to itself Each time it first looks for orders that

# the user has marked to be shipped and ships them It then

# displays an updated list of orders still awaiting shipping.

#

# The view contains a checkbox for each pending order If the

# user selects the checkbox to ship the product with id 123, then

# this method will see <tt>things_to_ship[123]</tt> set to "yes".

count_text = pluralize(count, "order")

flash.now[:notice] = "#{count_text} marked as shipped"

when 0: "No #{noun.pluralize}"

when 1: "One #{noun}"

else "#{count} #{noun.pluralize}"

end

end

end

Trang 5

File 85 # This controller performs double duty It contains the

# #login action, which is used to log in administrative users.

#

# It also contains the #add_user, #list_users, and #delete_user

# actions, used to maintain the users table in the database.

#

# The LoginController shares a layout with AdminController

#

# See also: User

class LoginController < ApplicationController

layout "admin"

# You must be logged in to use all functions except #login

before_filter :authorize, :except => :login

# The default action displays a status page.

def index

@total_orders = Order.count

@pending_orders = Order.count_pending

end

# Display the login form and wait for user to

# enter a name and password We then validate

# these, adding the user object to the session

if logged_in_usersession[:user_id] = logged_in_user.id

redirect_to(:action => "index")

else

flash[:notice] = "Invalid user/password combination"

end end end

# Add a new user to the database.

# Delete the user with the given ID from the database.

# The model raises an exception if we attempt to delete

# the last user.

Trang 6

flash[:notice] = "Can't delete that user"

end end

# Log out by clearing the user entry in the session We then

# redirect to the #login action.

# [#display_cart] Show the contents of the cart

class StoreController < ApplicationController

before_filter :find_cart, :except => :index

# Display the catalog, a list of all salable products.

rescue

logger.error("Attempt to access invalid product #{params[:id]}")

redirect_to_index('Invalid product')

end

# Display the contents of the cart If the cart is

# empty, display a notice and return to the

end end

# Remove all items from the cart

def empty_cart

@cart.empty!

redirect_to_index('Your cart is now empty')

end

Trang 7

# Prompt the user for their contact details and payment method,

# The checkout procedure is completed by the #save_order method.

# Called from checkout view, we convert a cart into an order

# and save it in the database.

private

# Save a cart object in the @cart variable If we already

# have one cached in the session, use it, otherwise create

# a new one and add it to the session

File 88 # A Cart consists of a list of LineItem objects and a current

# total price Adding a product to the cart will either add a

# new entry to the list or increase the quantity of an existing

# item in the list In both cases the total price will

# be updated.

#

# Class Cart is a model but does not represent information

# stored in the database It therefore does not inherit from

# Add a product to our list of items If an item already

# exists for that product, increase the quantity

# for that item rather than adding a new item.

def add_product(product)

item = @items.find {|i| i.product_id == product.id}

Trang 8

if itemitem.quantity += 1

# Empty the cart by resetting the list of items

# and zeroing the current total price.

File 89 # Line items tie products to orders (and before that, to carts).

# Because the price of a product may change after an order is placed,

# the line item contains a copy of the product price at the time

# it was created.

class LineItem < ActiveRecord::Base

belongs_to :product

belongs_to :order

# Return a new LineItem given a Product.

def self.for_product(product)

item = self.newitem.quantity = 1item.product = productitem.unit_price = product.priceitem

end

end

depot_final/app/models/order.rb:

File 90 # An Order contains details of the purchaser and

# has a set of child LineItem rows.

class Order < ActiveRecord::Base

has_many :line_items

# A list of the types of payments we accept The key is

# the text displayed in the selection list, and the

# value is the string that goes into the database.

PAYMENT_TYPES = [

[ "Check", "check" ], [ "Credit Card", "cc" ],

[ "Purchase Order", "po" ]].freeze

validates_presence_of :name, :email, :address, :pay_type

# Return a count of all orders pending shipping.

def self.count_pending

count("shipped_at is null")

end

# Return all orders pending shipping.

def self.pending_shipping

find(:all, :conditions => "shipped_at is null")

end

# The shipped_at column is +NULL+ for

Trang 9

# unshipped orders, the dtm of shipment otherwise.

File 91 # A Product is something we can sell (but only if

class Product < ActiveRecord::Base

:message => "must be a URL for a GIF, JPG, or PNG image"

# Return a list of products we can sell (which means they have to be

# available) Show the most recently available first.

def self.salable_items

find(:all,

:conditions => "date_available <= now()",

:order => "date_available desc")

end

protected

# Validate that the product price is a positive Float.

def validate #:doc:

errors.add(:price, "should be positive") unless price.nil? || price > 0.0

end

end

depot_final/app/models/user.rb:

File 92 require "digest/sha1"

# A User is used to validate administrative staff The class is

# complicated by the fact that on the application side it

# deals with plain-text passwords, but in the database it uses

# SHA1-hashed passwords.

class User < ActiveRecord::Base

# The plain-text password, which is not stored

# in the database

attr_accessor :password

# We never allow the hashed password to be

# set from a form

attr_accessible :name, :password

validates_uniqueness_of :name

validates_presence_of :name, :password

# Return the User with the given name and

Trang 10

# Log in if the name and password (after hashing)

# match the database, or if the name matches

# an entry in the database with no password

def try_to_login

User.login(self.name, self.password) ||

User.find_by_name_and_hashed_password(name, "")

end

# When a new User is created, it initially has a

# plain-text password We convert this to an SHA1 hash

# before saving the user in the database.

# saved this row This stops it being made available

# in the session

def after_create

@password = nil end

<title>ADMINISTER Pragprog Books Online Store</title>

<%= stylesheet_link_tag "scaffold", "depot", "admin", :media => "all" %>

Trang 11

<title>Pragprog Books Online Store</title>

<%= stylesheet_link_tag "scaffold", "depot", :media => "all" %>

<a href="http://www ">Home</a><br />

<a href="http://www /faq">Questions</a><br />

<a href="http://www /news">News</a><br />

<a href="http://www /contact">Contact</a><br />

<div class="olname"><%= h(order_line.name) %></div>

<div class="oladdress"><%= h(order_line.address) %></div>

</td>

<td class="olitembox">

<% order_line.line_items.each do |li| %>

<div class="olitem">

<span class="olitemqty"><%= li.quantity %></span>

<span class="olitemtitle"><%= li.product.title %></span>

Trang 12

File 94 <h1>Product Listing</h1>

<table cellpadding="5" cellspacing="0">

<%= link_to 'Show', :action => 'show', :id => product %><br/>

<%= link_to 'Edit', :action => 'edit', :id => product %><br/>

<%= link_to 'Destroy', { :action => 'destroy', :id => product },

:confirm => "Are you sure?" %>

<table cellpadding="5" cellspacing="0">

<%= render(:partial => "order_line", :collection => @pending_orders) %>

Trang 13

File 99 <% @page_title = "Administer your Store" -%>

<h1>Depot Store Status</h1>

Trang 14

File 102 <% @page_title = "Checkout" -%>

<%= error_messages_for("order") %>

<%= render_component(:action => "display_cart",

:params => { :context => :checkout }) %>

<h3>Please enter your details below</h3>

<li><%= link_to 'Continue shopping', :action => "index" %></li>

<% unless params[:context] == :checkout -%>

<li><%= link_to 'Empty cart', :action => "empty_cart" %></li>

<li><%= link_to 'Checkout', :action => "checkout" %></li>

Trang 15

<span class="catalogprice"><%= fmt_dollars(product.price) %></span>

<%= link_to 'Add to Cart',

{:action => 'add_to_cart', :id => product },:class => 'addtocart' %><br/>

File 122 ENV["RAILS_ENV"] = "test"

require File.dirname( FILE ) + "/ /config/environment"

Trang 16

def login(name='fred', password='abracadabra')

post :login, :user => {:name => name, :password => password}

assert_redirected_to :action => "index"

description: How to use version control

description: How to automate your project

description: How to beat the clock

image_url: http:// /future.jpg

date_available: <%= 1.day.from_now.strftime("%Y-%m-%d") %>

Trang 17

File 123 require File.dirname( FILE ) + '/ /test_helper'

class CartTest < Test::Unit::TestCase

@cart.total_priceassert_equal 2, @cart.items.size

end

def test_add_duplicate_product

@cart.add_product @version_control_book

@cart.add_product @version_control_bookassert_equal 2*@version_control_book.price, @cart.total_priceassert_equal 1, @cart.items.size

end

end

Trang 18

depot_testing/test/unit/product_test.rb: require

 → page480

require File.dirname( FILE ) + '/ /test_helper'

class ProductTest < Test::Unit::TestCase

assert_equal "Pragmatic Version Control", @product.title

assert_equal "How to use version control", @product.description

assert_equal "http:// /sk_svn_small.jpg", @product.image_url

assert_equal vc_book["id"], @product.id

assert_equal vc_book["title"], @product.title

assert_equal vc_book["description"], @product.description

assert_equal vc_book["image_url"], @product.image_url

assert_equal vc_book["price"], @product.price

assert_equal vc_book["date_available"], @product.date_available_before_type_cast

end

def test_read_with_fixture_variable

assert_kind_of Product, @product

assert_equal @version_control_book.id, @product.id

assert_equal @version_control_book.title, @product.title

assert_equal @version_control_book.description, @product.description

assert_equal @version_control_book.image_url, @product.image_url

assert_equal @version_control_book.price, @product.price

assert_equal @version_control_book.date_available, @product.date_available

end

Trang 19

def test_salable_items

items = Product.salable_itemsassert_equal 2, items.lengthassert items[0].date_available <= Time.nowassert items[1].date_available <= Time.nowassert !items.include?(@future_proof_book)

end

def test_salable_items_using_custom_assert

items = Product.salable_itemsassert_equal 2, items.lengthassert_salable items[0]

File 125 require File.dirname( FILE ) + '/ /test_helper'

class ProductTest < Test::Unit::TestCase

self.use_transactional_fixtures = true

# Re-raise errors caught by the controller.

class LoginController; def rescue_action(e) raise e end; end

class LoginControllerTest < Test::Unit::TestCase

def test_index

get :indexassert_response :success

end

def test_index_without_user

Trang 20

get :index

assert_redirected_to :action => "login"

assert_equal "Please log in", flash[:notice]

post :login, :user => {:name => 'fred', :password => 'abracadabra'}

assert_redirected_to :action => "index"

assert_not_nil(session[:user_id])user = User.find(session[:user_id])assert_equal 'fred', user.name

assert_equal "Pragmatic Version Control", products[0].title assert_tag :tag => "div",

:attributes => { :class => "results" },

:children => { :count => 1,

:only => { :tag => "div",

:attributes => { :class => "catalogentry" }}}

end

end

Trang 21

File 119 require File.dirname( FILE ) + '/ /test_helper'

require 'store_controller'

# Reraise errors caught by the controller.

class StoreController; def rescue_action(e) raise e end; end

class StoreControllerTest < Test::Unit::TestCase

fixtures :products, :orders

assert_template "store/display_cart"

end

def test_add_to_cart_invalid_product

get :add_to_cart, :id => '-1'

assert_redirected_to :action => 'index'

assert_equal "Invalid product", flash[:notice]

end

def test_checkout

test_add_to_cartget :checkoutassert_response :successassert_not_nil assigns(:order)

assert_template "store/checkout"

end

def test_save_invalid_order

test_add_to_cartpost :save_order, :order => {:name => 'fred', :email => nil}assert_response :success

post :save_order, :order => @valid_order_for_fred.attributesassert_redirected_to :action => 'index'

assert_equal "Thank you for your order.", flash[:notice]

Trang 22

assert_template "store/index"

assert_equal 0, session[:cart].items.sizeassert_equal 2, Order.find_all.size

end

def test_assert_tags_many_options

test_add_to_cartget :save_order, :order => {:name => 'fred', :email => nil}

assert_tag :tag => "html"

assert_tag :content => "Pragprog Books Online Store"

assert_tag :tag => "head", :parent => { :tag => "html" } assert_tag :tag => "html", :child => { :tag => "head" } assert_tag :tag => "div", :ancestor => { :tag => "html" } assert_tag :tag => "html", :descendant => { :tag => "div" } assert_tag :tag => "ul", :children => {

order = Order.find(id)

Trang 23

get :save_order, :order => order.attributesassert_redirected_to :action => 'index'

assert_equal("Thank you for your order.", flash[:notice])

end end

assert elapsedSeconds < 3.0, "Actually took #{elapsedSeconds} seconds"

Trang 25

/**** styles for the catalog ***/

/* === Use the Holly Hack to fix layout bugs in IE on Windows === */

/* Hide from IE-mac \*/

Trang 26

C.2 Sample System Notifier

The following is a modified version of the code used by the Basecamp

application to e-mail its maintainers when an exception occurs We show

how to hook this into the application on page 451

notifier/app/models/system_notifier.rb:

File 152 require 'pathname'

class SystemNotifier < ActionMailer::Base

SYSTEM_EMAIL_ADDRESS = %{"Error Notifier" <error.notifier@myapp.com>}

EXCEPTION_RECIPIENTS = %w{maintainer@myapp.com support@myapp.com}

def exception_notification(controller, request,

exception, sent_on=Time.now)

@subject = sprintf("[ERROR] %s\#%s (%s) %s",

controller.controller_name,controller.action_name,exception.class,

Trang 27

C.3 Cross-Reference of Code Samples

The following list can be used to find the file containing source code in

num-ber, you can look that number up in the list that follows to determine

the file containing that code The files are available for download from

Ngày đăng: 07/08/2014, 00:22

TỪ KHÓA LIÊN QUAN