QA@IT

cancanのcanヘルパーがうまく動きません

1819 PV

現在、Rails3.2でcancanというGemを使っているのですが、canヘルパーが期待通りに動作しません。

現在の状況を例に説明させていただきます。
まず現在あるクラスとしては、Userクラスがあり、このクラスは複数のBlogクラスを所持しています。
モデルのソースでいうと下記のようになります。

class Blog < ActiveRecord::Base
  attr_accessible :content, :title

  belongs_to :user
class User < ActiveRecord::Base
  has_many :blogs

Blogクラスを所有しているUserだけが編集やアップデート等を行えるように認可をしたいです。
そこで、cancanを使いAbilityクラスにこのように書きました。

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new # guest user (not logged in)
    proc = Proc.new{|blog| user.id == blog.user_id}
    can :read, Blog
    can :create, Blog do proc end
    can :edit, Blog do proc end
    can :delete, Blog do proc end
  end
end

これで実際にpostメソッド等でアクセスされた場合は、ブロックできるようになりました。
しかし、EditやDestroyのようなリンクがほかのユーザから丸見えでした。
そこでViewに対して以下のように書きました。

index.html.erb

<% @blogs.each do |blog| %>
        <% if can? :edit,blog %>
            <%= link_to 'Edit', edit_user_blog_path(params[:user_id],blog) %>
        <% end %>
        <% if can? :delete,Blog %>
            <%= link_to 'Destroy', user_blog_path(params[:user_id],blog), method: :delete, data: { confirm: 'Are you sure?' } %>
        <% end %>
    <% end %>

しかしこう書いても、EditもDestroyのリンクがログインしてないユーザにすら見えてしまいます。

おそらく|blog| user.id == blog.user_idとブロックを書いたのがまずい様な気がします。
なぜならAbilityのinitializeは一回しかよばれていない(eachで何度もまわしてるのに)ので、ブロックが正常に動作していないっぽいです。

pry等でcanの実装を追いかけたのですが、どうにも理解できずうまい解決策がわかりません。
どのようにすればこの問題を解決できるでしょうか?

回答

すいません。自己解決しました。

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new # guest user (not logged in)

    can :read, Blog

    can :manage, Blog do |blog|
      user.id == blog.user_id
    end
  end
end

これならうまくいきました。
自分は遅延評価をメソッドを後で実行するものだとは理解していましたが、中でつかう変数等は定義された時点の物を使うことを理解していませんでした。

お世話がせしました。

編集 履歴 (1)
ウォッチ

この質問への回答やコメントをメールでお知らせします。