ransack, ransack, ransack
DESCRIPTION
Introducing ransack gem.TRANSCRIPT
Ransack, Ransack, RansackAgileware kaigi 1
2014.7.25 @maimai77
Searching ActiveRecord!
一致検索はそこそこできるIssue.where(name: 'hoge').to_sql#=> "SELECT `issues`.* FROM `issues` WHERE `issues`.`name` = 'hoge'"
Issue.where(name: ['hoge', 'fuga']).to_sql#=> "SELECT `issues`.* FROM `issues` WHERE `issues`.`name` IN ('hoge', 'fuga')"
Searching ActiveRecord...
大小比較、部分一致は得意じゃないIssue.where("id < ?", 3).to_sql#=> "SELECT `issues`.* FROM `issues` WHERE (id < 3)"Issue.where("name LIKE ?", '%hoge%').to_sql#=> "SELECT `issues`.* FROM `issues` WHERE (name LIKE '%hoge%')"
辛くなってきた…
Searching ActiveRecord ;<
OR検索になるともうダメな子4 Arel → 可読性 #とは4 独自の検索クラス → 誰がメンテすんの?4 生SQL → 心が折れそうだ…
Ransack
What is Ransack?
ransack [rˈænsæk]
〈場所を〉くまなく探る[探す], あさり回る
What is Ransack?
ransack gemhttps://github.com/activerecord-hackery/ransack
4 ActiveRecordを拡張して検索機能をつけるgem
4 設定なしで一通りの検索をカバー4 high googleability → googleトップがgithub
4 ドキュメントが貧弱
How to use Ransack
In your Gemfile:
gem 'ransack'
> bundle install
Rails4, 4.1用に最適化されたbranchもある
Extend ActiveRecord
>bundle exec rails g model Issue name:string>bundle exec rake db:migrate
class Issue < ActiveRecord::Baseend
でIssue.search(name_cont: 'hoge').result.to_sql#=> "SELECT `issues`.* FROM `issues`# WHERE (`issues`.`name` LIKE '%hoge%')"
Search argument keys
Issue.search(name_cont: 'hoge').result
ransackable_attribute + predicate(述語)
ex)
4 name_eq
4 name_cont
Ransackable_attributes
Issue.ransackable_attributes#=> ["id", "name", "created_at", "updated_at"]
defaultはIssue.column_namesと同じ
Predicates
eq, gt, gteq, lt, lteq, in, cont, start, end and more
https://github.com/activerecord-hackery/ransack/wiki/Basic-Searching
Alias method
class Issue < ActiveRecord::Base def self.search 'hoge' endend
Issue.search#=> 'hoge'
Issue.ransack(name_cont: 'hoge').result.to_sql#=> "SELECT `issues`.* FROM `issues`# WHERE (`issues`.`name` LIKE '%hoge%')"
このあたりまではパーフェクトRuby on Railsにもちらっと書いてます。
More Ransack
Complex search
4 2つのカラムのいずれかが指定した値と一致4 関連先のテーブルを検索する
Complex search
4 2つのカラムのいずれかが指定した値と一致Issue.ransack(name_or_memo_eq: 'hoge').result.to_sql#=> "SELECT `issues`.* FROM `issues`# WHERE ((`issues`.`name` = 'hoge' OR `issues`.`memo` = 'hoge'))"
and, or で結合した ransackable_attribute も ransackable
Complex search
4 関連先のテーブルを検索するclass Issue < ActiveRecord::Base belong_to :projectend
Issue.search(project_name_eq: 'hoge').result.to_sql#=> "SELECT `issues`.* FROM `issues`# LEFT OUTER JOIN `projects` ON `projects`.`id` = `issues`.`project_id`# WHERE `projects`.`name` = 'hoge'"
association + ransackable_attribute も ransackable
More and more Ransack
More complex search
4 逆順の文字列と一致するものを検索4 2つのカラムの値を結合したものと比較4 半角スペースを無視した検索組み合わせだけじゃなんとも…→ ransacker
What is ransacker?
customized ransackable_attribute
ransackerメソッドで追加する
Define ransacker
4 逆順の文字列と一致するものを検索class Issue < ActiveRecord::Base ransacker :reversed_name, formatter: proc { |v| v.reverse } do |parent| parent.table[:name] endend
Issue.search(reversed_name_eq: 'hoge').result.to_sql#=> "SELECT `issues`.* FROM `issues` WHERE `issues`.`name` = 'egoh'"
Define ransacker
formatter: 検索に渡したvalueを加工するproc { |v| v.reverse }.call('hoge')#=> "egoh"
block: 検索対象のカラムを決定する
Define ransacker
4 2つのカラムの値を結合したものと比較class Address < ActiveRecord::Base ransacker :address do |parent| Arel::Nodes::NamedFunction.new( 'CONCAT', [parent.table[:region],parent.table[:city]] ) endend
Address.search(address_cont: '大阪府大阪市')
#=> "SELECT `addresses`.* FROM `addresses`# WHERE (CONCAT(`addresses`.`region`, '', `addresses`.`city`)# LIKE '%大阪府大阪市%')"
Define ransacker
4 半角スペースを無視した検索ransacker :name_without_spaces, formatter: proc { |v| v.gsub(' ', '') } do |parent| Arel::Nodes::NamedFunction.new( 'REPLACE', [parent.table[:name], ' ', ''] )end
Issue.search(name_without_spaces_eq: 'h o g e').result.to_sql#=> "SELECT `issues`.* FROM `issues`# WHERE REPLACE(`issues`.`name`, ' ', '') = 'hoge'"
Define ransacker
NamedFunctionはDB依存しがちなので注意なんかいい方法あったら教えてください
Custom Predicate
config/initializers/ransack.rb で定義Ransack.configure do |config| config.add_predicate 'equals_diddly', arel_predicate: 'eq', formatter: proc { |v| "#{v}-diddly" }, validator: proc { |v| v.present? }, compounds: true, type: :stringend
割愛。
Conclusion
Conclusion
4 機能として検索画面が必要ならとりあえずransack使う4 複雑な検索が必要になっても生SQLに逃げない4 Arelとはちょっと向き合う4 view helperも色々できる4 でもorの結合くらいwhereでできるようになって欲しい