ActiveRecord Optimistic Locking

2014-08-02T00:00:00+00:00 Ruby Rails

参考: http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html

Rails(っていうかActiveRecord)での楽観的ロックを使う場合のドキュメント見ながらやってみた

とりあえず実検証でのアプリケーションを作る必要があるので(ry

app/controllers/entries_controller.rb

class EntriesController < ApplicationController
  def show
    @entries = Entry.all
  end

  def edit
    @entry= Entry.find(params[:id])
  end

  def update
    entry = Entry.find(params[:entry][:id])
    update_params = params.require(:entry).permit(:name)
    entry.update(update_params)

    redirect_to :action => :show
  end
end

app/views/entries/show.erb

<% @entries.each do |sample| %>
    <%= link_to sample.name, { :action => "edit", :id => sample.id } %>
<% end %>

app/views/entries/edit.erb

<%= form_for @entry do |f| %>
  <%= f.hidden_field :id %>
  <%= f.text_field :name  %>
  <%= f.submit "update" %>
<% end %>

な感じで作ったとして、/entry/editに複数からアクセスしてアップデート処理を行った場合、一方が処理したのを他でアップデートされてデータが破壊されるっていうオチになる

でそういう風にならないようにデータをロックするなりで一方がアップデートして他が同時にアップデートしようとしたような場合にはエラーが起こるように出来る

Entryモデルのマイグレーションを修正

class CreateEntries < ActiveRecord::Migration
  def change
    create_table :entries do |t|
      t.string :name
      t.timestamps
      t.integer :lock_version, :default => 0
    end
  end
end

lock_versionっていう名前のカラムを作っておけばそれを利用して楽観的ロックな機能を利用する事が出来る模様。又、モデルにて

class Entry < ActiveRecord::Base
  self.locking_column = "lock_entry"
end

のようにlocking_columnでロックに使用するカラムを指定出来る。だから別にlock_versionじゃなきゃダメっていうわけでもない模様

これやれば良いだけのとちょっとアプリケーション自体を修正する必要もある

app/controllers/entries_controller.rbを修正

class EntriesController < ApplicationController
  def show
    @entries = Entry.all
  end

  def edit
    @entry= Entry.find(params[:id])
  end

  def update
    entry = Entry.find(params[:entry][:id])
    # 修正
    update_params = params.require(:entry).permit(:name, :lock_version)
    entry.update(update_params)

    redirect_to :action => :show
  end
end

app/views/entries/edit.erbを修正

<%= form_for @entry do |f| %>
  <%= f.hidden_field :id %>

  <!-- 追加 -->
  <%= f.hidden_field :lock_version %>

  <%= f.text_field :name  %>
  <%= f.submit "update" %>
<% end %>

ってな感じでやると、一方がアップデート処理をした後にもう一方側でアップデート処理等を使用とすると「ActiveRecord::StaleObjectError」というようなエラーになる

っていう感じ。疑問なのがこういうテストをどう書くのかっていう所。分かり次第追記する

FactoryGirlのロードの仕組み Capybaraで複数のセッションを使う