仕事で見かけたコードを汎用化した内容にして、個人ブログにメモっておく。*1
多段ネストを reverse して Proc でラップしたり、それを RSpec で検証する方法はブログ書いてないと忘れる自信がある。
モデルの定義
class User < ApplicationRecord default_scope { where(deleted_at: nil) } end class Article < ApplicationRecord default_scope { where.not(published_at: nil) } end
元コード
User.unscoped do Article.unscoped do assert_equal 'SELECT "users".* FROM "users"', User.all.to_sql assert_equal 'SELECT "articles".* FROM "articles"', Article.all.to_sql end end
書き換えたあと
module ScopeHelper def self.unscoped(models:, &block) models.reverse .inject(block) { |result, model| -> { model.unscoped(&result) } } .call end end ScopeHelper.unscoped(models: [User, Article]) do assert_equal 'SELECT "users".* FROM "users"', User.all.to_sql assert_equal 'SELECT "articles".* FROM "articles"', Article.all.to_sql end
RSpecで書いたテスト
RSpec.describe ScopeHelper do describe '.unscoped' do it do expect(User).to receive(:unscoped).and_call_original block = -> {} expect(Article).to receive(:unscoped) do |&args| expect(args).to eq block end ScopeHelper.unscoped(models: [User, Article], &block) end end end
*1:default_scopeとか絶対使わないけど、他に汎用化した例は思い浮かばなかった