1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ransack gemの`ransackable_attributes`メソッドを見に行った時の備忘録

Posted at

環境

  • Rails 7.1.5.1
  • Ruby 3.1.5

この記事を書こうと思った理由

ransack gemを2.5.0から4系にアップデートを行った時に、下記のようなメッセージが出力されました

RuntimeError in Home#index

Ransack needs User attributes explicitly allowlisted as
searchable. Define a `ransackable_attributes` class method in your `User`
model, watching out for items you DON'T want searchable (for
example, `encrypted_password`, `password_reset_token`, `owner` or
other sensitive information). You can use the following as a base:

下記の記事を見ながら解決しようと思った時に、どんな仕組みになっているのかなぁ〜とgemのコードを見に行った時の備忘録です

ransackable_attributesのコード

ransackable_attributes(auth_object = nil)

まずこんな感じで定義されています

ransack/lib/ransack/adapters/active_record/base.rb
# Ransackable_attributes, by default, returns all column names
# and any defined ransackers as an array of strings.
# For overriding with a whitelist array of strings.
#
def ransackable_attributes(auth_object = nil)
  @ransackable_attributes ||= deprecated_ransackable_list(:ransackable_attributes)
end

||=自己代入で@ransackable_attributesがなければdeprecated_ransackable_list(:ransackable_attributes)を呼びます

deprecated_ransackable_list(method)

ransackable_attributes(auth_object = nil)で呼ばれているdeprecated_ransackable_list(:ransackable_attributes)はこんな感じ

ransack/lib/ransack/adapters/active_record/base.rb
def deprecated_ransackable_list(method)
  list_type = method.to_s.delete_prefix("ransackable_")

  if explicitly_defined?(method)
    warn_deprecated <<~ERROR
      Ransack's builtin `#{method}` method is deprecated and will result
      in an error in the future. If you want to authorize the full list
      of searchable #{list_type} for this model, use
      `authorizable_#{method}` instead of delegating to `super`.
    ERROR

    public_send("authorizable_#{method}")
  else
    raise <<~MESSAGE
      Ransack needs #{name} #{list_type} explicitly allowlisted as
      searchable. Define a `#{method}` class method in your `#{name}`
      model, watching out for items you DON'T want searchable (for
      example, `encrypted_password`, `password_reset_token`, `owner` or
      other sensitive information). You can use the following as a base:

      ```ruby
      class #{name} < ApplicationRecord

        # ...

        def self.#{method}(auth_object = nil)
          #{public_send("authorizable_#{method}").sort.inspect}
        end

        # ...

      end
      ```
    MESSAGE
  end
end

パッと見た感じ、ここでエラーメッセージやら警告が定義してあるなぁというのがわかります

list_type = method.to_s.delete_prefix("ransackable_")list_typeattributesを代入します
delete_prefixは文字列の先頭から prefix を削除した文字列のコピーを返します

次にexplicitly_defined?(method)でどんな仕組みで分岐されているのかを見に行きます

explicitly_defined?(method)

ransack/lib/ransack/adapters/active_record/base.rb
def explicitly_defined?(method)
  definer_ancestor = singleton_class.ancestors.find do |ancestor|
    ancestor.instance_methods(false).include?(method)
  end

  definer_ancestor != Ransack::Adapters::ActiveRecord::Base
end

ここが一番むずかったす

初見、一体これは何をしているんだ…という感じですが、ざっくりいうと

ユーザーがattributesをカスタムしているかどうか?を判定してくれるのメソッドです

ユーザーがattributesをカスタムしていればdefiner_ancestor != Ransack::Adapters::ActiveRecord::Baseでtrueが返り、していなければRansack::Adapters::ActiveRecord::Baseで定義されいる元々のattributesとイコールになってしまうのでfalseが返ります

※見たことないメソッドについての補足

singleton_class

singleton_classメソッドとはオブジェクトのRubyの特異クラスを取得してくれるメソッドです

ancestors

モジュールやクラスの継承チェーンを配列で返してくれる

authorizable_ransackable_attributes

public_send("authorizable_#{method}")

ユーザーによってカスタムされていた場合最後はpublic_sendauthorizable_ransackable_attributesが呼び出されます

Breadcrumbsransack/lib/ransack/adapters/active_record/base.rb
# Bare list of all potentially searchable attributes. Searchable attributes
# need to be explicitly allowlisted through the `ransackable_attributes`
# method in each model, but if you're allowing almost everything to be
# searched, this list can be used as a base for exclusions.
#
def authorizable_ransackable_attributes
  if Ransack::SUPPORTS_ATTRIBUTE_ALIAS
    column_names + _ransackers.keys + _ransack_aliases.keys +
    attribute_aliases.keys
  else
    column_names + _ransackers.keys + _ransack_aliases.keys
  end.uniq
end

if Ransack::SUPPORTS_ATTRIBUTE_ALIASでransackがサポートしているエイリアスかどうかを判定します

そしてようやく見覚えのあるスネークケースのリストが生成されます

感想

  • 特異メソッドとか初めて聞きました。これもまた記事書くかも
  • ちょっとした気持ちで見にいくと複雑で帰れなくなる

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?