「空気を読む」検索
Rails の検索には Ransack が重用されます。Ransack では基本的にモデルの単一カラムに対して cont(contain) や eq(equal) といった条件で検索が可能です。この場合、ユーザーがひとつの用語で複数のカラムを検索したい場合、別々の入力フォームで複数回の入力を求めることになります。ここで、ひとつの検索語で複数カラムを一気に実行できたらどうでしょうか。これはいわゆる「空気を読む」検索と言えるかもしれません。
ゲームでの例を挙げます。例えば「龍飛」という入力をして検索をするユーザーはアイテム名「龍飛剣」を探しているのかもしれませんし、スキル名「龍飛の心得」を探しているのかもしれません。もしくは両方かもしれません。
Ransack でこの「空気を読む」検索を実現するためには入力語に対してモデルの複数カラムで検索を行い、結果の集約(と重複排除やソート)を行う必要があります。ここで Ruby の Runnable Object(Proc) を使用すると便利です。この実装で The Well-Grounded Rubyist, Third Edition の Chapter 14 で紹介されている Callable and runnable objects をはじめて有効活用できた気がします。
class EndpointsController < ApplicationController def search @input = params[:input] if (@input.empty?) then @input = "nothing" end @items = Item.search(@input) @skills = Skill.search(@input) @outcome = empty?([@items, @skills]) end def empty?(arg) (... snip ...) end
class Item < ApplicationRecord class << self def search(arg) result = {} result[:by_name] = search_by_name(arg) result[:by_description] = search_by_description(arg) result[:by_parameter] = search_by_parameter(arg) result end def search_by_name(arg) p = Proc.new do |x| { name: x.name, level: x.level, description: x.description, rarity: x.rarity.name, skills: x.skills.map(&:name) } end Item.ransack(name_cont: arg).result.map(&p) end end def search_by_description(arg) (... snip ...) end (... snip ...)