lib/defaults.rb
# frozen_string_literal: true
module Defaults
require "defaults/version"
def self.execute_callable(value, record)
callable = value.is_a?(Proc) ? value : value.method(:call)
callable.arity == 1 ? value.call(record) : value.call
end
def self.default_value_for_column(model, attribute)
column_info = model.columns_hash[attribute.to_s]
return unless column_info
ActiveRecord::Type.lookup(column_info.type).cast(column_info.default)
end
def self.default_value(model, record, attribute)
value = model.default_values[attribute]
value = Defaults.execute_callable(value, record) if value.respond_to?(:call)
value
end
def self.resolve_default_value(model, record, attribute)
attribute = attribute.to_sym
# Check if value has been defined by `defaults`.
# If it is, try to resolve by checking if value is callable.
# Otherwise returns the default value of that column.
if model.default_values.key?(attribute)
Defaults.default_value(model, record, attribute)
else
Defaults.default_value_for_column(model, attribute)
end
end
def self.included(base)
base.extend ClassMethods
class << base
attr_accessor :default_values
end
end
module ClassMethods
def defaults(attrs)
unless attrs.is_a?(Hash)
raise ArgumentError, "Hash expected; #{attrs.class} given."
end
include InstanceMethods
self.default_values ||= {}
self.default_values.merge!(attrs)
after_initialize :set_default_attributes
end
end
module InstanceMethods
def default_for(name)
Defaults.resolve_default_value(self.class, self, name)
end
private def set_default_attributes
return unless new_record?
self.class.default_values.keys.each do |name|
value = read_attribute(name) if changes[name]
value = default_for(name) if value.blank?
public_send "#{name}=", value
end
end
end
end
ActiveRecord::Base.include Defaults