ransack
ransack copied to clipboard
Date attributes aren't coerced as datetime when using "attribute attr, :datetime" in model
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 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?
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'"
It would be a great documentation update @leonardofalk, care to make a PR?