1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
# frozen-string-literal: true
module Sequel
module Plugins
# The defaults_setter plugin makes the column getter methods return the default
# values for new objects, if the values have not already been set. Example:
#
# # column a default NULL
# # column b default 2
# album = Album.new
# album.a # => nil
# album.b # => 2
# album = Album.new(a: 1, b: 3)
# album.a # => 1
# album.b # => 3
#
# You can manually set default values as well:
#
# Album.default_values[:a] = 4
# Album.new.a # => 4
#
# You can also provide procs to set default values:
#
# Album.default_values[:a] = lambda{Date.today}
# Album.new.a # => Date.today
#
# By default, default values returned are not cached:
#
# Album.new.a.equal?(Album.new.a) # => false
#
# However, you can turn on caching of default values:
#
# Album.plugin :defaults_setter, cache: true
# Album.new.a.equal?(Album.new.a) # => false
#
# Note that if the cache is turned on, the cached values are stored in
# the values hash:
#
# Album.plugin :defaults_setter, cache: true
# album = Album.new
# album.values # => {}
# album.a
# album.values # => {:a => Date.today}
#
# Usage:
#
# # Make all model subclass instances set defaults (called before loading subclasses)
# Sequel::Model.plugin :defaults_setter
#
# # Make the Album class set defaults
# Album.plugin :defaults_setter
module DefaultsSetter
# Set the default values based on the model schema. Options:
# :cache :: Cache default values returned in the model's values hash.
def self.configure(model, opts=OPTS)
model.instance_exec do
set_default_values
@cache_default_values = opts[:cache] if opts.has_key?(:cache)
end
end
module ClassMethods
# The default values to use for this model. A hash with column symbol
# keys and default values. If the default values respond to +call+, it will be called
# to get the value, otherwise the value will be used directly. You can manually modify
# this hash to set specific default values, by default the ones will be parsed from the database.
attr_reader :default_values
Plugins.after_set_dataset(self, :set_default_values)
Plugins.inherited_instance_variables(self, :@default_values=>:dup, :@cache_default_values=>nil)
# Whether default values should be cached in the values hash after being retrieved.
def cache_default_values?
@cache_default_values
end
# Freeze default values when freezing model class
def freeze
@default_values.freeze
super
end
private
# Parse the cached database schema for this model and set the default values appropriately.
def set_default_values
h = {}
if @db_schema
@db_schema.each do |k, v|
if v[:callable_default]
h[k] = v[:callable_default]
elsif !v[:ruby_default].nil?
h[k] = convert_default_value(v[:ruby_default])
end
end
end
@default_values = h.merge!(@default_values || OPTS)
end
# Handle the CURRENT_DATE and CURRENT_TIMESTAMP values specially by returning an appropriate Date or
# Time/DateTime value.
def convert_default_value(v)
case v
when Sequel::CURRENT_DATE
lambda{Date.today}
when Sequel::CURRENT_TIMESTAMP
lambda{dataset.current_datetime}
else
v
end
end
end
module InstanceMethods
# Use default value for a new record if values doesn't already contain an entry for it.
def [](k)
if new? && !values.has_key?(k)
v = model.default_values.fetch(k){return}
v = v.call if v.respond_to?(:call)
values[k] = v if model.cache_default_values?
v
else
super
end
end
end
end
end
end
|