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 <[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" , bject => @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 rder 后者同上 
 
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 => rders) } 
调用model的to_xml方法 
 
生成指定格式的feed xml格式文件 
format.atom { render :layout => false } 
生成JSON 的格式,针对http header请求 
format.json { render :layout => false ,:json => @product.to_json(:include => rders) } 
 
生成文档 
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 rders, :e_mail, :customer_email 
修改列属性 
change_column rders, rder_type, :string 
 
重命名表,注意对原有的model代码也会修改 
rename_table rder_histories, rder_notes 
 
当添加一些不允许修改的表信息时,可以在self.down中抛出异常,防止错误的修改,导致数据丢失 
def self.down 
    raise ActiveRecord::IrreversibleMigration 
end 
 
对表的创建,注意block的使用 
def self.up 
create_table rder_histories do |t| 
        t.integer rder_id, :null => false 
        t.text :notes 
        t.timestamps 
    end 
end 
 
定义索引,这里并不很了解,建议需要时查看API 
add_index rders, :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, ptions => 
    "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, 
                                            rder => "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 rder 
B:has_one :invoice 
 
One-to-Many 
A:belongs_to rder     #一方 
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, 
                                n => :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, nly_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 ff, nly => %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 ff 
也可以附带更多参数 
session ff, nly => [ :show, :list ] 
session ff, :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 nly => :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, 
                            nly => [ :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' , 
            bject => @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" , bject => @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连接 

                            

你可能感兴趣的:(Ruby)