95 lines
2.8 KiB
Ruby
95 lines
2.8 KiB
Ruby
|
# == Default Values
|
||
|
#
|
||
|
# Allows default attributes values to be declared. The callback used to set the values can be controlled via options.
|
||
|
#
|
||
|
# The following option keys are allowed:
|
||
|
# `:on`. If omitted, it defaults to :initialize.
|
||
|
# :on may be one of the following: :initialize, :create, :update, :save
|
||
|
#
|
||
|
# `:empty`. If omitted, it defaults to only updating `nil` values
|
||
|
# :empty should be a lambda (or proc) that accepts the current value of the attribute and returns true if the value
|
||
|
# should be replaced by the default
|
||
|
#
|
||
|
# Examples:
|
||
|
# Sets `attr` to 'default' and `attr2` to 'default' any time the class is instantiated
|
||
|
# default_values {
|
||
|
# attr: 'default',
|
||
|
# attr2: 'default'
|
||
|
# }
|
||
|
#
|
||
|
# Sets `roles` to [:super_admin] only when saving a new object and only if roles is an empty array (it will not update a nil value)
|
||
|
# default_values({roles: [:super_admin]}, {on: :create, empty: ->(v) { v == [] }})
|
||
|
#
|
||
|
module DefaultValues
|
||
|
extend ActiveSupport::Concern
|
||
|
|
||
|
DEFAULT_OPTIONS = { on: :initialize }
|
||
|
|
||
|
DefaultValue = Struct.new(:value, :options)
|
||
|
|
||
|
included do
|
||
|
class_attribute :_default_values
|
||
|
self._default_values = {}
|
||
|
|
||
|
after_initialize :set_default_values_initialize
|
||
|
before_create :set_default_values_create
|
||
|
before_update :set_default_values_update
|
||
|
before_save :set_default_values_save
|
||
|
end
|
||
|
|
||
|
def set_default_values_initialize
|
||
|
set_default_values(:initialize)
|
||
|
end
|
||
|
|
||
|
def set_default_values_create
|
||
|
set_default_values(:create)
|
||
|
end
|
||
|
|
||
|
def set_default_values_update
|
||
|
set_default_values(:update)
|
||
|
end
|
||
|
|
||
|
def set_default_values_save
|
||
|
set_default_values(:save)
|
||
|
end
|
||
|
|
||
|
def set_default_values(on)
|
||
|
_default_values.each do |k, dv|
|
||
|
if dv.options[:on].to_sym == on
|
||
|
v = dv.value
|
||
|
v_lambda = Proc === v ? v : -> { v }
|
||
|
|
||
|
tester = dv.options[:empty] || ->(x) { x.nil? }
|
||
|
|
||
|
self.send("#{k}=", self.instance_exec(&v_lambda)) if self.respond_to?(k) && tester.call(self.send(k))
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
module ClassMethods
|
||
|
# Copy defaults on inheritance.
|
||
|
def inherited(base)
|
||
|
base._default_values = _default_values.dup
|
||
|
super
|
||
|
end
|
||
|
|
||
|
def default_values(defaults_hash, options = {})
|
||
|
options = DEFAULT_OPTIONS.merge(options.symbolize_keys)
|
||
|
|
||
|
valid_ons = [:initialize, :create, :update, :save]
|
||
|
|
||
|
unless valid_ons.include? options[:on]
|
||
|
raise "Invalid options[:on] value: [#{options[:on]}]. Must be one of these symbols: #{valid_ons.join(', ')}"
|
||
|
end
|
||
|
|
||
|
if options[:empty].present?
|
||
|
proc = options[:empty]
|
||
|
raise "Invalid options[:empty]. Must be a Proc or Lambda with an arity of 1" unless (proc.is_a?(Proc) && proc.arity == 1)
|
||
|
end
|
||
|
|
||
|
defaults_hash.each do |k, v|
|
||
|
_default_values[k] = DefaultValue.new(v, options)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|