Rails 3, 不使用数据库的Active Model用法介绍

在第193篇【[url=http://railscasts.com/episodes/193-tableless-model]视频[/url], [url=http://asciicasts.com/episodes/193-tableless-model]文本[/url]】中,介绍了一种不使用实际数据库的ActiveRecord的用法。实际这只是因为需求而进行的hack的做法,因为ActiveRecord没有设计这样用。不过在Rails 3里,这个情况已经改变。在Rails3里ActiveMode是设计来解决不想存储到数据库的数据的需求。
这篇的主题就是介绍ActiveMode的用法。在正式介绍开始前,我们先看看我们将要用来展示的例子。

[img]http://dl.iteye.com/upload/attachment/279535/12cecc36-609d-30ba-bfc9-f0fbbc2dbc6f.png[/img]

截图展示一个用rails 脚手架创建的应用,其中Message是一个ActiveRecord的Mode。也就是脚手架默认的把Mode关联到数据库。我们将要做的是,我们只是把消息通过email发送但是不保持消息本身。
当然,在你要开始使用ActiveMode的时候你应该确认你是不是真的有这样的需求不使用数据库,因为,把数据库存到数据库可以作为一种数据的备份,或者用来进行后台的异步操作(译者晓夜注:比如当前例子的message可以存到数据库后异步调用发送)。对于我们的演示而言,我们就是要演示怎么使用ActiveMode,也没什么好考虑。那么,首先,看一下Message的
#/app/models/message.rb
class Message < ActiveRecord::Base
validates_presence_of :name
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
validates_length_of :content, :maximum => 500
end


从Message Mode的源代码我们可以看到,Message是继承自ActiveRecord::Base,也就是我们要不使用数据库,那么我们就要解除来自ActiveRecord的继承关系。然而,显然当我们去掉对应的ActiveRecord继承,那么对应的输入验证也就会失效。也就是我们将使用ActiveMode来支持这样的输入验证,这点在后文我们会看到。

查看Rails 3的源代码会看到 activerecord 和 activemodel的文件夹。实际上,Rails核心团队把所有和数据库无关的代码放到ActiveMode下。所以,ActiveRecord实际上依赖ActiveMode,而ActiveMode是经过了测试,并且可以独立于ActiveRecord使用的。
查看ActiveMode目录如下:


[img]http://dl.iteye.com/upload/attachment/279537/f3f98965-01ee-30bc-a93e-69065f877a05.png[/img]

从代码列表我们可以大概看出来,这个目录下将包括如下功能处理回调,dirty tracking, 序列化和验证,等等。比较显然,最后的三个文件是我们需要的。
打开validations的源代码,我们会看到如下的注释。从注释我们会知道使用验证相当容易,只要把加载 Validations模块和提供getter方法给我们要验证的属性。
# == Active Model Validations  
#
# Provides a full validation framework to your objects.
#
# A minimal implementation could be:
#
# class Person
# include ActiveModel::Validations
#
# attr_accessor :first_name, :last_name
#
# validates_each :first_name, :last_name do |record, attr, value|
# record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
# end
# end
#


那么,我们可以根据注释修改我们的Message Mode如下:
#/app/models/message.rb
class Message
include ActiveModel::Validations

attr_accessor :name, :email, :content

validates_presence_of :name
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
validates_length_of :content, :maximum => 500
end


那么,当前我们修改后的代码会有两个问题:1. 当我们创建Message的时候,Message.new方法会因为我们的initializer方法中不能创建接收参数的数据库插入操作而失效。2. 同样的save方法,原本会保存到数据库现在也将失效。
#/apps/controllers/messages_controller.rb
class MessagesController < ApplicationController
def new
@message = Message.new
end

def create
@message = Message.new(params[:message])
if @message.save
# TODO send message here
flash[:notice] = "Message sent! Thank you for contacting us."
redirect_to root_url
else
render :action => 'new'
end
end
end


我们首先修改保存的问题,因为我们并不需要保存message本身,所以save方法只需要进行验证操作就可以。也就是我们只需要用@message.valid? 方法代替@message.save方法就可以了。

#/app/controllers/messages_controllers.rb
def create
@message = Message.new(params[:message])
if @message.valid?
# TODO send message here
flash[:notice] = "Message sent! Thank you for contacting us."
redirect_to root_url
else
render :action => 'new'
end
end


至于,创建的问题,我们可以创建一个initialize的方法来接收传入Message的哈希参数,在initializer方法中,通过调用send(译者晓夜:调用变量对应的方法)方法给指定的属性赋值。

看起来应该没有什么问题了,可是当我们重新加载页面会看到如下错误:

[img]http://dl.iteye.com/upload/attachment/279541/6a887169-cb1c-31c5-b9bd-c262c65d8984.png[/img]

#/app/models/message.rb
class Message
include ActiveModel::Validations

attr_accessor :name, :email, :content

validates_presence_of :name
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
validates_length_of :content, :maximum => 500

def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
end

问题是在于我们没有在Message中定义to_key方法,form_for默认的认为我们model提供这样的方法,那么,我们需要添加这个功能。

实际上,在我们不知道Rails都需要我们的model提供什么功能的时候,我们可以加载ActiveModel::Lint::Tests模块到我们的modle测试中。在Lint::Tests模块中展示出包括to_key在内的Rails定义的model需要提供的方法。 至于,如何解决缺少方法的问题,我们可以加载一组ActiveRecord的模块来解决。 Conversion是其中包括to_key方法的模块。我们也需要Naming 方法,这里我们不使用include而是使用extend因为我们需要类方法(译者晓夜:对应于include类实例方法)。
我们引入了Conversion,那么我们需要重新定义persisted?方法,因为我们不需要使用数据库所以,我们呢需要persisted方法返回false。所以,我们的Message Model将会修改如下:
#/app/models/message.rb
class Message
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming

attr_accessor :name, :email, :content

validates_presence_of :name
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
validates_length_of :content, :maximum => 500

def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end

def persisted?
false
end
end


这时,我们再刷新页面我们会看到已经能够正常显示了,也就是说我们的Message model已经提供了Rails3需要的方法。提交form我们会发现验证的功能也可以正常使用了。

[img]http://dl.iteye.com/upload/attachment/279539/8a8bc95f-f781-33b6-9237-beb25696de6d.png[/img]


实际上我们只是介绍了ActiveMode中很少的一部分功能。ActiveMode中还有更多值得了解的方法,幸好这部分的代码结构和注释写的很好,在你需要使用的时候,只要参考对应的源代码就可以很好的了解如何使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值