Punditにコミットした

github.com

結果的には破壊的変更をしてしまったのでPR送った経緯を書いておこうと思う。 もちろんこのままリリースされるかはわからないけど。
ちなみにPunditは以下のように明瞭な形でリソースに対しての行動に制約を設定できるようになるGemだ。

 class PostPolicy
  attr_reader :user, :post

  def initialize(user, post)
    @user = user
    @post = post
  end

  def update?
    user.admin? or not post.published?
  end
end

# in controller
def update
  @post = authorize Post.find(params[:id])
  if @post.update(post_params)
    redirect_to @post
  else
    render :edit
  end
end
 

書きやすく読みやすいコードが書けて良いGemだと思う。

PRを立てた経緯

業務でPunditを利用させてもらっていて、ちょっと面倒なところがあった。

https://github.com/varvet/pundit#policy-namespacing にもあるがこのようなコードだ。

 module Admin
  class PostPolicy
  ~~
  end
end

class AdminController < ApplicationController
  def authorize(record, query = nil)
    super([:admin, record], query)
  end
end

class Admin::PostController < AdminController
  def show
    post = Post.find(params[:id])
    authorize(post)
  end
end
 

「同じリソースに対してpolicy*1名前空間で分けて複数設定する」というのはよく行うのだけれど、Punditは上記のように名前空間を配列で渡すとpolicyを推測して読み込んでくれる。
がしかし、この方法をつかうとせっかくクールなこの書き方が使えなくなってしまう。

 def show
  @user = authorize User.find(params[:id])
end
 

authorizeは渡された引数をそのまま返すので上記のように綺麗にかけるのだが、名前空間を推測させるために配列を渡すとそのまま配列が帰ってきてしまうのである。
仕方がないので業務では

 class AdminController < ApplicationController
  def authorize(record, query = nil)
    super([:admin, record], query)
    record
  end
end
 

としてrecordが帰ってくるようにした。*2

recordが帰ってくるのが望ましいだろう、ということでせっかくのOSSだし修正の提案をしたのがPRを立てるまでの経緯だ。

PR立てた後

マージされるまでメンテナーの方に丁寧なレビューをもらい感謝しかない。こういってはなんだが自分としては無料で自分のコードみてもらって良いことしかなかった。
OSSはやることが多くて大変」という言葉をOSSパッチ会でkoicさんもおっしゃっていたが、本当に尊敬できる方たちだと思う。
Punditという素晴らしいGemをメンテしていただきありがとうございます。
READMEの更新を忘れて後ですごすごPR立ててすいません
最近Elixirが楽しかったがRubyもやっぱり面白い。

*1:Punditではリソースに対しての制約をpolicyと呼ぶ

*2:もちろんこのままじゃないですよ!