Keep Defaults
Prevent ActiveRecord attributes for not null columns with default values from being set to nil.
Why is this necessary? Take this example:
class OrderItem < ApplicationRecord
# Has column: total numeric(11,2) not null default 0
# Validation etc...
end
class Order < ApplicationRecord
# Has columns: total and taxes, both numeric(11,2) not null default 0
# Validation etc...
has_many :order_items
def total
order_items.sum(&:total) + taxes
end
endThe columns have a default value of 0, but the attributes can still be set to nil.
This can make for code that is far from bulletproof:
o = Order.new
o.total # 0
o.taxes = nil
o.total # 💥 TypeError: nil can't be coerced into FixnumTo fix you can do something like:
class Order < ApplicationRecord
def total
order_items.sum(&:total) + taxes.to_f
end
endBut OrderItem#total can be set to nil too. You can do:
class OrderItem < ApplicationRecord
def total
super || 0
end
endBut what about the other contexts in which these can be called or the other attributes you may have? This can get tedious.
With Keep Defaults:
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
# Must come after setting abstract_class
include KeepDefaults
endo = Order.new
o.total # 0
o.taxes = nil
o.total # 0
o.taxes # 0Now if an attribute is set to nil it will retain —or be returned to— its default value instead.
Installation
Add this line to your application's Gemfile:
gem "keep_defaults"Or
gem install keep_defaultsUsage
To use everywhere add to ApplicationRecord:
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
# Must come after setting abstract_class
include KeepDefaults
endTo use for a specific class add it directly to that class:
class Order < ApplicationRecord
include KeepDefaults
endIf your class sets its table via table_name then include KeepDefaults must come after that.
Using With an Existing Column
To ensure that an attribute always returns its default value you must make sure its DB column does not allow null and has a default.
For example, given the column orders.taxes that does not meet these requirements, you can add a migration containing the following:
def change
change_column :orders, :taxes, :integer, :null => false, :default => 0
endKnown Issues
Classes That Explicitly Set table_name and Have an Ancestor Class That includes KeepDefaults
In this case include KeepDefaults must be taken out of the ancestor classes in added to all the subclasses.
License
The gem is available as open source under the terms of the MIT License.