Ransack Documentation
website·Indexed 30 days ago
https://activerecord-hackery.github.io/ransack/A Rails library for adding advanced searching and filtering to ActiveRecord models. The documentation covers simple and advanced search modes, predicates, sorting, polymorphic search, and integration with PostgreSQL-specific types like JSONB and arrays.
What's inside Ransack
- Ransack is a tool for adding searching capabilities to Ruby on Rails applications using standard Ruby and ERB, without requiring additional infrastructure like ElasticSearch or Algolia. It is supported for Rails 8.1, 8.0, and 7.2 on Ruby 3.2 and later.
Understand the Ransack search pipeline stages
A search operation in Ransack proceeds through four distinct stages, each located within
lib/ransack/:- Parse (
search.rb,nodes/): Converts the params hash into a tree of Grouping, Condition, Attribute, Value, and Sort nodes. This stage is database-agnostic. - Resolve (
context.rb,active_record/context.rb): Maps attribute names (e.g.,articles_title) to tables, columns, and the necessary joins. - Build (
visitor.rb,predicate.rb,constants.rb): Traverses the node tree to generate Arel predicates and orderings. - Integrate (
active_record/): Handles all Active Record interactions, including class methods added to models, join machinery, and the Context that evaluates the search into a relation.
- Parse (
Sort by virtual fields using model scopes
To sort by a complex value or SQL function (e.g.,REVERSE(name)), define scopes in your model that follow the naming conventionsort_by_[virtual_field_name]_[direction]. You can then reference the virtual field name insort_link.# In the Model class Person < ActiveRecord::Base scope :sort_by_reverse_name_asc, lambda { order("REVERSE(name) ASC") } scope :sort_by_reverse_name_desc, lambda { order("REVERSE(name) DESC") } end # In the View <%= sort_link(@q, :reverse_name) %>Construct search and sort URLs for Ransack
Ransack expects search parameters as a hash. In a URL, this is represented using bracket notation:
hash_name[key]=value.hash_name: The parameter defined in the controller (commonlyq).key: A combination of the attribute name and the search predicate (e.g.,first_name_cont).value: The search term.s: The sorting parameter, which must be nested inside the search hash (e.g.,q[s]=created_at+desc).
/users.json?q[first_name_cont]=pete&q[last_name_cont]=jack&q[s]=created_at+descInstall the bleeding-edge version of Ransack
To use the latest updates from themainbranch that have not yet been published to RubyGems, use the following Gemfile configuration:gem 'ransack', :github => 'activerecord-hackery/ransack', :branch => 'main'Migrate from Polyamorous to Ransack::ActiveRecord in 6.0
The
Polyamorousnamespace and its require path have been removed. The Active Record integration has moved toRansack::ActiveRecord.Key Mapping Changes:
Ransack::Adapters::ActiveRecord::Base$ ightarrow$Ransack::ActiveRecord::Base(Deprecated in 6.0, removed in 7.0).Polyamorous::Join$ ightarrow$Ransack::ActiveRecord::Join.Polyamorous::InnerJoin$ ightarrow$Arel::Nodes::InnerJoin.Polyamorous::OuterJoin$ ightarrow$Arel::Nodes::OuterJoin.Ransack::Context.for_classandfor_objectare replaced byRansack::Context.register.
Extend Arel to create complex custom predicates
If the required SQL logic is not available in Arel's standard predicates, you can monkey-patchArel::Predicationsto add a new method and then map a Ransack predicate to that new Arel method.# config/initializers/ransack.rb module Arel module Predications def gteq_or_null(other) left = gteq(other) right = eq(nil) left.or(right) end end end Ransack.configure do |config| config.add_predicate 'gteq_or_null', arel_predicate: 'gteq_or_null' endSearch across ActiveRecord associations with Ransack
Ransack allows searching for objects based on attributes of associated models inhas_manyandbelongs_torelationships. To search an association, use the association name followed by the attribute name and a Ransack predicate (e.g.,department_title_contto search for a department's title containing a specific string).# Models class Employee < ActiveRecord::Base belongs_to :supervisor end class Department < ActiveRecord::Base has_many :supervisors end class Supervisor < ActiveRecord::Base belongs_to :department has_many :employees end # Controller def index @q = Supervisor.ransack(params[:q]) @supervisors = @q.result.includes(:department, :employees) end # View (search_form_for) <%= search_form_for @q do |f| %> # Search Supervisor's own last name <%= f.search_field :last_name_cont %> # Search associated Department's title <%= f.search_field :department_title_cont %> # Search associated Employees' first OR last name <%= f.search_field :employees_first_name_or_employees_last_name_cont %> <% end %>Search PostgreSQL JSONB columns using the contains operator
To perform searches on any key within a JSONB column using the PostgreSQL contains operator (@>), you must first install theActiveRecordExtendedgem to provide thecontainsarel predicate. Then, define a custom predicate in the Ransack configuration to handle JSON parsing of the input value.# config/initializers/ransack.rb Ransack.configure do |config| config.add_predicate 'jcont', arel_predicate: 'contains', formatter: proc { |v| JSON.parse(v) } end # Usage: Find entries where the 'data' JSONB column contains the specified key-value pair Person.ransack(data_jcont: '{"group": "experts"}').result.to_sql # Generates: SELECT "persons".* FROM "persons" WHERE "persons"."data" @> '"{\"group\": \"experts\"}"'Publish a Ransack release to RubyGems
Ransack uses an automated workflow where publishing a GitHub Release triggers the build and push to RubyGems via Trusted Publishing.
Release Workflow Steps:
- Merge all feature/fix pull requests into
main. - Merge a version pull request that updates
lib/ransack/version.rb(this should be the final commit before tagging). - Create a GitHub Release:
- Tag:
vX.Y.Z(e.g.,v5.1.0), targetmain. - Release title:
X.Y.Z(e.g.,5.1.0). - Notes: Use 'Generate release notes' and organize by breaking changes, features, and bug fixes.
- Latest release: Ensure 'Set as the latest release' is checked.
- Tag:
- Publish the release to trigger the GitHub Action. The workflow verifies that the tag matches
Ransack::VERSIONbefore pushing to RubyGems.
module Ransack VERSION = '5.1.0' end- Merge all feature/fix pull requests into
Search polymorphic associations with namespaced models
When the target model of a polymorphic association is within a namespace (module), use a quoted string in the search key containing the standard Ruby module notation (e.g.,Module::Class).# Search for locations where the associated Residences::House has a number equal to 100 Location.ransack('locatable_of_Residences::House_type_number_eq' => 100).resultSearch Active Record enum attributes by label
Ransack allows searching Active Recordenumattributes using their human-readable labels instead of their underlying integer values. Ransack automatically casts the label to the corresponding value before building the query.class Person < ApplicationRecord enum :temperament, { sanguine: 1, choleric: 2, melancholic: 3, phlegmatic: 4 } end # Search by single label Person.ransack(temperament_eq: 'choleric').result.to_sql # ... WHERE "people"."temperament" = 2 # Search by multiple labels Person.ransack(temperament_in: ['sanguine', 'choleric']).result.to_sql # ... WHERE "people"."temperament" IN (1, 2)