ruby

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 <frazers@isp.email>" ) %>中的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 => "dave@pragprog.com" ,
: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 => "dave@pragprog.com" ,
:address => "123 Main St" ,
:pay_type => "check" )
其实这里的save方法可以理解成hibernate中的saveorupdate方法

create方法可以从array中直接保存一组对象到数据库中
orders = Order.create(
[ { :name => "Dave Thomas" ,
:email => "dave@pragprog.com" ,
:address => "123 Main St" ,
:pay_type => "check"
},
{ :name => "Andy Hunt" ,
:email => "andy@pragprog.com" ,
: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 => "barney@bedrock.com" )

常用的还是update 和update_all方法
order = Order.update(12, :name => "Barney" , :email => "barney@bedrock.com" ) #,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连接 [list]
[*]
[/list][list]
[*]
[/list][list]
[*]
[/list][code="java"][/code]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值