Rails Integration
Queries integrate with Rails forms and controllers through ActiveModel compatibility – model_name, to_params, and the from_params boundary method.
Controller pattern
ruby
class EmployeesController < ApplicationController
def index
@query = Employee::Query.from_params(params, scope: policy_scope(Employee))
@employees = pagy(@query.resolve)
end
endfrom_params
from_params handles the messy work of extracting search parameters from a controller request:
ruby
Employee::Query.from_params(params)
Employee::Query.from_params(params, scope: current_department.employees)
Employee::Query.from_params(params, scope: current_department.employees, department: current_department)Here's what it does, in order:
- Extracts the nested hash from
params[param_key](e.g.,params[:employee_query]). Falls back to flat params if the key is missing. - Pulls out the
sortvalue and validates it against declared sorts. Invalid sorts are silently dropped (falls back to default). - Strips blank strings to
nilfor optional props. - Compacts array blanks –
["admin", ""]becomes["admin"]. - Coerces string values to their declared types (Integer, Float, Date, Time, DateTime, BigDecimal). Values that can't be coerced are dropped to
nil. - Skips
_Reftyped props entirely – they must be passed as keyword overrides. This prevents ID injection from user input. - Merges keyword overrides last, so controller-pinned values always win.
- Returns a query instance (not a relation – call
.resolveto execute).
Search forms
Queries work with form_with for search forms:
erb
<%= form_with model: @query, url: employees_path, method: :get do |f| %>
<%= f.text_field :name, placeholder: "Search by name" %>
<%= f.select :status, %w[active inactive], include_blank: "Any status" %>
<%= f.hidden_field :sort, value: @query.sort %>
<%= f.submit "Search" %>
<% end %>param_key
By default, the param key derives from the class name (Employee::Query → employee_query). Override it when you want shorter URLs:
ruby
class Employee::Query < Dex::Query
param_key :q
# params[:q][:name] instead of params[:employee_query][:name]
endto_params
Returns a hash of non-nil prop values plus the current sort – useful for generating links that preserve search state:
ruby
query = Employee::Query.new(name: "ali", sort: "-name")
query.to_params # => { name: "ali", sort: "-name" }erb
<%= link_to "Sort by name", employees_path(@query.to_params.merge(sort: "name")) %>model_name
Derived from the class name by default. When param_key is set explicitly, model_name adapts to match. Anonymous classes fall back to "Query".