at posts/single.html

更新時刻の設定をコントローラからモデルへ

さっきの例では、ページの更新時刻をコントローラー (note_controller.rb) で設定していた。 でもよく考えると、コントローラのアクションが増えるごとに、同じような処理を書かないといけない。 既に、 NoteController の create と update で、同じ処理(@page.updated = Time.now)が書かれているし。 コントローラをシンプルに書けるのが Rails のいい所らしいので、処理をコントローラ (note_controller.rb) からモデル (page.rb) へと移す。

Rails のモデル (ActiveRecord::Base) にはコールバックという仕組みがあって、生成や更新時などに実行したい処理を登録できるようになっている。 生成時 (before_create) と更新時 (before_save) にそれぞれ現在時刻を設定する処理を追加した。

Index: app/models/page.rb
===================================================================
--- app/models/page.rb  (リビジョン 2)
+++ app/models/page.rb  (作業コピー)
@@ -1,4 +1,7 @@
 class Page < ActiveRecord::Base
+  before_create {|model| model.created = Time.now }
+  before_save   {|model| model.updated = Time.now }
+
   def title
     body.each {|title| return title.chomp }
   end

そして、先ほど追加したコントローラ (note_controller.rb) の create と update の処理を削除する。

Index: app/controllers/note_controller.rb
===================================================================
--- app/controllers/note_controller.rb  (リビジョン 2)
+++ app/controllers/note_controller.rb  (作業コピー)
@@ -23,8 +23,6 @@

   def create
     @page = Page.new(params[:page])
-    @page.created = Time.now
-    @page.updated = Time.now
     if @page.save
       flash[:notice] = 'Page was successfully created.'
       redirect_to :action => 'list'
@@ -39,7 +37,6 @@

   def update
     @page = Page.find(params[:id])
-    @page.updated = Time.now
     if @page.update_attributes(params[:page])
       flash[:notice] = 'Page was successfully updated.'
       redirect_to :action => 'show', :id => @page

これでもちゃんと、生成時刻と更新時刻が設定されていることを確認。 うん。大丈夫そうだ。

リポジトリへも登録しておいた。

さらに簡略化

さらに調べてみると、モデルに created_on と updated_on というカラムを作っておくと、生成時刻と更新時刻を Rails が自動的にセットしてくれるらしい。 惜しい…「_on」が足りなかった。 でも仮に、この機能を知らずに created_on, updated_on なんて名前を付けていたら、それはそれで不思議な挙動に悩んでいただろうな。 モデルのコールバックの使い方も覚えられたし、まぁいいか。

とりあえず、 created を created_on に、 update を updated_on に変更する。 まずはデータベースのマイグレーションから。

$ vi db/migrate/003_rename_update_of_page.rb

class RenameUpdateOfPage < ActiveRecord::Migration
  def self.up
    remove_index  :pages, :created
    remove_index  :pages, :updated
    rename_column :pages, :created, :created_on
    rename_column :pages, :updated, :updated_on
    add_index     :pages, :created_on
    add_index     :pages, :updated_on
  end

  def self.down
    remove_index  :pages, :created_on
    remove_index  :pages, :updated_on
    rename_column :pages, :created_on, :created
    rename_column :pages, :updated_on, :updated
    add_index     :pages, :created
    add_index     :pages, :updated
  end
end

この内容でマイグレーションを実行すると、なぜか既存の時刻の内容が消えてしまった (created_on と updated_on が空になった) 。 まぁ、今はサンプルデータだから支障は無いんだけど。

他にもモデル・コントローラ・ビューを修正する。 Webブラウザからアクセスして、ちゃんと更新順に並ぶことを確認した。

screenshot (Ruby on Rails) - (6)

関連する日記