在第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的
从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方法给我们要验证的属性。
那么,我们可以根据注释修改我们的Message Mode如下:
那么,当前我们修改后的代码会有两个问题:1. 当我们创建Message的时候,Message.new方法会因为我们的initializer方法中不能创建接收参数的数据库插入操作而失效。2. 同样的save方法,原本会保存到数据库现在也将失效。
我们首先修改保存的问题,因为我们并不需要保存message本身,所以save方法只需要进行验证操作就可以。也就是我们只需要用@message.valid? 方法代替@message.save方法就可以了。
至于,创建的问题,我们可以创建一个initialize的方法来接收传入Message的哈希参数,在initializer方法中,通过调用send(译者晓夜:调用变量对应的方法)方法给指定的属性赋值。
看起来应该没有什么问题了,可是当我们重新加载页面会看到如下错误:
[img]http://dl.iteye.com/upload/attachment/279541/6a887169-cb1c-31c5-b9bd-c262c65d8984.png[/img]
问题是在于我们没有在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将会修改如下:
这时,我们再刷新页面我们会看到已经能够正常显示了,也就是说我们的Message model已经提供了Rails3需要的方法。提交form我们会发现验证的功能也可以正常使用了。
[img]http://dl.iteye.com/upload/attachment/279539/8a8bc95f-f781-33b6-9237-beb25696de6d.png[/img]
实际上我们只是介绍了ActiveMode中很少的一部分功能。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中还有更多值得了解的方法,幸好这部分的代码结构和注释写的很好,在你需要使用的时候,只要参考对应的源代码就可以很好的了解如何使用。