homeASCIIcasts

177: Model Versioning 

(view original Railscast)

Other translations: En

Other formats:

Written by samsam (www.isamsam.com)

在这一集里我们要建一个wiki风格的应用。应用有一个page模型,下面是主页的show action。

Our wiki's home page.

像大部分wiki一样,任何人都可以编辑其中的一个页面,改变它的内容,但是我们的少了一个显著的功能,就是回过去看页面上一个版本的能力。我们需要的是模型的一个版本解决方案。

Vestal Versions

Ruby5 podcast的第三集推荐一个插件可以帮助我们解决这个问题。Vestal Versions存储ActiveRecord模型的修改历史,这正是我们的wiki要找的。

Vestal Versions作为gem存在,所以要在应用中使用它,我们只要加上

config.gem 'laserlemon-vestal_versions', :lib => 'vestal_versions', :source => 'http://gems.github.com'

/config/environment.rb中的config block中,然后运行:

sudo rake gems:install

确保gem已经安装了。

因为Vestal Versions需要修改我们应用的数据库,下一步是运行它的migration generator。

script/generate vestal_versions_migration

这生成Vestal Versions用的versions表的migration文件,所以下一步我们要迁移数据库。

rake db:migrate

现在可以给我们的Page模型添加版本控制。我们所有要做的就是在我们的模型里调用versioned

/app/models/page.rb

class Page < ActiveRecord::Base  
  versioned  
end

就是那样。Page的所有修改现在会被versions表跟踪。

添加版本控制到我们的Wiki页面

我们wiki应用要做的第一个修改是在show action页面上显示版本号。我们值需要显示页面的version

<% title @page.name %>  
<p><%= simple_format wiki_link(h(@page.content))%></p>  
<p>  
  <%= link_to "Edit", edit_page_path(@page) %>  
  | Version <%= @page.version %>  
</p>

当我们重新载入页面,不管怎么样版本号没有显示。

The version number isn’t displayed.

这是因为页面在添加Vestal Versions到应用之前就存在了,因此还没有使用version。我们可以更新存在的能让Vestal Versions使用版本控制的模型来修复。

如果我们只是要修改应用development的版本我们可以在console里来做,但是我们要保证版本控制要设置在任何代码部署的地方。要确保这些我们要生成一个migration来替代。

script/generate migration version_existing_pages

在migration中我们要更新任何已经存在的Page记录。我们的应用运行在Rails2.3.3上,我们可以用新的touch功能来简单的更新每一个。

class VersionExistingPages < ActiveRecord::Migration  
  def self.up  
    say_with_time "Setting initial version for pages" do  
      Page.find_each(&:touch)  
    end  
  end  
 
  def self.down  
  end  
end

在一个page上调用touch,会更新它的timestamp,并且会引起版本控制。注意我们用了say_with_time来输出一个migration的消息到console上。

我们现在再次运行

rake db:migrate

每个page都得到了一个版本。

如果现在我们回到应用中,并重载页面我们能看到版本号现在显示了。

The version number is now showing.

而且当我们编辑页面的时候,版本号应该变为2。

When we update the page the version number increases.

行了,我们现在显示主页的版本2。

由于我们的页面现在已经版本化了,如果我们在页面上当前版本号边上加一个显示以前版本的链接将会更好。

<% if @page.version > 1 %>  
| <%= link_to "Previous version", :version => @page.version-1 %>  
<% end %>

if条件确保链接只有页面有上个版本的时候显示。至于这个链接本身,我们要显示同一个页面,但是在有页面上个版本的值的URL里带上一个version参数。然后,在PagesController show action里面我们可以检查这个version参数,如果我们找到,就到页面的适当版本。

/app/controllers/pages_controller.rb

def show  
  @page = Page.find(params[:id])  
  @page.revert_to(params[:version].to_i) if params[:version]  
end

show action里如果version参数传递过来,我们可以用Vestal Versions的revert_to方法来取得页面的当前版本。要注意的一件事情是revert_to期望一个integer值,这个参数是一个string,所以我们要先转化它。

当我们再次重载页面,我们现在看待”previous Version”链接。

Adding a “previous version” link.

我们点击它,我们得到页面的第一个版本。

Looking at a previous version of the page.

当找页面的老版本的时候,有一个回到当前版本的链接是很方便的。我们可以添加这些看到的代码来实现。

<% if params[:version] %>  
  | <%= link_to "Latest version", :version => nil %>  
<% end %>

现在我们在浏览页面上一个版本的时候(也就是在查询字符串里有version参数)我们可以看到一个回到页面当前版本的链接。

Older versions now have a link to the current version.

一些其他技巧

Vestal Versions能做一些其他事情,我们会在console里显示。(注意我们这儿用hirb来让输出更好看。)如果我们得到第一个页面

>> p = Page.first
+----+----------+--------------------+--------------------+--------------------+
| id | name     | content            | created_at         | updated_at         |
+----+----------+--------------------+--------------------+--------------------+
| 1  | HomePage | Welcome to out ... | 2009-08-31 08:4... | 2009-08-31 10:1... |
+----+----------+--------------------+--------------------+--------------------+
1 row in set

我们然后在page上调用versions方法来得到它的所有版本。

>> p.versions
+----+--------------+---------------+---------------+--------+---------------+
| id | versioned_id | versioned_... | changes       | number | created_at    |
+----+--------------+---------------+---------------+--------+---------------+
| 1  | 1            | Page          | nameHomePa... | 1      | 2009-08-31... |
| 2  | 1            | Page          | updated_at... | 2      | 2009-08-31... |
+----+--------------+---------------+---------------+--------+---------------+
2 rows in set

versions是设置成一个映射到我们早期用migration创建的versions表的ActiveRecord关联(是它自己模型的)。举个例子,我们能用这个来拥有一个显示页面各个版本的历史页面和到每个的链接。

先前我们用revert_to来显示页面的一个早期版本,传递给它我们要的版本id。不过我们可以给revert_to传递不同的参数,包含datetime值。要得到我们一个半小时前的版本,我们可以传递30.minutes.ago,我们应该看到我们创建的页面的第一个版本。

>> p.revert_to(60.minutes.ago)
=> 1
>> p.content
=> "Welcome to out humble wiki where you can learn how to PurchasePianos,
WriteMusic and PlayPiano. If you're just getting started, check out the
BeginnerPiano page. Enjoy!"

我们也可以传递symbols :first或:last来得到模型最老或最新的版本。

>> p.revert_to(:first)
=> 1
>> p.revert_to(:last)
=> 2

这一集就是这些。我们抱有希望地展示给你Vestal Versions是一个在各种情况下你需要保留你的rails应用的ActiveRecord的改变历史的一个很好的解决方案。