分类目录归档:Ruby on Rails

Ruby on Rails常用内置方法

对象是否为空blank?
puts [ ].blank? #=> true
puts { 1 => 2}.blank? #=> false
puts " cat ".blank? #=> false
puts "".blank? #=> true
puts " ".blank? #自动去除前后空格=> true
puts nil.blank? #=> true

排序:group_by()
groups = posts.group_by {|post| post.author_id}

组句:to_sentence()
puts [ "ant", "bat", "cat"].to_sentence #=> "ant, bat, and cat"
puts [ "ant", "bat", "cat"].to_sentence(:connector => "and not forgetting")
#=> "ant, bat, and not forgetting cat"
puts [ "ant", "bat", "cat"].to_sentence(:skip_last_comma => true)
#=> "ant, bat and cat"

分组:in_groups_of()
[1,2,3,4,5,6,7].in_groups_of(3) {|slice| p slice}
#=> [1, 2, 3]
[4, 5, 6]
[7,nil, nil]
[1,2,3,4,5,6,7].in_groups_of(3,"X") {|slice| p slice}
#=> [1, 2, 3]
[4, 5, 6]
[7,"X", "X"]

字符串方法:
string = "Now is the time"
puts string.at(2) #=> "w"
puts string.from(8) #=> "he time"
puts string.to(8) #=> "Now is th"
puts string.first #=> "N"
puts string.first(3) #=> "Now"
puts string.last #=> "e"
puts string.last(4) #=> "time"
puts string.starts_with?("No") #=> true
puts string.ends_with?("ME") #=> false

count = Hash.new(0)
string.each_char {|ch| count[ch] += 1}
p count #=> {" "=>3, "w"=>1, "m"=>1, "N"=>1, "o"=>1, "e"=>2, "h"=>1, "s"=>1, "t"=>2, "i"=>2}

改变字符串的显示格式:
puts "cat".pluralize #=> cats
puts "cats".pluralize #=> cats
puts "erratum".pluralize #=> errata
puts "cats".singularize #=> cat
puts "errata".singularize #=> erratum
puts "first_name".humanize #=> "First name"
puts "now is the time".titleize #=> "Now Is The Time"
你可以自定义特定单词的单复数转换规则

数字方法:
puts 3.ordinalize #=> "3rd"
puts 321.ordinalize #=> "321st"

数量方法:
puts 20.bytes #=> 20
puts 20.kilobytes #=> 20480
puts 20.megabytes #=> 20971520
puts 20.gigabytes #=> 21474836480
puts 20.terabytes #=> 21990232555520
puts 20.petabytes #=> 22517998136852480
puts 1.exabyte #=> 1152921504606846976

时间方法:
puts 20.seconds #=> 20
puts 20.minutes #=> 1200
puts 20.hours #=> 72000
puts 20.days #=> 1728000
puts 20.weeks #=> 12096000
puts 20.fortnights #=> 24192000
puts 20.months #=> 51840000
puts 20.years #=> 630720000

puts Time.now #=> Thu May 18 23:29:14 CDT 2006
puts 20.minutes.ago #=> Thu May 18 23:09:14 CDT 2006
puts 20.hours.from_now #=> Fri May 19 19:29:14 CDT 2006
puts 20.weeks.from_now #=> Thu Oct 05 23:29:14 CDT 2006
puts 20.months.ago #=> Sat Sep 25 23:29:16 CDT 2004
puts 20.minutes.until("2006-12-25 12:00:00".to_time)
#=> Mon Dec 25 11:40:00 UTC 2006
puts 20.minutes.since("2006-12-25 12:00:00".to_time)
#=> Mon Dec 25 12:20:00 UTC 2006

now = Time.now
puts now #=> Thu May 18 23:36:10 CDT 2006
puts now.to_date #=> 2006-05-18
puts now.to_s #=> Thu May 18 23:36:10 CDT 2006
puts now.to_s(:short) #=> 18 May 23:36
puts now.to_s(:long) #=> May 18, 2006 23:36
puts now.to_s(:db) #=> 2006-05-18 23:36:10
puts now.to_s(:rfc822) #=> Thu, 18 May 2006 23:36:10 -0500
puts now.ago(3600) #=> Thu May 18 22:36:10 CDT 2006
puts now.at_beginning_of_day #=> Thu May 18 00:00:00 CDT 2006
puts now.at_beginning_of_month #=> Mon May 01 00:00:00 CDT 2006
puts now.at_beginning_of_week #=> Mon May 15 00:00:00 CDT 2006
puts now.beginning_of_quarter #=> Sat Apr 01 00:00:00 CST 2006
puts now.at_beginning_of_year #=> Sun Jan 01 00:00:00 CST 2006
puts now.at_midnight #=> Thu May 18 00:00:00 CDT 2006
puts now.change(:hour => 13) #=> Thu May 18 13:00:00 CDT 2006
puts now.last_month #=> Tue Apr 18 23:36:10 CDT 2006
puts now.last_year #=> Wed May 18 23:36:10 CDT 2005
puts now.midnight #=> Thu May 18 00:00:00 CDT 2006
puts now.monday #=> Mon May 15 00:00:00 CDT 2006
puts now.months_ago(2) #=> Sat Mar 18 23:36:10 CST 2006
puts now.months_since(2) #=> Tue Jul 18 23:36:10 CDT 2006
puts now.next_week #=> Mon May 22 00:00:00 CDT 2006
puts now.next_year #=> Fri May 18 23:36:10 CDT 2007
puts now.seconds_since_midnight #=> 84970.423472
puts now.since(7200) #=> Fri May 19 01:36:10 CDT 2006
puts now.tomorrow #=> Fri May 19 23:36:10 CDT 2006
puts now.years_ago(2) #=> Tue May 18 23:36:10 CDT 2004
puts now.years_since(2) #=> Sun May 18 23:36:10 CDT 2008
puts now.yesterday #=> Wed May 17 23:36:10 CDT 2006
puts now.advance(:days => 30) #=> Sat Jun 17 23:36:10 CDT 2006
puts Time.days_in_month(2) #=> 28
puts Time.days_in_month(2, 2000) #=> 29

日期方法:
date = Date.today
puts date.to_s #=> "2006-05-18"
puts date.to_time #=> Thu May 18 00:00:00 CDT 2006
puts date.to_s(:short) #=> "18 May"
puts date.to_s(:long) #=> "May 18, 2006"
puts date.to_s(:db) #=> "2006-05-18"

将字符串转成时间日期:
puts "2006-12-25 12:34:56".to_time #=> Mon Dec 25 12:34:56 UTC 2006
puts "2006-12-25 12:34:56".to_date #=> 2006-12-25

发表在 Ruby on Rails | 留下评论

Ruby on Rails项目文件目录结构和环境设置

Ruby on Rails项目文件目录结构
README 说明文档
Rakefile 生成脚本
app/ 存放项目的Model, View和Controller等文件
components/  存放可重复使用的组件, 现在已经不大使用
config/ 配置文档及数据库连接配置文档
db/ 存放数据库schema和migration信息
doc/  存放自动生成的文档
lib/ 存放在Model, View和Controller间共享的代码
  为方便整理,不同功能的代码可放到其不同的子目录下.

  调用sub_dir下的my_lib_code.rb代码的方法为: require "sub_dir/my_lib_code"
log/ 存放程序生成的日志文件
  里面有三个最主要的log为:development.log, test.log和production.log,分别对应不同的运行环境.
public/ 存放对网络公开的文件
script/ 存放脚本工具
  不带参数直接运行大多数的脚本工具可以显示出相应的使用帮助信息.
  常见的script:
  about
  breakpointer
  destroy<=>generate
  server
  script/performance/benchmarker
test/  测试工具
tmp/  存放临时文件, 如缓存, session, socket…
vendor/ 存放外来代码, 如外来插件, 也可用来放rails构架本身, 以使rails兼容不同版本需求的程序.

运行环境
使用-e XXXX切换运行环境:
ruby script/server -e production #默认为development

配置数据库参数
修改config/database.yml文件

配置环境
修改config/environment.rb文件.

命名规范
所有变量以小写字母单词命名,单词间用下划线"_"分隔. 如order_status
所有class和module以大写开头的单词命名,单词间直接连接,如: LineItem

数据库中所有表格命名形式同变量名,且全为复数形式,如:orders
所有文件名命名形式也同变量名

Rails可推论相关文件名, 所以在框架内可以节省很多require "xxx". Rails会自动加入相关的调用.
Rails关于文件名的思维逻辑实例:
如果一个class名为:LineItem
ralis将推出:
1.此class对应的数据库表格名为line_items.
2.此class对应的文件名为line_item.rb
….

创建model下的controller
ruby script/generate controller Admin::Book action1 action2 …

使用debug() helper method
<%=debug(@order) %>

发表在 Ruby on Rails | 2 条评论

Ruby on Rails实战–创建一个网上商店F收尾工作

上一节:Ruby on Rails实战–创建一个网上商店E用户管理模块

本节是depot电子商店的项目的最后一节。将完成生成XML Feed,并介绍如何生成项目文档。
生成通过商品ID反查定购用户信息的XML Feed.

orders的id和关联line_items表中的order_id,而line_items表中的product_id和products表中的id相关联, 通过orders可以查products的id,现在建立反向关联.
在depot/app/models/product.rb文件加入:
has_many :orders, :through => :line_items
使product通过line_item查order信息

新建一个info controller:

depot> ruby script/generate controller info
controllers/info_controller.rb文件内容为:
class InfoController < ApplicationController
  def who_bought
    @product = Product.find(params[:id])
    @orders = @product.orders
    respond_to do |accepts| #下面代码根据request的形式选择发送html还是xml格式内容
      accepts.html
      accepts.xml
    end
  end
end

加入html形式内容页面:depot/app/views/info/who_bought.rhtml,内容如下:
<h3>People Who Bought <%= @product.title %></h3>
<ul>
 <% for order in @orders -%>
  <li> <%= mail_to order.email, order.name %> </li>
 <% end -%>
</ul>

加入xml形式内容页面:depot/app/views/info/who_bought.rxml,内容如下:
xml.order_list(:for_product => @product.title) do
  for o in @orders
    xml.order do
      xml.name(o.name)
      xml.email(o.email)
    end
  end
end

现在通过http://localhost:3000/info/who_bought/1 地址就可以查出商品1(ID)的定购者信息了.

 

生成开发文档RDoc

项目中的doc/README_FOR_APP文件可以加入项目相关的说明信息.
使用depot> rake doc:app 可以生成项目的开发文档文件. 程序对项目中所有文件,classes和methods进行分析, 列出相关信息. 文件为html格式.

至此depot项目完成. 一个用Ruby on Rails续建的简单的网上商店完成了!

发表在 Ruby on Rails | 留下评论

Ruby on Rails实战–创建一个网上商店E用户管理模块

本节创建商店的用户管理系统. 创建存放用户资料的数据表,加入添加,删除用户功能, 后台管理用户权限审查功能,和用户登录 退出功能.
创建用户model: depot> ruby script/generate model user
修改depot/db/migrate/007_create_users.rb文件内容如下:
class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.column :name, :string
      t.column :hashed_password, :string
      t.column :salt, :string #生成密码hash用的salt
    end
  end

 
  def self.down
    drop_table :users
  end
end

应用depot> rake db:migrate

修改depot/app/models/user.rb文件内容如下:
require ‘digest/sha1′
#用来生成密码hash值
class User < ActiveRecord::Base

#检验添加用户表格
  validates_presence_of :name,
  :password,
  :password_confirmation
  validates_uniqueness_of :name
  validates_length_of :password,
  :minimum => 5,
  :message => "should be at least 5 characters long"
  attr_accessor :password_confirmation
  #自动"再输入一次密码"的检验
  validates_confirmation_of :password
 
  # ‘password’ 是一个 virtual attribute
  def password
    @password
  end
 
  def password=(pwd)
    @password = pwd
    create_new_salt
    self.hashed_password = User.encrypted_password(self.password, self.salt)
  end
 
  def self.authenticate(name, password)
    user = self.find_by_name(name)
    if user
      expected_password = encrypted_password(password, user.salt)
      if user.hashed_password != expected_password
        user = nil
      end
    end
    user
  end
 
  #安全删除,当试图删除用户表中最后一个用户时rollback数据库内容
  def safe_delete
    transaction do
      destroy
      if User.count.zero?
        raise "Can’t delete last user"
      end
    end
  end
 
  private
 
  def self.encrypted_password(password, salt)
    string_to_hash = password + "wibble" + salt
    Digest::SHA1.hexdigest(string_to_hash)
  end
 
  def create_new_salt
    self.salt = self.object_id.to_s + rand.to_s
  end
end

生成Login controller
depot> ruby script/generate controller Login add_user login logout delete_user list_users
后面接的大堆都是login里面的actions.
depot/app/controllers/login_controller.rb文件内容为:
class LoginController < ApplicationController
  #所有actions,除了login都要进行权限审查
  before_filter :authorize, :except => :login
  #加入admin的模板
  layout"admin"
 
  def index
    @total_orders = Order.count
  end
 
  def add_user
    @user = User.new(params[:user])
    if request.post? and @user.save
 #requset.post?用来检验request是否POST形式
      flash[:notice] = "User #{@user.name} created"
      @user = User.new
    end
  end
 
  def login
    session[:user_id] = nil
    if request.post?
      user = User.authenticate(params[:name], params[:password])
      if user
        session[:user_id] = user.id
        #如果原来页面存在,返回原先页面
        redirect_to(session[:original_url] || { :action => "index" })
      else
        flash[:notice] = "Invalid user/password combination"
      end
    end
  end
 
  def logout
    session[:user_id] = nil
    flash[:notice] = "Logged out"
    redirect_to(:action => "login")
  end
 
  def delete_user
    id = params[:id]
    if id && user = User.find(id)
      begin
   #安全删除
        user.safe_delete
        flash[:notice] = "User #{user.name} deleted"
      rescue Exception => e
        flash[:notice] = e.message
      end
    end
    redirect_to(:action => :list_users)
  end
 
  def list_users
    @all_users = User.find(:all)
  end
end

修改depot/app/controllers/application.rb文件,添加用户权限审查代码:
class ApplicationController < ActionController::Base
  private
  def authorize
    unless User.find_by_id(session[:user_id])
      session[:original_uri] = request.request_uri #记住原来页面
      flash[:notice] = "Please log in"
      redirect_to(:controller => "login", :action => "login")
    end
  end
end

向depot/app/controllers/admin_controller.rb文件加入审查要求:
class AdminController < ApplicationController
  before_filter :authorize

  #….

修改管理页面使用的页面模板depot/app/views/layouts/admin.rhtml,内容如下:
<html>
 <head>
  <title>Administer the Bookstore</title>
  <%= stylesheet_link_tag "scaffold", "depot", :media => "all" %>
 </head>
 <body id="admin">
  <div id="banner">
   <img src="/images/logo.png"/>
   <%= @page_title || "Pragmatic Bookshelf" %>
  </div>
  <div id="columns">
   <div id="side">
    <p>
     <%= link_to "Products", :controller => ‘admin’, :action => ‘list’ %>
    </p>
    <p>
     <%= link_to "List users", :controller => ‘login’, :action => ‘list_users’ %>
     <br/>
     <%= link_to "Add user", :controller => ‘login’, :action => ‘add_user’ %>
    </p>
    <p>
     <%= link_to "Logout", :controller => ‘login’, :action => ‘logout’ %>
    </p>
   </div>
   <div id="main">
    <% if flash[:notice] -%>
     <div id="notice"><%= flash[:notice] %></div>
    <% end -%>
    <%= @content_for_layout %>
   </div>
  </div>
 </body>
</html>

添加用户页面depot/app/views/login/add_user.rhtml内容:
<div class="depot-form">
 <%= error_messages_for ‘user’ %>
 <fieldset>
  <legend>Enter User Details</legend>
  <% form_for :user do |form| %>
   <p>
    <label for="user_name">Name:</label>
    <%= form.text_field :name, :size => 40 %>
    <!–上面代码自动生成input框–>
   </p>
   <p>
    <label for="user_password">Password:</label>
    <%= form.password_field :password, :size => 40 %>
   </p>
   <p>
    <label for="user_password_confirmation">Confirm:</label>
    <%= form.password_field :password_confirmation, :size => 40 %>
   </p>
   <%= submit_tag "Add User", :class => "submit" %>
  <% end %>
 </fieldset>
</div>

用户登录页面depot/app/views/login/login.rhtml内容为:
<div class="depot-form">
 <fieldset>
  <legend>Please Log In</legend>
  <%= start_form_tag %>
  <!–form开始–>
   <p>
    <label for="name">Name:</label>
    <%= text_field_tag :name, params[:name] %>
   </p>
   <p>
    <label for="password">Password:</label>
    <%= password_field_tag :password, params[:password] %>
   </p>
   <p>
    <%= submit_tag "Login" %>
   </p>
  <%= end_form_tag %>
 </fieldset>
</div>

用户主页depot/app/views/login/index.rhtml内容:
<h1>Welcome</h1>
It’s <%= Time.now %>.
We have <%= pluralize(@total_orders,"order") %>.
<!–pluralize自动根据@total_orders的数量决定order使用单数还是复数形式–>

用户列表页面depot/app/views/login/list_users.rhtml内容:
<h1>Administrators</h1>
<ul>
 <%  for user in @all_users %>
  <li><%= link_to "[X]", { # link_to 参数
  #在用户名前自动添加删除用户的链接
  :controller => ‘login’,
  :action => ‘delete_user’,
  :id => user},
  { # html 参数
  :post => true,
  :confirm => "Really delete #{user.name}?"
  } %>
  <%= user.name %>
  </li>
 <% end %>
</ul>

整理代码:
在depot/app/controllers/store_controller.rb中加入:
  before_filter :find_cart, :except => :empty_cart
  def find_cart
    @cart = (session[:cart] ||= Cart.new)
  end
并删除其它散杂的@cart赋值语句.

本节结束.

发表在 Ruby on Rails | 留下评论

Ruby on Rails实战–创建一个网上商店D收银台

上一节:Ruby on Rails实战–创建一个网上商店C小试Ajax
本节完成收银台功能. 为页面新增一个结账按钮,用户挑选商品后点击结账按钮时出现一个用户资料表单,用户填交后,系统将商品信息和用户信息保存到数据库中.

创建order model
depot> ruby script/generate model order
修改depot/db/migrate/005_create_orders.rb 内容为:
class CreateOrders < ActiveRecord::Migration
  def self.up
    create_table :orders do |t|
      t.column :name, :string
      t.column :address, :text
      t.column :email, :string
      t.column :pay_type, :string, :limit => 10
    end
  end

  def self.down
    drop_table :orders
  end
end

创建line_item model,
depot>ruby script/generate model line_item
修改depot/db/migrate/006_create_line_items.rb文件内容为:
class CreateLineItems < ActiveRecord::Migration
  def self.up
    create_table :line_items do |t|
      t.column :product_id, :integer, :null => false
      t.column :order_id, :integer, :null => false
      t.column :quantity, :integer, :null => false
      t.column :total_price, :integer, :null => false
    end
 #创建两个foreign key
    execute "alter table line_items
            add constraint fk_line_item_products
            foreign key (product_id) references products(id)"

    execute "alter table line_items
            add constraint fk_line_item_orders
            foreign key (order_id) references orders(id)"
  end
 
  def self.down
    drop_table :line_items
  end
end

向数据库应用
depot> rake db:migrate

关联model:
在depot/app/models/order.rb和
depot/app/models/product.rb两文件者加入:
has_many :line_items
声明这两个model下有多个line_items
在depot/app/models/line_item.rb文件中加入:
belongs_to :order
belongs_to :product
声明这个model从属于order和product
*if a table has foreign keys, the corresponding model should have a belongs_to for each.

添加结账按钮:
在depot/app/views/store/_cart.rhtml文件:
<%= button_to "Empty cart", :action => :empty_cart %>
行上方加入:
<%= button_to "Checkout", :action => :checkout %>

checkout action
在depot/app/controllers/store_controller.rb文件中定义checkout action 和save_order action:
  def checkout
    @cart = find_cart
    if @cart.items.empty?
 #判断篮中是否为空
      redirect_to_index("Your cart is empty")
    else
      @order = Order.new
    end
  end

  def save_order
    @cart = find_cart
    @order = Order.new(params[:order])
    @order.add_line_items_from_cart(@cart)
    if @order.save #保存数据
      session[:cart] = nil #清空购物篮
      redirect_to_index("Thank you for your order")
    else
      render :action => :checkout
    end
  end

相应 checkout 的view文件depot/app/views/store/checkout.rhtml内容为:
<div class="depot-form">
 <%= error_messages_for ‘order’ %>
 <fieldset>
  <legend>Please Enter Your Details</legend>
  <% form_for :order, :url => { :action => :save_order } do |form| %>
   <p>
    <label for="order_name">Name:</label>
    <%= form.text_field :name, :size => 40 %>
   </p>
   <p>
    <label for="order_address">Address:</label>
    <%= form.text_area :address, :rows => 3, :cols => 40 %>
   </p>
   <p>
    <label for="order_email">E-Mail:</label>
    <%= form.text_field :email, :size => 40 %>
   </p>
   <p>
    <label for="order_pay_type">Pay with:</label>
    <%=
     form.select :pay_type,
     Order::PAYMENT_TYPES,
     #这个下拉列表内容另定义
     :prompt => "Select a payment method"
    %>
   </p>
   <%= submit_tag "Place Order", :class => "submit" %>
  <% end %>
 </fieldset>
</div>
上面蓝色的代码用来组建form.

在depot/app/models/order.rb文件中定义上面下拉列表的PAYMENT_TYPES内容:
  PAYMENT_TYPES = [
  # 显示文字 数据库内容
  [ "Check", "check" ],
  ["Credit card", "cc" ],
  ["Purchase order", "po" ]
  ]
  #校验用户名,地址,信箱地址和支付方式是否为空
  validates_presence_of :name, :address, :email, :pay_type
  #校验支付方式是否合法(加强安全性)
  validates_inclusion_of :pay_type, :in => PAYMENT_TYPES.map {|disp, value| value}
 
  #定义上面用到的add_line_items_from_cart()
   def add_line_items_from_cart(cart)
    cart.items.each do |item|
      li = LineItem.from_cart_item(item)
      line_items << li
    end
  end

修改depot/app/models/line_item.rb文件内容为:
class LineItem < ActiveRecord::Base
  belongs_to :order
  belongs_to :product
  def self.from_cart_item(cart_item)
    li = self.new
    li.product = cart_item.product
    li.quantity = cart_item.quantity
    li.total_price = cart_item.price
    li
  end
end

在depot/app/views/store/add_to_cart.rjs文件中加入:
page.select(‘div#notice’).each { |div| div.hide }
搜索页面中id=notice的div,如有找到,将div隐藏.
在用户结账后继续购物时隐藏结账成功提示语.

本节结束

下一节:Ruby on Rails实战–创建一个网上商店E用户管理模块

发表在 Ruby on Rails | 留下评论

Ruby on Rails实战–创建一个网上商店C小试Ajax

上一节:Ruby on Rails实战–创建一个网上商店B前台

本节将向商店加入一个Ajax购物篮,使商店产品列表在不刷新的情况下更新显示购物篮信息

修改depot/app/controllers/store_controller.rb文件,如下:
  def index
    @products = Product.find_products_for_sale
    @cart = find_cart #加入对@cart的定义
  end
   def add_to_cart
    begin
      @product = Product.find(params[:id])
    rescue
      logger.error("Attempt to access invalid product #{params[:id]}")
      redirect_to_index("Invalid product")
    else

      @cart = find_cart
      @current_item = @cart.add_product(@product)
      redirect_to_index unless request.xhr?
   #request.xhr判断是否xmlHttpRequest, 否将指向index action, 用来兼容关闭了javascript功能的浏览器
   #开发时以无Ajax的为蓝本,再修改到ajax,以方便兼容性处理
    end
  end
  #….
  private
  def redirect_to_index(msg = nil)
#msg = nil 指定msg的默认值, 如果msg没有说明, msg = nil.
    flash[:notice] = msg if msg
 #加入对无参数的支持
    redirect_to :action => :index
  end
  #…

修改depot/app/models/cart.rb文件,如下:
def add_product(product)
 current_item = @items.find {|item| item.product == product}
 if current_item
  current_item.increment_quantity
 else
  current_item = CartItem.new(product)
  @items << current_item
 end
 current_item #新加,使返回修改商品
end

修改depot/app/controllers/store_controller.rb文件,如下:
def add_to_cart
 begin
  @product = Product.find(params[:id])
 rescue
  logger.error("Attempt to access invalid product #{params[:id]}")
  redirect_to_index("Invalid product")
 else
  @cart = find_cart
  @current_item = @cart.add_product(@product) #新加,定义@current_item
 end
end
#….
  def empty_cart
    session[:cart] = nil
    redirect_to_index #清空购物篮后返回商品列表
  end
#…

修改depot/app/models/cart.rb文件,增加一个购物篮内商品数据变量
def total_items
 @items.inject(0) {|sum, item| sum + item.quantity}
end

在depot/app/views/store/index.rhtml
中将:
 <%= button_to "Add to Cart", :action => :add_to_cart, :id => product %>
改为:
  <%= form_remote_tag :url => { :action => :add_to_cart, :id => product } %>
        <%= submit_tag "Add to Cart" %>
        <%= end_form_tag %>
 
在depot/app/views/layouts/store.rhtml文件里加入模板对javascript的支持,如下:
<html>
 <head>
  <title>Pragprog Books Online Store</title>
  <%= stylesheet_link_tag "depot", :media => "all" %>
  <%= javascript_include_tag :defaults %>
 </head>

在depot/app/views/store/新加一个partial template _cart.rhtml,内容如下:
<div class="cart-title">Your Cart</div>
<table>
 <%= render(:partial => "cart_item", :collection => cart.items) %>
 <!–调用另一个partial template _cart_item.rhtml–>
 <tr class="total-line">
  <td colspan="2">Total</td>
  <td class="total-cell"><%= format_price(cart.total_price) %></td>
 </tr>
</table>
<%= button_to "Empty cart", :action => :empty_cart %>
partial template以下找线开头,由render的调用.

在depot/app/views/store/新加一个partial template _cart_item.rhtml,内容如下:
<% if cart_item == @current_item %>
<!–后面用来显示特殊效果–>
 <tr id="current_item">
 <% else %>
 <tr>
<% end %>
  <td><%= cart_item.quantity %>&times;</td>
  <td><%= h(cart_item.title) %></td>
  <td class="item-price"><%= format_price(cart_item.price) %></td>
 </tr>

在depot/app/views/layouts/store.rhtml文件中增加调用partial template的代码:
   <div id="side">
                <%= hidden_div_if(@cart.items.empty?, :id => "cart") %>
    <!–hidden_div_if 这个 helpper method用来向div加入display:none属性, 代码见下文–>
                    <%= render(:partial =>"cart", :object => @cart) %>
                </div>
    …..

创建hidden_div_if  helpper method
  def hidden_div_if(condition, attributes = {})
    if condition
      attributes["style"] = "display: none"
    end
    attrs = tag_options(attributes.stringify_keys)
    "<div #{attrs}>"
  end

删除不用了的add_to_cart.rhtml

新建depot/app/views/store/add_to_cart.rjs文件,内容如下:
page[:cart].replace_html :partial => ‘cart’, :object => @cart
#将页面中id=cart的元素换成partial _cart.rhtml的内容
page[:cart].visual_effect :blind_down if @cart.total_items == 1
#当向购物篮添加第一个商品时,特效显示出购物篮
page[:current_item].visual_effect :highlight,
               :startcolor =>"#88ff88",
               :endcolor =>"#114411"
#当购物篮商品发生变化时,特效显示变化行

本节完成,为使ajax效果生效, 我重新启动了服务器

下一节:Ruby on Rails实战–创建一个网上商店D收银台

本文最后更新 20070108

发表在 Ruby on Rails | 留下评论

Ruby语言入门

1.Ruby是一个面向对象的(object-oriented)的编程语言.
Ruby中所有的操作元都是对象(object).
类(Class),方法(method),实例(instance),对象(object, class instance), constructor, instance variable, instance method, –当自然语言学家遇上程序语言…
method实例:
"dave".length
line_item_one.quantity
-1942.abs
cart.add_line_item(next_purchase)

2.命名

局部变量,method参数和method名以小写字母或下划线开头.实例变量以@号开头.多单词名最好以下划线分隔单词
实例: line_item
Class名,module名,常量以大写字母开头. 最好以大写字母分隔不同单词.如: PurchaseOrder
Symbols如: redirect_to :action => "edit", :id => params[:id]
用来指定值. ???

3.Method
实例:
def say_goodnight(name)
 result = "晚安, " + "#{name.capitalize}"
 return result
end
#该道别了…
puts say_goodnight("小李")
puts (say_goodnight("王老板"))

Ruby语句每句一行,每行结束不用任何符号
+可以用来连接字符串
注释语句以#开头
使用def定义method
puts method用来调用method,将结果显示在平台上.
双引号字符串内的转意字符(如
)和变量名(上面的#{name}及其method capitalize())将被解释,单引号内字符串则不被解释.
Ruby自动返回method中最后一个表达式的值. 上method等于:
def say_goodnight(name)
 "晚安, #{name.capitalize}"
end
puts say_goodnight("小李")

4.Class
class Order < ActiveRecord::Base
#class 类名 < 子model ::主class名 (定义声明和从属声明)
 has_many :line_items

 def self.find_all_unpaid
 #使用 self.前缀,定义class method (相对于instance method)
 #可在程序中任意调用Order.find_all_unpaid
  find(:all, ‘paid = 0′)
 end

 def total
 #定义instance method
 #调用时应用到实例对象上
 #如puts "总数是#{order.total)",注意这个小写的order
  sum = 0
  line_items.each {|li| sum += li.total}
 end
end

关于instance变量
class Greeter
 def initialize(name)
  @name = name
  # instance变量以@开头
  #变量@name只能在类内调用
 end
 def say(phrase)
  puts "#{phrase},#{@name}"
 end
end

g1 = Greeter.new("小李")
g2 = Greeter.new("王老板")

g1.say("好啊")  #=> 老啊,小李
g2.say("早啊")  #=> 早啊,王老板

如要将Greeter里的name在外部调用,要用以下方法:
Class Greeter
 def initialize(name)
  @name = name
 end
 def name
  @name
 end
 def name=(new_name)
 #???
  @name = new_name
 end
end

g = Greeter.new("小李")
puts g.name #=>小李
g.name = "王老板"
puts g.name #=> 王老板
方便实现上面功能的代码:
class Greeter
 attr_accessor :name #可读可写
 attr_reader :name #只可读
 attr_writer :name #只可写

Private和Protected
Private methods只能在同instance内调用
Protected methods除了可在同instance内调用, 也可在类或子类内调用

5.Module
Module和Class类似,是一个常量,method,module和class集. 不同的是不能用Module来创建object.
Module的作用:
1.产生namespace
2.分享class间的功能,没继承效果
多个class可以共用一个module,一个class也可使用多个module.

Rails还使用module产生helper method, 辅助应用到view.

6. Array 和Hash
Array和Hash 都是一个数组. Array的索引是整数, 而Hash的索引可以是object. Array的效率相对比Hash高.
Array和Hash都可包含不同类型的变量
Array实例:
a = [1, '狗狗', 3.14] #新数组
a[0] #调用(1)
a[2] #调用(nil, 在Ruby中nil是一个object,指代nothing)
#只有nil和false才等同于逻辑"否". 0为true

又一实例说明<<的用法:
ages = [] #定义新数组
for person in @people
 ages << person.age #<<将数值追加到数组中去
end

又又一个实例说明自动生成单词数组的用法:
a = ['ant', 'bee', 'cat', 'dog', 'elk']
a = %w{ ant bee cat dog elk }

Hash实例:
inst_section = {
 :高手 => ‘小张’,
 :新来的 => ‘小李’,
 :老板 => ‘王老板’
}
#调用
inst_section[:高手] #=>’小张’
inst_section[:最讨厌的] #=>nil
左边的symbols是索引,右边的是值,索引不能重复
值可以另一个array或hash
当hash是method的最后一个参数时,可以省略括号,如:
redirect_to :action => ‘show’, :id => product.id

7.流控制
if实例:
if count > 10
 puts "再来一次"
elseif
 "你输 了"
else
 "输入一个数字"
end
while实例:
while weight < 100 and num_pallets <= 30
 pallet = next_pallet()
 weight += pallet.weight
 num_pallets += 1
end
表达式简化式实例:
puts "早上好" if time < a

8.正则表达式
写法 /pattern/ 或 %r{pattern}
用法实例:
if line =~ /P(erl|ython)/ #用这个来查找匹配
 puts "这个不怎么样吧?"
end
正则小例:
/ab+c/ a跟着多个或一个b,后面接c

9.Block 和 Iterator
一行的Block实例:
{ puts "你好" }
多行的Block实例:
do
 club.enroll(person)
 person.socialize
end
Block的用法实例:
greet { puts "嗨" }
放在method后,如有参数,block放到参数后,如:
verbose_greet("王五","老板") { puts "嗨" }

iterator is a method that returns successive elements from some kind of collection, such as an array.
Block 和 Iterator联合使用实例:
animals = %w( ant bee cat dog elk )
animals.each { |animal| puts animal }
#animals.each会yield出后面的block. 本身的值会经|animal|传给block.
所有的整数带有times() method, 使用:
3.times { print "哇! " }

10.意外处理
begin
 content = load_blog_data(file_name)
rescue BlogDataNotFound
 STDERR.puts "#{file_name}文件没有找到"
rescue BlogDataFormatError
 STDERR.puts "#{file_name}文件数据无效"
rescue Exception => exc
 STDERR.puts "读取#{file_name}文件出错:#{exc.message}"
end

11.Marshaling Objects
将object 转换成数据流,方便移动.

12. 互交Ruby

13.惯用形式
empty! Bang method( normally do something destructive to the receiver)
empty? 判断
a || b 如果a为false或nil, 则返回 b, 否则返回a.
a ||= b a = a || b
a += 1 a = a +1
a *= 5 a = a * 5
* 对于大部分操作:a op= b a = a op b

发表在 Ruby on Rails | 留下评论

Ruby语言简介

Ruby是一种面向对象(object-oriented)的计算机编程语言.由日本人松本行弘(Yukihiro "Matz" Matsumoto)于1993年发明.
Ruby语言简介
Ruby之父 松本行弘(Matsumoto Yukihiro)

松本行弘认为以往人们在开发编程语言时过于看重"计算机", 而忽视"人", 过分强调运行速度. 而松本行弘提倡机器不是主人,是工具,而真正的主人应该是"人", 于是他打算开发一种更人性化的编程语言,这就是后来的Ruby.
松本行弘还提出语言不但是表达思想的工具,也是人类进行思想的方法,使用的语言不同, 人也会产生不同的思想模式. 松本行弘说编程语言也会产生类似的作用. 他认为一个好的编程语言不但应该可以执行程序任务, 也应该帮助程序员形成更好的编程逻辑.
松本行弘也强调编程语言应该不单让程序员带来工资,也要给他们带来乐趣.
ruby语言官方主页:
http://www.ruby-lang.org/
松本行弘谈ruby设计原则(英语录音):
http://www.itconversations.com/shows/detail1638.html

发表在 Ruby on Rails | 留下评论

Ruby on Rails实战–创建一个网上商店B前台

上一节: Ruby on Rails实战–创建一个网上商店a
本节创建商店的前台模块, 也就是客户看到的页面.

1.创建前台Controller Store:depot> ruby script/generate controller store index
最后的index创建一个index Action,用作默认action

2.创建一个session,用来保存购物篮数据
depot> rake db:sessions:create
然后
depot> rake db:migrate
(users of Rails applications must have cookies enabled in their browsers.???)
在config/environment.rb中将
config.action_controller.session_store = :active_record_store
语句前的注解符#去除,启动数据库保存session功能
*使用depot> rake db:sessions:clear可以清空当前session内容

3.创建前台的Store的Controller
修改depot/app/controllers/store_controller.rb文件为
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
class StoreController < ApplicationController
  def index
    @products = Product.find_products_for_sale
  end
  #find_products_for_sale将在model里面定义,见下文
  #下面是购物篮添加程序
  def add_to_cart
    begin
      @product = Product.find(params[:id])
    rescue
      logger.error("Attempt to access invalid product #{params[:id]}")
      redirect_to_index("Invalid product")
    else
      @cart = find_cart
      @cart.add_product(@product)
    end
  end
  #上面的begin…rescue…else…end用来处理错误
  #当begin出错,即找不到当前ID的商品时进行rescue处理,以防止入侵或意外出错
  #上面的logger.eeror用来记录错误log,对应log目录里的development.log文件
  # @product = Product.find(params[:id])可以接 add_to_cart/1 样式传来的request.
  #上面的redirect_to_index见下面代码
  #上面的find_cart见下面代码
  #add_product()代码见下面.

  #下面是购物篮清空程序
  def  empty_cart
    session[:cart] = nil
    redirect_to_index("Your cart is currently empty")
  end

  private #开始定义局部代码

#一个高压缩处理了的错误信息显示代码
  def redirect_to_index(msg)
    flash[:notice] = msg #用来显示出错信息
    redirect_to :action => :index
  end
#flash[:notice] = msg #用来显示flash信息
#redirect_to :action => :index 可用来转移页面.

#使用session来保存购物篮数据
  def find_cart
    session[:cart] ||= Cart.new
  end
end
#上面的||=语句用来创建检查session,如果存在则调出数据,如果不存在生成新session
#!!!好符号化的语言啊!!!日本人不太懂英语吧.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

4.创建model:
修改depot/app/models/product.rb文件为:
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
class Product < ActiveRecord::Base
  def self.find_products_for_sale
    find(:all, :order => "title")
  end
 #用 self.前缀,定义class method
  #上面的find(:all, :order => "title")让程序列出所有商品,按标题排序
  #以下为原来的效验代码….
end
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

手动创建一个depot/app/models/cart.rb, 作为购物篮的model,内容如下:
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
class Cart
  include Reloadable #手动创建的model要加入这句,以后服务器才会自动刷新
  #Whenever youcreatea non-database model, youneed toinclude a includeReloadable directive in the source.
  attr_reader :items 
  def initialize #初始化method
    @items = [] # @items = []来赋空集.
  end
  def add_product(product)
    existing_product = @items.find {|item| item.product == product}
    if existing_product #如果篮中已有商品,累加数量
      existing_product.increment_quantity
    else
      @items << CartItem.new(product)
    end
  end
  def total_price
    @items.inject(0) { |sum, item| sum + item.price }
  end
end
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

手动创建一个depot/app/models/cart_item.rb model,用来处理购物篮内的商品,内容如下:
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
class CartItem
  include Reloadable #见上文
  attr_reader :product, :quantity
  def initialize(product)
    @product = product
    @quantity = 1
  end
  def increment_quantity
    @quantity += 1
  end
  def title
    @product.title
  end
  def price
    @product.price * @quantity
  end
end
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

5.创建View:
修改depot/app/views/store/index.rhtml文件为:
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
<% for product in @products -%>
 <div class="entry">
  <img src="<%= product.image_url %>"/>
  <h3><%= h(product.title) %></h3>
  <%= product.description %>
  <span class="price"><%= format_price(product.price) %></span>
  <%= button_to "Add to Cart", :action => :add_to_cart, :id => product %>
 </div>
 <br />
<% end %>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
*上面的product.description为使描述带有HTML代码,没有使用h(),带有安全隐患.
*上面的format_price为一个helper,来用格式化整数形的价格.见下文.
*上面的button_to将生成一个按钮,链接到add_to_cart action,用于向购物篮添加商品,它将以post方向向add_to_cart action传送商品ID信息. (post比get好???)
(And a POST request is just the ticket when we want to do something like add an item to a cart.)
*button_to的作用实际上相应于生成如下代码:
<form method="post" action="/store/add_to_cart/1" class="button-to">
<input type="submit" value="Add to Cart" />
</form>

手动建立一个添加商品页面的view depot/app/views/store/add_to_cart.rhtml,文件内容如下:
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
<div class="cart-title">Your Cart</div>
<table>
 <% for cart_item in @cart.items %>
  <tr>
   <td><%= cart_item.quantity %> &times;</td>
   <td><%= h(cart_item.title) %></td>
   <td class="item-price"><%= format_price(cart_item.price) %></td>
  </tr>
 <% end %>
 <tr class="total-line">
  <td colspan="2">Total</td>
  <td class="total-cell"><%= format_price(@cart.total_price) %></td>
 </tr>
</table>
<%= button_to "Empty cart", :action => :empty_cart %>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
* &times;生成一个乘号.

6.增加format_price helper
修改depot/app/helpers/store_helper.rb文件为:
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
module StoreHelper
  def format_price(amount)
    dollars, cents = amount.divmod(100)
    sprintf("$%d.%02d", dollars, cents)
  end
end
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
*上面的divmod是一个内置方法. 用它将以分为单位的整数形价格分为元和分两部分.

7.给view添加一个模板
修改:depot/app/views/layouts/store.rhtml文件内容为:
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
<html>
 <head>
  <title>Pragprog Books Online Store</title>
  <%= stylesheet_link_tag "depot", :media => "all" %>
 </head>
 <body id="store">
  <div id="banner">
   <img src="/images/logo.png"/>
   <%= @page_title || "Pragmatic Bookshelf" %>
  </div>
  <div id="columns">
   <div id="side">
    <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>
   <div id="main">
    <!–下面的代码显示错误提示信息–>
    <% if flash[:notice] -%>
     <div id="notice"><%= flash[:notice] %></div>
    <% end -%>
    <!–下面的代码显示不同action的子view内容–>
    <%= @content_for_layout %>
    <!–其它的内容为共用模板–>
   </div>
  </div>
 </body>
</html>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
*这个和controller同名的layout文件为view的默认模板.
*<%= @page_title || "Pragmatic Bookshelf" %> ???

到这里用户界面已经基本完成.
调试使用 http://localhost:3000/store/访问

下一节:Ruby on Rails实战–创建一个网上商店C小试Ajax

发表在 Ruby on Rails | 一条评论

Ruby on Rails实战–创建一个网上商店a

这是一个使用Ruby on Rails创建一个网上商店的实例. 实例来自<Agile Web Development with Rails>第二版 beta. 下面是我整理的笔记. 笔记对实例进行了注解, 记录我的学习路程, 同在学习Ruby on Rails的朋友可作为参考. 如果里面有什么错误,还请留言指正.
按学习日程,我把这个实例分为几段, 分别完成: 网上商店基本构架 Ruby语言入门

这是第一部分, 完成商店的基本构架.

首先要在准备好实例的学习环境, 参考: 在Windows平台上学习Ruby on Rails 的环境准备

1.it’s easier to throw something away if you didn’t spend a long time creating it.
2.使用笔+纸打草图以加快速度.

以下的命令都是在ruby on rails的命令输入窗进行的.
3.项目代号Depot,创建相应项目:work> rails depot
4.创建mysql数据库depot_development:
depot> mysqladmin -u root create depot_development
注意mysql的用户名和密码.
数据库测试:depot> rake db:migrate
不显示错误提示为正常.
rake db:migrate也用来应用数据库的修改, 以后会经常用到.

下面开始创建后台功能

5.创建model product:depot> ruby script/generate model Product
生成的文件名001_create_products.rb中的001为版本号.
修改db/migrate/001_create_products.rb内容为:
—————————————————————————
class CreateProducts < ActiveRecord::Migration
  def self.up
    create_table :products do |t|
      t.column :title, :string
      t.column :description, :text
      t.column :image_url, :string
    end
  end
  def self.down
    drop_table :products
  end
end
—————————————————————————
使用rake命令应用数据库:depot> rake db:migrate
后面的:string是数据类型,常用的数据类型还有:text,integer,time…

7.创建Controller admin:depot> ruby script/generate controller admin
修改depot/app/controllers/admin_controller.rb内容为:
class AdminController < ApplicationController
 scaffold :product
end
scaffold是rails的一个内建model框架.
现在启动服务器(depot> ruby script/server)调试,后台功能已经基本实现.
调试方法请参看: 网上商店基本构架  Ruby语言入门

这是第一部分, 完成商店的基本构架.

首先要在准备好实例的学习环境, 参考: Ruby on Rails入门 –写第一个程序

8.向数据库追加字段price
生成追加文件:depot> ruby script/generate migration add_price
修改文件depot/db/migrate/002_add_price.rb内容为:
—————————————————————————–
class AddPrice < ActiveRecord::Migration
  def self.up
    add_column :products, :price, :integer, :default => 0
  end
  def self.down
    remove_column :products, :price
  end
end
—————————————————————————-
*可以使用:default=>0为price字段设置默认值.
可用参数:
default=>0设定默认值
null=>false设定不能为空
limit => 10
应用数据库改变:depot> rake db:migrate

9.为数据输入增加校验功能
修改depot/app/models/product.rb为:
——————————————————————————-
class Product < ActiveRecord::Base
  validates_presence_of :title, :description, :image_url
  validates_numericality_of :price, :only_integer => true
  protected
  def validate
    errors.add(:price,"should be positive") if price.nil? || price <= 0
  end

  validates_uniqueness_of :title
  validates_format_of :image_url,
  :with => %r{\.(gif|jpg|png)$}i,
  :message =>"must be a URL for a GIF, JPG, or PNG image"
end
——————————————————————————-
*其中validates_presence_of指定字段输入不能为空.
*validates_numericality_of指定字段为数字(浮点数)
*  protected
  def validate
    errors.add(:price,"should be positive") if price.nil? || price <= 0
  end

  让price字段不为空,且大于0,否则显示"price should be positive"提示.
  protected参见: 网上商店基本构架  Ruby语言入门

这是第一部分, 完成商店的基本构架.

首先要在准备好实例的学习环境, 参考: Ruby语言入门

validate???
*validates_uniqueness_of指定字段不能有重复内容
*validates_format_of和后面的正则表达式限制图片路径指向指定格式的图片文件.
其它:
  validates_length_of :name, :within => 6..100 指定字段长度取值范围
  validates_length_of :content, :minimum => 10  指定长度最小值
  validates_associated :bliki ????

10.以脚本方式向数据库追加内容
创建新migration: depot> ruby script/generate migration add_test_data
修改depot/db/migrate/003_add_test_data.rb文件内容为:
——————————————————————————————-
class AddTestData < ActiveRecord::Migration
  def self.up
    Product.create(:title =>’Pragmatic Version Control’,
    :description =>
%{<p>This book is a recipe-based approach to using Subversion that will get you up and running quickly–and correctly. All projects need’s a foundational piece of any project’s version control: it infrastructure. Yet half of all project teams in the U.S. don’t use any version control at all. Many others don’t use it well, and end up experiencing time-consuming problems.</p>},
    :image_url =>’/images/svn.jpg’,
    :price => 2850)
    # . . .
  end
  def self.down
    Product.delete_all
  end
end
————————————————————————————————
使用rake db:migrate命令应用到数据库

10.美化商品列表
分离出scaffold代码:depot> ruby script/generate scaffold product admin
scaffold为rails提供的内建动态功能,使用上面的语句将代码分离出来,以便修改.
分离过程需要按两次"y"确认.
depot/app/controllers/admin_controller.rb文件被修改
修改depot/app/views/layouts/admin.rhtml文件,将
<%= stylesheet_link_tag ‘scaffold’ %>
修改为:
<%= stylesheet_link_tag ‘scaffold’, ‘depot’ %>
这会将网站使用的CSS文件由默认的scaffold.css指向depot.css文件.
在public/stylesheets目录下新建depot.css文件,内容为:
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
/*Global styles*/
#notice {
border: 2px solid red;
padding: 1em;
margin-bottom: 2em;
background-color:#f0f0f0;
font: bold smaller sans-serif;
}
/*Styles for admin/list */
#product-list .list-title {
color: #244;
font-weight: bold;
font-size: larger;
}
#product-list .list-image {
width: 60px;
height: 70px;
}
#product-list .list-actions {
font-size: x-small;
text-align: right;
padding-left: 1em;
}
#product-list .list-line-even {
background:#e0f8f8;
}
#product-list .list-line-odd {
background:#f8b0f8;
}
/* Styles for main page */
#banner {
background:#9c9;
padding-top: 10px;
padding-bottom: 10px;
border-bottom: 2px solid;
font: small-caps 40px/40px "Times New Roman", serif;
color:#282;
text-align: center;
}
#banner img {
float: left;
}
#columns {
background:#141;
}
#main {
margin-left: 15em;
padding-top: 4ex;
padding-left: 2em;
background: white;
}
#side {
float: left;
padding-top: 1em;
padding-left: 1em;
padding-bottom: 1em;
width: 14em;
background:#141;
}
#side a {
color:#bfb;
font-size: small;
}
h1 {
font: 150% sans-serif;
color:#226;
border-bottom: 3px dotted #77d;
}

/*And entry in the store catalog*/
#store .entry {
border-bottom: 1px dotted #77d;
}
#store .title {
font-size: 120%;
font-family: sans-serif;
}
#store .entry img {
width: 75px;
float: left;
}
#store .entry h3 {
margin-bottom: 2px;
color:#227;
}
#store .entry p {
margin-top: 0px;
margin-bottom: 0.8em;
}
#store .entry .price-line {
}
#store .entry .add-to-cart {
position: relative;
}
#store .entry .price {
color:#44a;
font-weight: bold;
margin-right: 2em;
float: left;
}

/*Styles for the cart in the main page and the sidebar*/
.cart-title {
font: 120% bold;
}
.item-price, .total-line {
text-align: right;
}
.total-line .total-cell {
font-weight: bold;
border-top: 1px solid #595;
}
/* Styles for the cart in the sidebar */
#cart, #cart table {
font-size: smaller;
color: white;
}
#cart table {
border-top: 1px dotted #595;
border-bottom: 1px dotted #595;
margin-bottom: 10px;
}
/*Styles for order form*/
.depot-form fieldset {
background:#efe;
}
.depot-form legend {
color:#dfd;
background:#141;
font-style: sans-serif;
padding: 0.2em 1em;
}
.depot-form label {
width: 5em;
float: left;
text-align: right;
margin-right: 0.5em;
display: block;
}
.depot-form .submit {
margin-left: 5.5em;
}

/*The error box*/
.fieldWithErrors {
padding: 2px;
background-color: red;
display: table;
}

#errorExplanation {
width: 400px;
border: 2px solid red;
padding: 7px;
padding-bottom: 12px;
margin-bottom: 20px;
background-color:#f0f0f0;
}
#errorExplanation h2 {
text-align: left;
font-weight: bold;
padding: 5px 5px 5px 15px;
font-size: 12px;
margin: -7px;
background-color:#c00;
color:#fff;
}
#errorExplanation p {
color:#333;
margin-bottom: 0;
padding: 5px;
}
#errorExplanation ul li {
font-size: 12px;
list-style: square;
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
这个文件包含整站的CSS代码.

修改depot/app/views/admin/list.rhtml文件内容为:
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
<div id="product-list">
 <h1>Product Listing</h1>
 <table cellpadding="5" cellspacing="0">
  <% for product in @products %>
   <tr valign="top" class="<%= cycle(‘list-line-odd’, ‘list-line-even’) %>">
    <td>
     <img class="list-image" src="<%= product.image_url %>"/>
    </td>
    <td width="60%">
     <span class="list-title"><%= h(product.title) %></span><br />
     <%= h(truncate(product.description, 80)) %>
    </td>
    <td class="list-actions">
     <%= 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?",
     :post => true
%>
    </td>
   </tr>
  <% end %>
 </table>
</div>
<%= if @product_pages.current.previous
 link_to("Previous page", { :page => @product_pages.current.previous })
end
%>
<%= if @product_pages.current.next
 link_to("Next page", { :page => @product_pages.current.next })
end
%>
<br />
<%= link_to ‘New product’, :action => ‘new’ %>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
系统自动提供@products变量.
手动创建的方法是:
在controller里字义@products = Product.find_products_for_sale
在Product类(models/product.rb)里定义:
class Product < ActiveRecord::Base
  def self.find_products_for_sale
    find(:all, :order => "title")
  end
  #….
 用 self.前缀,定义class method
*h(string) 用来处理html符号.
*class=cycle(‘样式名1′,’样式名2′)是一个helper method,用于在连续的行之间交互不同的样式.
*truncate(‘字串’,长度数值)用来截取字符
*link_to用法 link_to ‘链接标题’, :action => ‘action名’, :id => product
*link_to ’Destroy’后面的:confirm =>"Are you sure?"会为页面加入一个删除确认提示.
* :post => true强制rails使用post方式传输数据,这个方式比get方式更适合用来做"删除数据"链接

使用http://localhost:3000/admin/ 地址访问调试.

本节结束.
下一节:
网上商店基本构架  Ruby语言入门

这是第一部分, 完成商店的基本构架.

首先要在准备好实例的学习环境, 参考: Ruby on Rails实战–创建一个网上商店B前台

发表在 Ruby on Rails | 一条评论