分類目錄歸檔: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 | 一條評論