Rails隨記

繞了不少彎路,感覺這本書上說的還是比較簡單


rails是作爲ruby的gem進行安裝的,需要注意安裝相關的依賴包
對於ODBC的連接SQL數據庫,需要進行不少的設置,包括控制面板內的管理工具,設置本地的dsn設置

創建rails應用後,可以通過rake db:migrate檢查是否配置完成,也可以使用idea來執行判斷
數據庫database.yml配置如下
development:
adapter: sqlserver
mode: odbc
dsn: CHENHENG
username : sa
password : 123
其他大體如上

controllers 目錄下可以創建對應的c部分,裏面創建的文件命名格式爲
admin_controller.rb
訪問時URL主體部分爲admin/action,
其中action爲方法名,可以通過直接定義def action來訪問自定義的方法
方法執行完,會自動跳轉views/admin/目錄下的對應action.html.erb類似規則的文件

使用rails建立model時,會自動在db/migrate目錄下創建rb文件,可以創建數據庫所需的表信息

動態網頁內容.兩種方式
一:erb,
使用<%=...%>執行ruby,輸出變量,與Jsp標籤類似
使用<%%>執行ruby語句,循環等,注意end的使用 注意其中<% ...-%>格式的使用,似乎用於去除
所產生的空格

如果需要在<%=%>中輸出帶特殊字符的ruby代碼,可以使用
<%= h("Ann & Bill <[email protected]>" ) %>中的h()

獲取action中定義的局部變量的方式@var...
在action中定義的@var變量,可以直接在erb文件中使用<%= @var %>讀取

創建超鏈接的方式, //指向其他的URL,將會忽視對相對和絕對路徑的URL處理

修改rails數據庫環境的方式,切換development等..
rake db:create RAILS_ENV='development'

在使用Idea開發時,需要注意使用scaffold生成腳手架時,參數格式如下
product title:string desc:text
product爲model的名稱,即數據庫中表的名稱映射,一般會被映射成複數s的形式
title爲列名,string爲orm的類型
完整的指令應該爲generate scaffold -f product title:string desc:text image_url:string

在開發時,注意不要存在多個不同版本的gem,idea會默認使用最高版本的gem,忽視原有的配置,
可以使用gem uninstall 進行卸載,包括所依賴的相關版本

爲已存在的model添加屬性,主要目的是爲數據庫添加列,並且增加所需的記錄
generate migration -f add_price_to_product price:decimal
將會生成新的migrate文件,用於記錄新增的記錄,同時也提供了還原的方式
然後運行rake db:migrate 執行語句即可,如果要在對應的html中進行顯示,可以使用
generate scaffold -f product title:string desc:text image_url:string
再次執行生成

添加驗證,
在model中添加驗證的方式
validates_presence_of :title, :description, :image_url //驗證指定的字段非空
validates_numericality_of :price //驗證字段必須爲數字

添加自定義驗證的方式
validate :price_must_be_at_least_a_cent
protected
def price_must_be_at_least_a_cent
errors.add(:price, 'should be at least 0.01' ) if price.nil? ||price < 0.01 //這裏主要是errors的使用
end

通過正則進行定義驗證的方式
validates_format_of :image_url,
:with => %r{\.(gif|jpg|png)$}i,
:message => 'must be a URL for GIF, JPG or PNG image.'

通過數據庫進行驗證
validates_uniqueness_of :title

可以通過建立controller時,可以使用帶參數,同時創建action,並且會自動生成對應的erb頁面

<%=h ... %> 用於轉換內容中的HTML代碼
格式化價格
<%= sprintf("$%0.02f" , product.price) %>

貨幣格式轉換的方式
<%= number_to_currency(product.price) %>

rails提供了兩種用於超鏈接的標籤
button_to 用於生成一個form,並且使用post的提交方法
link_to 用於生成一個簡單的a href,使用get方式

創建基於rails本身的session解決數據存儲方案
rake db:sessions:create //保存在數據庫中
然後執行rake db:migrate ,創建所需的表
在rails2.3中,需要在config/initializers/session_store.rb中配置
Action_controller.session_store = :active_record_store

存儲在數據庫中,主要用於提供了一種跨多機器的存儲環境
建立專屬的controller
application_controller.rb //注意是必須的重寫,內容可以參考框架下的實現,可以直接拷貝

從http請求中獲取參數
params[:key]

Flash域,用於保存臨時顯示的數據,原理與struts2類似,同樣保存在session中,自動刪除
常用於保存錯誤信息

記錄錯誤信息與flash的使用
logger.error("Attempt to access invalid product #{params[:id]}" )
flash[:notice] = "Invalid product"
redirect_to :action => 'index" //URL跳轉,重定向,可以通過重構抽取相同的內容到方法中

讀取flash的時候,加入if判斷是否存在會更好一些
<% if flash[:notice] -%>
<div id="notice"><%= flash[:notice] %></div>
<% end -%>

判斷指定域中對象是否爲空的方式
if session[:counter].nil? ,也可以採取||=進行縮寫

在頁面中使用標籤導入其他erb內容,類似jsp的include
<%= render(:partial => "cart_item" , :collection => @cart.items) %>
同時在目標中,可以直接包含循環體內容,而不需要重新循環讀取
直接集合了c:foreach和include
其中partial爲var ,collection爲items. 不過功能應該更豐富一些
其中特殊的一點是,只要將需要嵌入的文件命名爲_cart_item.html.erb即可自動尋找到,與partial屬性相關

:object => @cart 用於傳入一個對象

生成一個Ajax請求的標籤
<% form_remote_tag :url => { :action => 'add_to_cart', :id => product } do %>
<%= submit_tag "Add to Cart" %>
<% end %>

rjs文件,用於生成所需的js模板文件,使用上與erb類似
在layout文件中,加入以下標籤,用於導入默認所需的js文件
<%= javascript_include_tag :defaults %>

在rjs模板中同樣適用標籤進行聲明
page.replace_html("cart" , :partial => "cart" , :object => @cart)
表示替換調用頁面中id=cart元素中的html內容

使用內置的effects.js 特效 其中:current_item 爲html中元素的id
page[:current_item].visual_effect :highlight,
:startcolor => "#88ff88",
:endcolor => "#114411"
上述代碼同樣定義在rjs文件中

helpers/ 目錄中保存了與controller同名的module文件,用於爲對應的controller提供更多功能
將會被自動include,不需要手動編寫

在進行渲染時,可以通過判斷選擇不同的方式
respond_to do |format|
format.js if request.xhr?
format.html {redirect_to_index}
end

在rails建立一對多的主外鍵關聯
在module中添加
一方:has_many :line_items 後者爲其他model
多方:belongs_to :order 後者同上

application_controller 作用類似web.xml,用於用代碼代替配置工程中的屬性
before_filter :authorize, :except => :login 配置過濾器
其中的anthorize
protected
def authorize
unless User.find_by_id(session[:user_id])
flash[:notice] = "Please log in"
redirect_to :controller => 'admin' , :action => 'login'
end
end

:except 用於設置例外的界面或請求

創建基於xml的渲染器,類似erb,rjs
respond_to do |format|
format.html //會根據http header中Accept:內容的不同,自動選擇不同的請求render
format.xml { render :layout => false } //false用於關閉使用layout模板
end
擴展名如下,使用時候比較特殊一些
who_bought.xml.builder
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

其他情況下,根據路由routes.rb的配置,可以直接使用擴展名,返回不同的渲染
map.connect ':controller/:action/:id.:format

使用內置自動生成xml文件
format.xml { render :layout => false ,:xml => @product.to_xml(:include => :orders) }
調用model的to_xml方法

生成指定格式的feed xml格式文件
format.atom { render :layout => false }
生成JSON 的格式,針對http header請求
format.json { render :layout => false ,:json => @product.to_json(:include => :orders) }

生成文檔
rake doc:app

i18n/test跳過,不研究

rails項目的目錄結構

doc/ 用於存放項目的說明文檔
lib/ 可以用於放置一些rb類,可以被controller,view,model中引入,注意需要加上目錄
lib/tasks, 用用於放置自定義的take任務
log/用於放置日誌信息
public/放置一些靜態文件,css,js等,以及404等錯誤html頁面
script/開發用的腳本
vendor/存放第三方插件,代碼,用於採用非全局的gem等,
使用項目gem rake rails:freeze:gems
使用系統gem rake rails:unfreeze
其他rake rails:freeze:edge
config / 目錄 , 用於存放一些配置文件,數據庫,環境等
ruby script/server -e development //切換運行環境,參數還包括test,production
config/database.yml 數據庫連接的配置

Active Support
主要爲model中的類,rails爲其擴展了許多方便的方法

對於集合的操作,繼承了Enumerable中的方法
groups = posts.group_by {|post| post.author_id}
state_lookup = us_states.index_by {|state| state.short_name}
total_orders = Order.find(:all).sum {|order| order.value }
rails也對Array進行了擴展
puts [ "ant" , "bat" , "cat" ].to_sentence #=> "ant, bat, and cat"

對String的擴展也很多...除了字符的截取,還提供了一些英文 單數,複數的轉換,大小寫格式化
擴展自己的語法轉換規則
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular "goose" , "geese"
end

對數字同樣也提供了擴展
提供了字節,時間的便捷操作

時間和日期的擴展
date = Date.today
puts date.tomorrow
以及更多的日期格式化獲取

對Ruby語法的擴展,如方法調用
groups = posts.group_by {|post| post.author_id}
轉化成
groups = posts.group_by(&:author_id) , 節約了臨時對象的編寫

Migrations
Rails中使用對Migration的維護,來對數據庫進行DDL操作,同時對每次的處理,都使用單獨的文件進行
保存,便於版本的控制,其中也會將當前項目的db信息保存在表schema_migrations table中

最常見的執行 rake db:migrate,將會比對當前數據庫表中與文件的區別,進行執行

對db版本的控制
rake db:migrate VERSION=20080601000010 //將版本調整至指定版本,
rake db:migrate:redo STEP=3 //效果一致

常見的列類型,使用在Migration文件中的add_column:屬性中
:binary, :boolean, :date, :datetime, :decimal,:float, :integer, :string, :text, :time, and :timestamp.
如:add_column :表名, :column名, :類型

對列的屬性設置
:null => true or false //是否允許空
:limit => size //列的長度
:default => value //默認值
decimal類型時候,:precision and :scale 用於設置精度

重命名列
rename_column :orders, :e_mail, :customer_email
修改列屬性
change_column :orders, :order_type, :string

重命名錶,注意對原有的model代碼也會修改
rename_table :order_histories, :order_notes

當添加一些不允許修改的表信息時,可以在self.down中拋出異常,防止錯誤的修改,導致數據丟失
def self.down
raise ActiveRecord::IrreversibleMigration
end

對錶的創建,注意block的使用
def self.up
create_table :order_histories do |t|
t.integer :order_id, :null => false
t.text :notes
t.timestamps
end
end

定義索引,這裏並不很瞭解,建議需要時查看API
add_index :orders, :name,:unique => true
orders 爲表名稱
:name => "somename" 用於指定類名,默認爲
unique表示不允許重複

rails創建表時,會自動創建名稱爲id的主鍵,也可以自定義
create_table :tickets, :primary_key => :number do |t|...end

創建不包含主鍵的表
create_table :authors_books, :id => false do |t|..end

可以通過在migration中創建對數據庫的增加數據操作,來爲測試提供便利,不過感覺數據可能會因此被
固化在migration中,違反了原來的初衷

Loading Data from Fixtures
可以通過定義一些保存數據的yml文件,專門保存數據的內容,然後使用migration進行加載
def self.up
down //調用當前的down操作,用於清除數據
directory = File.join(File.dirname(__FILE__), 'dev_data' ) //dev_data用於保存數據yml文件的目錄
Fixtures.create_fixtures(directory, "users" )//加載該目錄下的users.yml文件
end
def self.down
User.delete_all
end

注意需要引入所需的類
require 'active_record/fixtures'
yml中的格式如下
mike:
name: Mike Clark
status: admin

不過上訴方式,只建議用於加載在開發環境和生產環境下都需要使用的數據,如果要用於加載測試數據,可以用
rake的任務實現

一些時候,比如在修改列的類型,也可以通過在數據庫中更新列值,來避免有可能出現的數據丟失
Product.update_all("price = price * 100" )
change_column :products, :price, :integer

使用原生的sql語句
create_table :line_items do |t|
t.integer :product_id, :null => false, :options =>
"CONSTRAINT fk_line_item_products REFERENCES products(id)"

execute %{
CREATE TRIGGER #{constraint_name}_delete
BEFORE DELETE ON #{to_table}
FOR EACH ROW BEGIN
SELECT
RAISE(ABORT, "constraint violation: #{constraint_name}" )
WHERE
(SELECT id FROM #{from_table} WHERE #{from_column} = OLD.id) IS NOT NULL;
END;
}

對model類的控制
class Sheep < ActiveRecord::Base
set_table_name "sheep" #手動設置表名
end
也可以使用self.table_name = "sheep"

在model中也比較特別的一點,就是在model中並不能看到表中的屬性,也就是看不到所對應的映射屬性,當卻可以被正常使用和顯示,因爲在創建migration時,rails將會自動保存這些信息,如果直接使用scaffold時,也可以自動創建所有的信息.所以並不會重複在model class中顯示,包括id

如果需要添加屬性,可以通過新建migration來使用
可以通過在命令臺執行
ruby script/console
Order.column_names 來查看Order model內的列名

對於Boolean值的使用.因爲數據庫本身的有些支持Boolean,有些則需要使用int或char代替
建議使用user.superuser?進行判斷,注意?的使用
然後在model定義該方法,superuser,返回對內部字符的判斷返回結果,而不直接依賴數據庫中屬性

自定義主鍵列,默認會使用自增的Integer作爲主鍵列,並且使用id進行索引
class LegacyBook < ActiveRecord::Base
self.primary_key = "isbn"
end
在設置值時,需要使用.id進行設置值,當訪問時,使用設置的列名進行訪問,使用id來讀,使用列名來取,並且保持唯一

在model中類,因爲都是繼承ActiveRecord類中,所以也會默認使用你所配置的數據庫連接
也可以在model中進行覆蓋
establish_connection(
:adapter => "mysql" ,
:host => "dbserver.com" ,
:database => "backend" ,
:username => "chicho" ,
:password => "piano" )

CRUD操作
使用model類的
Model.new創建一個新的對象,設置屬性後,調用save就可以保存
也可以使用block的方式
Order.new do |o|
o.name = "Dave Thomas"
# . . .
o.save
end

也可以通過hash的形式構建對象,在調用save更新到數據庫
an_order = Order.new(
:name => "Dave Thomas" ,
:email => "[email protected]" ,
:address => "123 Main St" ,
:pay_type => "check" )
an_order.save

與Hibernate類似,save後,該對象也自動會有id出現

從表單參數中直接創建對象
order = Order.new(params[:order]) //form名字爲order

使用create保存對象,無須調用save方法
an_order = Order.create(
:name => "Dave Thomas" ,
:email => "[email protected]" ,
:address => "123 Main St" ,
:pay_type => "check" )
其實這裏的save方法可以理解成hibernate中的saveorupdate方法

create方法可以從array中直接保存一組對象到數據庫中
orders = Order.create(
[ { :name => "Dave Thomas" ,
:email => "[email protected]" ,
:address => "123 Main St" ,
:pay_type => "check"
},
{ :name => "Andy Hunt" ,
:email => "[email protected]" ,
:address => "456 Gentle Drive" ,
:pay_type => "po"
} ] )

查找對象的方式
Order.find(27) //使用id進行搜索
product_list = params[:product_ids]
Product.find(product_list).sum(&:price) 從一組id中獲取對象,並計算其中的price屬性
Person.find(:first, :conditions=>"name=’Dave’") 帶條件的find
Give me the first person row that has the name Dave.”
也可以使用:all代替first,表示搜索全部
conditions中帶上and ,可以附帶更多的搜索條件,其實也就是在sql中帶上where從句,注意引號的使用

在參數中,使用#{}帶上ruby變量
:conditions => "name = '#{name}' and pay_type = 'po'"
注意以上方法,有可能導致注入的發生

使用?的形式,設置參數
pos = Order.find(:all,:conditions => ["name = ? and pay_type = 'po'" , name])
使用key的形式,設置參數
pos = Order.find(:all,:conditions => ["name = :name and pay_type = :pay_type" ,{:pay_type => pay_type, :name => name}])

注意這裏的conditions的兩種形式字符串與[]數組,其中數組形式的就省去and的使用.會自動進行組合

使用like子句
User.find(:all, :conditions => ["name like ?" , params[:name]+"%" ]).
不能直接使用?+字符串的%拼接

find()中的其他參數
:order sql中的order子句字符串
:limit 在使用:all時,限制返回的數量
:offset 用於分頁時,可以限制返回的數據開始的位置:offset => page_num*page_size
:joins sql中的join子句
:joins => "as li inner join products as pr on li.product_id = pr.id"
注意:model提供了許多對外鍵操作的映射方式,所以並不需要經常自定義joins
:select 返回需要的列,其實也就是sql中的select中的列
:select => "*, a.name" //用於多表join查詢時
:readonly 只讀的查詢方式
:from 代替table表名的查詢方式
:group sql的分組子句 :group => "sku"
:lock 設置鎖,太麻煩,不研究

使用自定義的sql語句
orders = LineItem.find_by_sql("select line_items.* from line_items, orders where order_id = orders.id and orders.name = 'Dave Thomas' " )

只要寫出標準的格式,rails會自動完成映射,也可以理解成封裝成hash的形式,訪問時直接使用order.列名進行訪問即可

也可以擴展成帶參數形式的sql
Order.find_by_sql(["select * from orders where amount > ?" ,params[:amount]])

還提供了對多種列的統計
average = Order.average(:amount) # average amount of orders
max = Order.maximum(:amount)
min = Order.minimum(:amount)
total = Order.sum(:amount)
number = Order.count

注意ruby中方法調用時,可以省略掉()
result = Order.maximum :amount,
:group => "state" ,
:limit => 3,
:order => "max(amount) desc"

count的擴展
result1 = Order.count ["amount > ?" , minimum_purchase]
Order.count :conditions => "amount > 10" ,:group => "state"
也可以使用自定義的sql語句,注意方法的不同
count = LineItem.count_by_sql("sql...")

動態方法查詢,rails提供了多種語義豐富的方法,便於理解和使用
order = Order.find_by_name("Dave Thomas" )
orders = Order.find_all_by_name("Dave Thomas" )
orders = Order.find_all_by_email(params['email' ])

使用!感嘆號,用於在查找不到對象時,拋出異常,而不是返回nil
order = Order.find_by_name!("Dave Thomas" )

更豐富的動態方法
user = User.find_by_name_and_password(name, pw) #多列條件查詢
還有更多語義豐富的方法
User.find_all_by_name
Cart.find_or_initialize_by_user_id(user.id)

可以通過在model內部添加name_scope,爲model添加更多的動態方法
named_scope :last_n_days, lambda { |days| :condition =>['updated < ?' , days] }
調用:orders = Orders.last_n_days(7)

重載加載數據庫對象,用於讀取出一個對象後,更新該對象爲最新的數據庫中的屬性
Object.reload

更新數據
save方法已經介紹,會自動判斷新建還是更新
update屬性方法,使用hash的參數形式
order.update_attributes(:name => "Barney",:email => "[email protected]" )

常用的還是update 和update_all方法
order = Order.update(12, :name => "Barney" , :email => "[email protected]" ) #,12爲id
result = Product.update_all("price = 1.1*price" , "title like '%Java%'" )

save與create方法的!版本
失敗都會拋出異常,成功則返回true與完整的record對象

刪除數據
Order.delete(123) #id
User.delete([2,3,4,5]) #id
Product.delete_all(["price > ?" , @expensive_price]) #條件
order.destroy //單個對象,實例級別方法
Order.destroy_all(["shipped_at < ?" , 30.days.ago]) //類級別方法

將Ruby對象序列化到數據庫中
class Purchase < ActiveRecord::Base
serialize :last_five #聲明爲存儲序列化數據
# ...
end
寫入
purchase.last_five = [ 'shoes' , 'shirt' , 'socks' , 'ski mask' , 'shorts' ]
purchase.save
讀取時,將會自動轉換成ruby的數組對象,注:列的類型最好爲text

將一張表存放在不同的對象之中
class Customer < ActiveRecord::Base
composed_of :name, :class_name => 'Name' , ...
end
其中name爲單獨的一個class,並不繼承activerecord類
注意其構造函數和內置屬性,必須能否與表名內的列名對應
def initialize(first, initials, last)
@first = first
@initials = initials
@last = last
end

也可以手動指定所需的列名與字段的映射
composed_of :name,:class_name => "Name" ,
:mapping =>[ # database ruby
%w[ first_name first ], #列名,屬性名
%w[ initials initials ],
%w[ last_name last ]
]

使用底層的sql連接,來執行sql,並且返回結果
res = Order.connection.select_all("select id, quantity*unit_price as total from line_items" )
將會返回hash形式的對象數組

注意使用自定義的sql查詢時,默認將不會返回id,有此可能造成id確實,update變成create

注意一些rails在table中定義的保留字段的列
created_at, created_on, updated_at, updated_on //記錄相關日期
lock_version //鎖
id //默認生成的主鍵
xxx_id //外鍵中使用
xxx_count //保存子表的字段數目
position //acts_as_list使用時
parent_id //acts_as_tree使用時
type //單表繼承使用時

rails也提供了一系列的方法,跟蹤對象的變化情況..changed?,name_change等.

類似hibernate,rails也提供了查詢的緩存

在rails中也可以通過在表中手動創建_id的外鍵列方式,來維護主外鍵關係,注意可以通過創建index,來提高join查詢時的性能

常見的關係配置
One-to-One
A:belongs_to :order
B:has_one :invoice

One-to-Many
A:belongs_to :order #一方
B:has_many :line_items #多方

Many-to-Many
A:has_and_belongs_to_many :products
B:has_and_belongs_to_many :categories
無須關心中間表的配置

belongs_to,用於在多方設置,或者理解成外鍵一方
Declaration in child Foreign Key Parent Class Parent Table
belongs_to :product product_id Product products
belongs_to :invoice_item invoice_item_id InvoiceItem invoice_items
belongs_to :paid_order,
:class_name => "Order" ,
:foreign_key => "order_id" ,
:conditions => "paid_on is not null"

has_one: 在一方設置,屬於一對一關係
Declaration Foreign Key Target Class Target Tabl
has_one :invoice order_id Invoice invoices
可以使用 :dependent => :destroy 設置級聯

has_many:用於設置在一方,表示擁有多個元素
Declaration Foreign Key Target Class Target Table
has_many :line_items order_id LineItem line_items

高級設置,可以配置sql查詢的方式
has_many :rails_line_items,
:class_name => "LineItem" ,
:finder_sql => "select l.* from line_items l, products p " +
" where l.product_id = p.id " +
" and p.title like '%rails%'"
還可以設置
:order => "quantity, unit_price DESC"
多表查詢時,可以使用:uniq => true屬性,來防止出現重複的數據
或者:select => "distinct users.*"

對象生命週期
Object Life Cycle

Validation 驗證
在model進行save等操作時,都會自動執行驗證
也可以自定義進行設置,不同情況下驗證的方法
class User < ActiveRecord::Base
validate :valid_name?
validate_on_create :unique_name?

private
def valid_name?
unless name && name =~ /^\w+$/
errors.add(:name, "is missing or invalid" )
end
end
進行驗證的時候,注意判斷的方法,可以用使用帶?的方法,將返回true,而不會跑出異常或者nil

內置的一些驗證helper方法
格式驗證
validates_format_of :name,
:with => /^\w+$/,
:message => "is missing or invalid
唯一驗證
validates_uniqueness_of :name,
:on => :create, #表示開啓時機
:message => "is already being used" #錯誤信息
驗證被選擇
validates_acceptance_of :terms,
:message => "Please accept the terms to proceed"
對相關對象進行驗證
validates_associated :line_items,
:message => "are messed up"
validates_associated :user
驗證指定格式字段的值相同, //常用於密碼驗證
validates_confirmation_of :password
注意html的name屬性
<%= password_field "user" , "password" %><br />
<%= password_field "user" , "password_confirmation" %><br />
循環驗證屬性 使用block
validates_each :name, :email do |model, attr, value|
if value =~ /groucho|harpo|chico/i
model.errors.add(attr, "You can't be serious, #{value}" )
end
end
對集合中內容進行過濾驗證
validates_exclusion_of :genre,
:in => %w{ polka twostep foxtrot },
:message => "no wild music allowed"
格式驗證,也是使用正則
validates_format_of :length, :with => /^\d+(in|cm)/
驗證集合必須包含內容
validates_inclusion_of :gender,
:in => %w{ male female },
:message => "should be 'male' or 'female'"
驗證長度
validates_length_of :name, :maximum => 50
validates_length_of :password, :in => 6..20
驗證數據格式
validates_numericality_of :age, :only_integer => true
validates_numericality_of :height_in_meters
非空的屬性驗證
validates_presence_of :name, :address
驗證大小,與length長度使用一致
validates_size_of
驗證值的唯一
validates_uniqueness_of :name, :scope => "group_id" //可以設置驗證的域

Callbacks 回調函數,也可以理解成監聽ActiveRecord操作後,自動執行的方法
使用的方式,只要在class中重載這些方法即可, 如:
def before_save
self.payment_due ||= Time.now + 30.days
end

也可以使用類似validate的方式,使用自定義的函數
before_validation :normalize_credit_card_number
protected //注意域
def normalize_credit_card_number
self.cc_number.gsub!(/[-\s]/, '' )
end

也可以使用
after_create do |order|
logger.info "Order #{order.id} created"
end

Observers的使用
ActiveRecord::Observer
可以用於監視指定的model類,而無須寫入在model中,也提供了複用
根據約定大於配置
OrderObserver將會自動作用於名稱爲Order的類
如果需要手動控制同時控制多個類,可以使用以下方法
observe Order, Payment, Refund

默認情況下 observer類也放置在model目錄下

默認情況下,Active record對象執行查詢時,會用hash的方式從數據庫中得到對象,再封裝到column對象中,如果要自己定義查詢時,如.find_by_sql,那麼返回的將會是以數組形式保存的hash對象,訪問的時候,默認hash的key與sql中的列名相關,也可以使用as語句來進行簡化

緩存對象,具體用途不知
def length
# ...
end
memoize :length

Transactions 事務
事務的使用 ,手動調用方式
Account.transaction do
account1.deposit(100)
account2.withdraw(100)
end
樂觀鎖 省略....

Action Controller: Routing and URLs

默認的路由設置 Routing Requests
config/routes.rb 文件中 ,如果符合路由設置,將會默認將參數對應的保存在@params變量中,使用hash方式

自定義路由規則時,可以使用script/console 進行測試
depot> ruby script/console
>> rs = ActionController::Routing::Routes
>> rs.recognize_path "/store"
將會打印出匹配的route規則

如果更新文件後,需要使用下面命令進行加載
>> load "config/routes.rb"

map.connect用於設置單條規則,使用方法參數的方式設置參數
在route中,設置了一些默認值,用於對不完整URL的補充
defaults => { :action => "index" , :id => nil }

必須匹配規則
:requirements => { :name =>/regexp/, ...}
條件
:conditions => { :name =>/regexp/orstring, ...}

例子
map.connect 'store/checkout' ,
:conditions => { :method => :get },
:controller => "store" ,
:action => "display_checkout_form"
也可以進一步對匹配的內容,進行再驗證
map.connect "blog/:year/:month/:day" ,
:controller => "blog" ,
:action => "show_date" ,
:requirements => { :year => /(19|20)\d\d/,
:month => /[01]?\d/,
:day => /[0-3]?\d/},
:day => nil,
:month => nil

生成符合指定route規則的url
@link = url_for(:controller => "store" , :action => "display" , :id => 123)

其他的依據路由設置,來便捷實用url的方式
redirect_to(:action => 'delete' , :id => user.id)

可以方便的爲model類添加route的rest效果
map.resources :articles # articles爲model類名

Rails中controller的方法定義

index 返回model的集合,包含多個對象
create 創建一個model對象,保存到數據庫中 POST
new 創建一個新資源,當並不保存到數據庫中,返回給客戶端進行填充
show 根據唯一的條件,返回符合該條件的唯一對象model
update根據id更新自定內容 _POST
edit 返回根據ID提取出來的內容,填充到界面的編輯表單中
destory 根據id,銷燬對象

上訴的操作,都能包含了簡單的CRUD操作

route中限制controller中action的方式
map.resources :comments, :except => [:update, :destroy]

這裏還是回顧一下.使用內置的scaffold生成包括model在內的rest服務
ruby script/generate scaffold article title:string summary:text content:text

controller中創建的環境變量
action_name 當前所處的action名稱
cookies cookie中的內容,可以進行讀寫
headers 一組用hash保存的http請求中的頭信息,只用於返回使用,不用來編寫cookie
params 保存提交上來的參數,form and url
request 獲取請求的信息,包括http方法等
可以用於訪問 protocol://host:port/path?query_string. 對應的所有屬性
也可以只用其中的屬性,獲取客戶端信息 request.env['HTTP_ACCEPT_LANGUAGE' ]
包括大量屬性的使用,建議有需要的時候直接查看API
response ,保存http請求返回內容
session, 會話

填充模板的方法
render() 方法,用於執行填充操作,如果不附帶參數,將會自動填充對應的action的模板界面
render(:text =>string) ,使用text爲key,添加指定的內容要模板中
render(:inline =>string, [ :type =>"erb"|"builder"|"rjs" ], [ :locals =>hash] )
添加更詳細的參數,可以選擇要填充的模板類型
render(:action =>action_name) 調用其他的action模板,注意不會調用其他的action方法
render(:file =>path, [ :use_full_path =>true|false], [:locals =>hash]) 填充指定的模板
render(:template =>name, [:locals =>hash] ) 轉發向指定的action,跨controller
render(:partial =>name, ...) 填充部分模板
render(:nothing => true) 返回空內容給瀏覽者
render(:xml =>stuff ) 填充指定的內容爲xml,同樣會設置Application/xml
render(:json =>stuff, [callback =>hash] ) ,返回內容給json,當後面函數未知
render(:update) do |page| ... end 使用block的方式填充一個類似rjs的模板
render還附帶了一些通用的參數 :status, :layout, and :content_type
:status,用於返回http狀態碼,正常的爲200,不建議返回30x
:layout 布爾值,用於設置是否使用模板,似乎還能用於選擇其他佈局文件
:content_type 設置Content-Type HTTP header

返回其他內容,或者字符的方式
send_data
send_data(data, options...)
如:send_data(png_data, :type => "image/png" , :disposition => "inline" )

send_file
send_file(path, options...) 將指定的文件發送給客戶端
send_file("/files/secret_list" )

Redirects 重定向, 常見的http status code 爲301, or 307
redirect_to(:action => 'display'), 如果要傳遞變量,可以使用flash域進行保存與讀取

redirect_to("/help/order_entry.html") 跳轉指定的url
redirect_to(:action => ..., options...) 跳轉指定的action
返回上一頁的請求 ----HTTP_REFERER中獲取信息
redirect_to(:back)

修改頭信息,並重定向
headers["Status" ] = "301 Moved Permanently"
redirect_to("http://my.new.home" )

Cookies and Sessions

操作cookie的例子
cookies[:marsupial] = { :value => "wombat" ,:expires => 30.days.from_now,:path => "/store" }

可以在controller中,對session進行設置
session :off, :only => %w{ fetch_rss fetch_atom }

rails中有多種對session的存儲機制
session_store = :cookie_store 默認的存儲機制,在2.0後使用特別的格式進行存儲,可以保存任何對象,限制爲4k的存儲上限

session_store = :p_store
存儲在一個flat文件中,使用PStore format格式,可以在environment.rb文件中進行設置文件保存目錄
config.action_controller.session_store = CGI::Session::PStore
config.action_controller.session_options[:tmpdir] = "/Users/dave/tmp"
config.action_controller.session_options[:prefix] = "myapp_session_"

session_store = :active_record_store
存儲在數據庫中
rake db:sessions:create //在數據庫中創建所需的表

session_store = :drb_store
使用Ruby的DRb協議,允許在ruby進程之間共享對象,存儲在drb服務中,獨立web服務

session_store = :mem_cache_store
存儲在memcached中

session_store = :memory_store
存儲在內存中,對於ruby並不是一個好的處理方法

session_store = :file_store
存儲在文件中,但只能保存字符串

可以在controller或者action中 開關session的使用
session :off
也可以附帶更多參數
session :off, :only => [ :show, :list ]
session :off, :if => proc { Time.now.wday == 0 }

Flash: Communicating Between Actions
flash,用於在action之間傳遞參數,是指在重定向操作時,可以將對象保存在flash中進行傳遞
flash.keep() 可以將flash內的值持久保存在session中,可以帶參數保存指定的key對象

Filters and Verification 過濾與驗證
rails支持三種類型的過濾器before, after, and around,都是使用def的方法,經過配置規則進行調用

Before and After Filters 使用
before_filter :authorize, :except => :login
protected
def authorize
unless User.find_by_id(session[:user_id])
flash[:notice] = "Please log in"
redirect_to :controller => 'admin' , :action => 'login'
end
end

也可以通過block與class的形式,設置filter
before_filter do |controller|
logger.info("Processing #{controller.action_name}" )
end

after_filter AuditFilter
class AuditFilter
def self.filter(controller)
AuditLog.create(:action => controller.action_name)
end
end

after filter常用於修改返回的內容,比如壓縮返回的字符,修改頭信息

Around Filters
around_filter :time_an_action
def time_an_action
started = Time.now
yield //用於執行代碼塊
elapsed = Time.now - started
logger.info("#{action_name} took #{elapsed} seconds" )
end

同樣也可以使用class和block的形式進行設置
這裏使用class實例的設置方式
class TimingFilter
def filter(controller)
started = Time.now
yield
elapsed = Time.now - started
controller.logger.info("#{controller.action_name} took #{elapsed} seconds" )
end
end
around_filter TimingFilter.new //注意這裏使用的是實例

當同時使用兩個around_filter時, 執行的順序爲 1 2 .. 2 1

filter可以被子類繼承,也可以被重寫覆蓋

Verification驗證
區別於對於字段屬性的驗證, 是用於權限驗證的機制
verify :only => :post_comment, #action 名稱
:session => :user_id, #session中的key
:add_flash => { :note => "You must log in to comment" }, #錯誤信息,添加到flash中
:redirect_to => :index #重定向的action 名稱

過濾設置
:only =>:name or [ :name, ... ]
Verifies only the listed action or actions.
:except =>:name or [ :name, ... ]
Verifies all actions except those listed.

可以進行判斷的內容
:flash =>:key or [ :key, ... ]
:method =>:symbol or [ :symbol, ... ] #http 請求方法
:params =>:key or [ :key, ... ] #請求的參數
:session =>:key or [ :key, ... ]
:xhr => true or false #是否爲ajax請求

Actions 添加值進行傳遞
:add_flash =>hash
:add_headers =>hash #均爲:key=>value
:redirect_to =>params #使用指定的hash進行重定向
:render =>params #使用指定參數進行渲染設置

Caching 緩存
rails提供了三種緩存 page caching , action caching ,fragment caching,

設置的方式也很簡單,只要添加action即可
caches_page :public_content
caches_action :premium_content

默認情況下,只有在production環境下才開啓緩存,可以通過設置調整
ActionController::Base.perform_caching = true | false
config.action_controller.perform_caching = true

對於page的緩存,使用基於url的機制控制頁面緩存
清理緩存的方式
expire_page :action => "public_content"
expire_action :action => "premium_content" , :id => article

配置緩存策略
多服務器情況下,使用一臺專門的服務器放置緩存文件,通過局域網進行讀取
ActionController::Base.cache_store = :file_store, "#{RAILS_ROOT}/cache"

sweeper 用於專門清理緩存內容的類
app/sweepers:
導入使用:
cache_sweeper :article_sweeper,
:only => [ :create_article,
:update_article,
:delete_article ]

設置客戶端緩存
在controller中可以設置Expires header
expires_in 20.minutes
expires_in 1.year

相應的再Apache中,也需要通過配置對於的擴展名添加緩存的http header
ExpiresActive On
<FilesMatch "\.(ico|gif|jpe?g|png|js|css)$">
ExpiresDefault "access plus 1 year"
</FilesMatch>

LastModified and ETag Support
304 Not Modified
不過似乎用於只讀的使用,沒有太大意義,忽略之

使用get獲取數據,使用post提交數據,注意方法的使用

Action View
填充的方式
render(:action => 'fake_action_name' )
render(:template => 'controller/name' )
render(:file => 'dir/template' )

rails支持三種模板
builder 用於構建xml的返回內容
erb 用於生成html內容
rjs 用於生成JavaScript內容

environment.rb中配置如下,用於修改erb界面中,設置標籤的類型 默認爲>
<% 3.times do %>
Ho!<br/>
<% end %>

config.action_view.erb_trim_mode = "%"

% 5.downto(1) do |i|
<%= i %>... <br/>
% end

對encode後的參數讀取
<%=h params[:name] %> #h的使用

Helpers 的使用,針對controller,可以將部分方法抽取到對於的helper後,直接在頁面上使用,減少標籤的代碼

module StoreHelper
def page_title
@page_title || "Pragmatic Store"
end
end

<h3><%= page_title %></h3>

rails也提供了大量內置的helper,主要爲格式化字符串提供了便利
如:
<%= number_to_percentage(66.66666, :precision => 1) %>
66.7%

在開發環境下 也可以使用debug獲取參數的信息
<%= debug(params) %>

Linking to Other Pages and Resources 連接其他資源,提供了一些便利的方法
<%= link_to "Add Comment" , :action => "add_comment" %>

<%= link_to "Delete" , { :action => "delete" , :id => @product},
{ :class => "dangerous" ,
:confirm => "Are you sure?" ,
:method => :delete}
%>
提供了很詳細的設置

絕對路徑的連接
<%= link_to("Help" , "http://my.site/help/index.html" ) %>

還包括了對mail,css等文件的鏈接生成方式

可以通過安裝'will_paginate的gem,爲rails添加分頁功能

表單的使用 ,(多種)
form_for
<% form_for :product, :url => { :action => :create } do |form| %>
<p>Title: <%= form.text_field :title, :size => 30 %></p>
<p>Description: <%= form.text_area :description, :rows => 3 %></p>
<p>Image URL: <%= form.text_field :image_url %></p>
<p>Price: <%= form.text_field :price, :size => 10 %></p>
<%= form.select :title, %w{ one two three } %>
<p><%= submit_tag %></p>
<% end %>

<% form_for :product,
:url => { :action => :create, :id => @product },
:html => { :class => "my_form" , :method => :put } do |form| %>

表單字段
Text
form.text_field(:attribute, options)
form.hidden_field(:attribute, options)
form.password_field(:attribute, options)

Text Areas
form.text_area(:attribute, options)

Radio Buttons
form.radio_button(:attribute, tag_value, options)

Checkboxes
form.check_box(:attribute, options, on_value, off_value)

Selection Lists
form.select(:attribute, choices, options, html_options)
如:
<% form_for :user do |form| %>
<%= form.select(:name, %w{ Andy Bert Chas Dave Eric Fred }) %>
<% end %>
也提供了內置對時間等組件

Multiple Models in a Form
顯示model的值
<% form_for :user do |form| %>
Name: <%= form.text_field :name %>

通過建立model級別的表關係,可以直接在form中對多表進行直接的處理

scaffold.css文件保存了許多樣式設置,可以通過重寫,或者自定義的覆蓋,來達到自定義的效果

rails 使用Layouts,最大程度的減少了常見的頁面內容重複
<%= yield :layout %> 用於在layout中加載內容

默認會使用同名的layout,也可以在controller中進行自定義
layout "standard"
layout "standard" , :except => [ :rss, :atom ]
通過方法,動態選擇layout
def determine_layout
if Store.is_closed?
"store_down"
else
"standard"
end
end

不適用layout的填充
render(:partial => 'article' ,
:object => @an_article,
:locals => { :authorized_by => session[:user_name],
:from_ip => request.remote_ip })

使用layout的填充
<%= render :partial => "user" , :layout => "administrator" %>

共享的填充模板
<%= render("shared/header" , :title => @article.title) %>
<%= render(:partial => "shared/post" , :object => @article) %>

部分頁面的緩存設置
<% cache do %> <!-- Here's the content we cache -->
<ul>
<% for article in Article.find_recent -%>
<li><p><%= h(article.body) %></p></li>
<% end -%>
</ul>
<% end %>

清理片段緩存的方式
<% cache(:action => 'list', :part => 'articles') do %>
<ul>
<% for article in @articles -%>
<li><p><%= h(article.body) %></p></li>
<% end -%>
</ul>
<% end %>
清理:expire_fragment(:action => 'list' , :part => 'articles' )

緩存機制選項
ActionController::Base.cache_store = <one of the following>
:memory_store,
:file_store, "/path/to/cache/directory"
:drb_store, "druby://localhost:9192"
:mem_cache_store, "localhost" ActionController::Base.cache_store =MyOwnStore.new("parameter")

標籤生成的form格式
<input type="text" name="user[password]" />

ssl_requirement gem用於提供ssl的https連接


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章