Ransack follows semantic versioning: a major release is the only place a breaking change can land. This page lists what changed in each major release and what to do about it. The full list of changes in every release is in the GitHub release notes.

Upgrading to 6.0

Ruby 3.2 or later is required

Ruby 3.1 reached end of life in March 2025 and no longer receives security fixes. Ransack 6.0 requires Ruby 3.2 or later, which is also the floor for Rails 8.0. If you are on Ruby 3.1 you are already limited to Rails 7.2; stay on Ransack 5.x until you can upgrade Ruby.

cont, start and end are case-sensitive on PostgreSQL

Ransack never told Arel whether a LIKE should be case-sensitive, and Arel’s PostgreSQL visitor renders the default as ILIKE. So on PostgreSQL cont, start, end and matches all ignored case, while the docs said cont used LIKE. They now do: cont, start and end are LIKE; i_cont and the new i_start and i_end are ILIKE. A PostgreSQL application that relied on those predicates ignoring case should switch to the i_ form. Nothing raises when it does not; the search just stops matching rows whose case differs, so check filters that are wired up by another library. ActiveAdmin’s string filter, for example, offers cont, start and end. MySQL and SQLite are unaffected; their LIKE follows the column’s collation as before. See Search Matchers.

Database dialects are detected from the adapter class

A few places generate different SQL per database. They used to compare the adapter’s name against a list ("PostgreSQL", "PostGIS", "Mysql2", "Trilogy"), so an adapter that was not on the list — even one built on a known adapter — got the wrong SQL, and PostGIS was carried as a development dependency just to keep it on the list. The dialect is now read from the adapter class’s ancestry, so PostGIS is PostgreSQL without being named, and config.dialect overrides the detection. The activerecord-postgis-adapter development dependency and its CI job are gone. See Configuration.

Strict searches check more

Under ransack! or ignore_unknown_conditions = false, two things that were silently accepted now raise Ransack::InvalidSearchError:

  • a sort on an attribute that is not ransortable or does not exist, which used to drop the whole ORDER BY (see Sorting);
  • a condition name that mixes _and_ and _or_, which used to apply the first combinator to every attribute (see Simple Mode).

Permissive searches are unchanged.

Values are cast by the declared attribute type

A column redeclared with attribute :name, :datetime is now cast as a datetime; before, the schema column’s type won. A Date given for a datetime column now means midnight in Time.zone; before, it was midnight in the server’s system time zone, which moved the day boundary when the two differed. See Search Matchers.

Scopes that skip sanitizing receive false

A scope listed in ransackable_scopes_skip_sanitize_args now receives a bare false instead of being skipped, so it can be driven by a yes / no / any select. Other scopes still treat false as an unticked checkbox. See Other notes.

Joins already on the relation are reused

A search on a relation that already joins a table, through joins, left_outer_joins, an eager-loaded includes or a previous search, now reuses that join. Before, the association was joined again under a fresh alias, which multiplied rows for a has_many and could bind a condition to the wrong one of two joins to the same table. A query that depended on the duplicate is unlikely, but row counts from such searches will change. The joins Ransack adds are now stashed on the relation’s left_outer_joins rather than its joins; code that inspected joins_values for them should look at left_outer_joins_values. See Associations.

sorts= replaces instead of appending

search.sorts = 'name asc' now sets exactly that sort; before, it added to whatever sorts the search already had, and sorts = [] did nothing. Code that relied on the append should call build_sort for each additional sort. See Sorting.

A scope wins over an attribute of the same name in the writer

Search#build already applied a ransackable_scopes entry before looking for an attribute of the same name; the attribute writer and reader used by form builders (search.salary = 100, f.check_box :salary) checked attributes first and silently skipped the scope. They agree now.

ransack_alias resolves through associations and in compounds

author_name_cont (alias on Author) and text_or_author_name_cont used to produce SQL referring to columns that do not exist; they now expand the alias. The alias name itself no longer needs to be in ransackable_attributes, and an alias whose target does not exist raises under ransack! like any unknown attribute. See Ransack Aliases.

Polyamorous is gone; Ransack::Adapters::ActiveRecord is deprecated

The Active Record integration now lives under Ransack::ActiveRecord. See How Ransack is built for the layout.

  • Ransack::Adapters::ActiveRecord::Base and ::Context still resolve, with a deprecation warning, and will be removed in 7.0. An initializer that reopens Ransack::Adapters::ActiveRecord::Base to change the ransackable_* defaults should reopen Ransack::ActiveRecord::Base instead.
  • The Polyamorous namespace and the polyamorous/polyamorous require path are removed. Polyamorous::Join is Ransack::ActiveRecord::Join; Polyamorous::InnerJoin and OuterJoin were aliases for Arel::Nodes::InnerJoin and Arel::Nodes::OuterJoin, so use those.
  • Ransack::SUPPORTS_ATTRIBUTE_ALIAS is removed; every supported Active Record version supports attribute aliases.
  • Ransack::Context.for_class and for_object are replaced by a resolver registry, Ransack::Context.register, so another ORM can plug in without patching Ransack.

A model without allowlists can be searched through strong parameters

ransackable_attributes, ransackable_associations and ransortable_attributes are now optional for a model that is only searched through a controller that permits the search keys. When the model defines none of them, a search built from permitted ActionController::Parameters treats the controller’s permit list as the authorization, and every permitted key that names a real column, ransacker, alias or association is searchable. A model that defines a list is always asked, so nothing changes for an existing model until you remove its lists. A plain Hash, or unpermitted parameters, still need the lists and raise without them, and ransackable_scopes still applies to every search. See Authorization.

Upgrading to 5.0

Ransack 5.0 shipped on 2026-09-21 with these behaviour changes. Each is described in more detail on the page it links to.

  • LIKE wildcards (% and _) in search values are escaped on every database, and every LIKE carries an explicit ESCAPE clause. A value of 100% now matches the literal text 100%. See Search Matchers.
  • Empty arrays and blank strings can be treated as real filter values with config.ignore_blank_values = false. See Configuration.
  • An unknown combinator (m:) raises under ransack! or ignore_unknown_conditions: false rather than silently meaning and, and OR / :or are normalised to or. See Advanced Mode.
  • config.postgres_fields_sort_option is renamed config.fields_sort_option, because NULLS FIRST / NULLS LAST are emitted through Arel and work on every database that supports them. The old name still works. See Sorting.
  • The monkey patch on ActionView::Helpers::Tags::Base#value is gone. Form fields read their values through the search object’s own readers, so nothing changes for search_form_for, but code that depended on the patch indirectly should be checked.