現象
ActiveAdminにて、以下のようなコードで自作scopeを利用したfilterを使うと、検索値が'1'のときに ArgumentError (wrong number of arguments (0 for 1))
が発生する。
class Product < ActiveRecord::Base
...
# custom scope
scope :original_scope, -> (product_id) {
product = Product.find(product_id)
# do something
end
def self.ransackable_scopes(_auth_object = nil)
%i(original_scope)
end
...
end
ActiveAdmin.register Product, namespace: :product_admin do
...
filter :original_scope, as: :select, collection: -> { Product.all }
...
end
原因調査
調べてみると、どうやらscopeに渡すときに、0と'0'をfalseに、1と'1'をtrueに変換しているので、値で渡せないようです。
Ransackで自前scope使う時に'1'や'0'を値として渡せない?
関連するIssueを見るとCloseになっているのですが、バージョンの問題なのか私の環境ではエラーのままでした。
Wrong result and errors for join/group/having scope with certain values (0 and 1) #502
対処
案1:可変長引数を使う
可変長引数で受けるとvalue=[]
になるので、無理やり書き換える。
今回のケースではid
を渡す用途のため、0
は無視する。
class Product < ActiveRecord::Base
...
# custom scope
scope :original_scope, -> (*product_id) {
product_id = [1] if product_id == [] # hotfix
product = Product.find(product_id[0])
# do something
end
...
end
かなり強引なやりかたで、汎用性も低そうなので、できれば避けたい。
案2:代わりになる引数を使う
今回のケースでは、引数で渡した数値(ID)を使ってProduct
の検索をするため、数値を使わずに検索できる方法を利用した。
class Product < ActiveRecord::Base
...
# custom scope
scope :original_scope, -> (product_name) {
product = Product.find_by(name: product_name)
# do something
end
...
end
この方法では当たり前かもしれないが、
- ユニークな値であること
- インデックスが張られていること
がないとレコードが複数件ヒットしたり、検索が遅かったりする。
もっとスマートな解決方法があれば、ぜひコメントをm(_ _)m
おわり