ransack icon indicating copy to clipboard operation
ransack copied to clipboard

Fix date predicate formatter receiving String instead of Date object

Open Copilot opened this issue 3 months ago • 1 comments

Problem

After upgrading from Ransack 4.3.0 to 4.4.0, custom predicate formatters with type: :date started receiving String values instead of properly casted Date/Time objects. This caused errors when trying to call date methods like .end_of_day:

Ransack.configure do |config|
  config.add_predicate 'lteq_eod',
                       arel_predicate: 'lteq',
                       formatter: proc { |value| value.end_of_day },  # Error: undefined method `end_of_day' for String
                       type: :date
end

Root Cause

The Value#cast method uses a Ruby case statement to match type values against symbols:

case type
when :date
  cast_to_date(value)
when :datetime, :timestamp, :time, :timestamptz
  cast_to_time(value)
else
  cast_to_string(value)  # String types fall through here!
end

When type is a String ("date") instead of a Symbol (:date), it doesn't match any of the when clauses and falls through to the else branch, which returns the original string value instead of a Date object.

Solution

Added defensive type conversion at the beginning of the Value#cast method to handle both string and symbol types:

def cast(type)
  # Convert type to symbol if it's a string to ensure proper case matching
  type = type.to_sym if type.is_a?(String)
  
  case type
  when :date
    cast_to_date(value)
  ...
  end
end

This ensures that string types like "date", "datetime", etc. are automatically converted to symbols before case matching, allowing them to be cast correctly.

Changes

  • lib/ransack/nodes/value.rb: Added 3 lines to convert string types to symbols
  • spec/ransack/nodes/value_spec.rb: Added test verifying string type 'date' works correctly
  • spec/ransack/predicate_spec.rb: Added comprehensive test for custom predicates with date formatters

Impact

  • ✅ Minimal change: only 3 lines of production code
  • ✅ Surgical fix: no changes to existing behavior for symbol types
  • ✅ Backward compatible: handles both string and symbol types
  • ✅ Well tested: comprehensive test coverage added

Example Usage

After this fix, the following code works correctly:

Ransack.configure do |config|
  config.add_predicate 'lteq_eod',
                       arel_predicate: 'lteq',
                       formatter: proc { |value| value.end_of_day },  # value is now a Date
                       type: :date
end

# Usage in a search
@search = Person.ransack(life_start_lteq_eod: '2022-05-23')

The formatter now correctly receives a Date object and can call .end_of_day without errors.

Fixes #XXXX

Original prompt

This section details on the original issue you should resolve

<issue_title>Date predicate is formatted as string</issue_title> <issue_description>Hello,

in previous versions, block in this code use to receive a time object:

Ransack.configure do |config|
  config.add_predicate 'lteq_eod',
                       arel_predicate: 'lteq',
                       formatter: proc { |value| value.end_of_day },
                       type: :date
end

After upgrading from 4.3.0 to 4.4.0, the proc block started receiving an string.</issue_description>

Comments on the Issue (you are @copilot in this section)

Fixes activerecord-hackery/ransack#1649

💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

Copilot avatar Oct 04 '25 21:10 Copilot

@dcrec1 are you able to review this PR? https://github.com/activerecord-hackery/ransack/issues/1649#issuecomment-3368568273

scarroll32 avatar Oct 06 '25 17:10 scarroll32