ransack icon indicating copy to clipboard operation
ransack copied to clipboard

Date attributes aren't coerced as datetime when using "attribute attr, :datetime" in model

Open leonardofalk opened this issue 5 years ago • 3 comments

Hello, I don't know if this is exactly an issue or it is by design, but here it goes anyway:

Problem

Consider this schema and model:

  ...
  create_table "tests", force: :cascade do |t|
    t.string "name"
    t.string "desc"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.date "custom_date"
  end
  ...
class Test < ApplicationRecord
  attribute :custom_date, :datetime
end

Ransack ignores the attribute API method and coerces input into a date format:

2.5.1 :001 > Test.ransack(custom_date_gteq: DateTime.now).result.to_sql
 => "SELECT \"tests\".* FROM \"tests\" WHERE \"tests\".\"custom_date\" >= '2019-05-31'" 
2.5.1 :002 > Test.where(Test.arel_table[:custom_date].gteq(DateTime.now)).to_sql
 => "SELECT \"tests\".* FROM \"tests\" WHERE \"tests\".\"custom_date\" >= '2019-05-31 21:57:43.867422'"
2.5.1 :003 > Test.ransack(created_at_gteq: DateTime.now).result.to_sql
 => "SELECT \"tests\".* FROM \"tests\" WHERE \"tests\".\"created_at\" >= '2019-05-31 22:11:10.838885'" 
 

Reproducible Repository

https://github.com/leonardofalk/ransack-error-test

We actually ran into this because we're using a legacy oracle database which we can't simply change date columns, even though they have datetime values stored in it.

I'll investigate more later, but I think ransack should use the current attribute type, regardless of the original type.

Workaround

class ApplcationRecord < ActiveRecord::Base
  class << self
    def datetime_attribute(*attributes)
      [attributes].flatten.each do |attribute_name|
        attr_name = attribute_name.to_sym

        attribute attr_name, :datetime
        ransacker attr_name, type: :datetime, formatter: ->(datetime) { datetime.to_time }
      end
    end
  end
end

class Post < ApplicationRecord
  datetime_attributes :custom_date
end

leonardofalk avatar May 31 '19 22:05 leonardofalk

@leonardofalk thanks for the repo. It doesn't seem to have the controller or spec setup describing the problem.

Is it possible to submit a PR to this repo with a failing test?

scarroll32 avatar Jan 10 '20 23:01 scarroll32

Well, I don't think this is actually an issue, I think it's more like ransack need you to tell which type is that attribute. The fact is, based on the currrent behaviour, Rails changes when you tell the type, but ransack doesn't follow, you have to force it, calling ransacker :attribute_name, type: :datetime. At first I thought it was a weird behaviour, but I'm fine with it now, if that's how it is supposed to be.

Using Ransack (uses only date, even with attribute :custom_date, :datetime)

2.5.1 :001 > Test.ransack(custom_date_gteq: DateTime.now).result.to_sql
 => "SELECT \"tests\".* FROM \"tests\" WHERE \"tests\".\"custom_date\" >= '2019-05-31'" 

Using AREL (uses date and time)

2.5.1 :002 > Test.where(Test.arel_table[:custom_date].gteq(DateTime.now)).to_sql
 => "SELECT \"tests\".* FROM \"tests\" WHERE \"tests\".\"custom_date\" >= '2019-05-31 21:57:43.867422'"

leonardofalk avatar Jan 11 '20 11:01 leonardofalk

It would be a great documentation update @leonardofalk, care to make a PR?

scarroll32 avatar Nov 27 '20 19:11 scarroll32