Rails allows us to change the type and set the default value of the attribute.
Let’s consider Message
class with sent_at
attribute of type datetime
.
class Message
end
> Message.type_for_attribute(:sent_at)
=> <ActiveRecord::Type::DateTime:0x00007fa01c1fd650 @precision=6, @scale=nil, @limit=nil>
> Message.new.sent_at
=> nil
We can change the sent_at
attribute’s type to integer
in the following way:
class Message
attribute :sent_at, :integer
end
> Message.type_for_attribute(:sent_at)
=> <ActiveModel::Type::Integer:0x00007fa01bd86ba8 @precision=nil, @scale=nil, @limit=nil, @range=-2147483648...2147483648>
We can also set the default value by providing a default
option:
class Message
attribute :sent_at, default: -> { Time.now.utc }
end
> Time.now.utc
=> 2020-10-15 12:10:16 UTC
> Message.new.sent_at
=> 2020-10-15 12:10:16 UTC
But the problem with this is, Rails also changes the type of the attribute when setting the default value, and drops the proper type:
> Message.type_for_attribute(:sent_at)
=> <ActiveModel::Type::Value:0x00007fd16d255418 @precision=nil, @scale=nil, @limit=nil>
Rails 6.1
Rails 6.1 has now fixed this issue.
class Message
attribute :sent_at, default: -> { Time.now.utc }
end
> Time.now.utc
=> 2020-10-15 12:10:16 UTC
> Message.new.sent_at
=> 2020-10-15 12:10:16 UTC
> Message.type_for_attribute(:sent_at)
=> <ActiveRecord::Type::DateTime:0x00007fa01c1fd650 @precision=6, @scale=nil, @limit=nil>
As we can see, the type here is set to ActiveRecord::Type::DateTime
based on the default value,
instead of dropping the type.