Created
June 22, 2010 23:19
-
-
Save zerowidth/449251 to your computer and use it in GitHub Desktop.
DEFAULT literals in AR for postgres defaults (via @copiousfreetime)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Force ActiveRecord to insert the 'DEFAULT' literal for columns which | |
# are non-null but do not have a default that AR understands. | |
# | |
# MySQL is lenient in that if you insert a NULL in something with a default, | |
# the database will just insert the default anyway. Postgres is not so kind: | |
# it'll do exactly what you told it, causing an error. | |
# Usually AR can figure out the default for a column, but if it's a default | |
# value that the connection adapter can't figure out, it'll insert a NULL. | |
# So: allow explicit 'DEFAULT' literals (which is SQL standard) to be used instead | |
# where appropriate. | |
# | |
require 'set' | |
# This exists as a way to make active record use SQL literals in attributes when | |
# creating or updating records | |
module ActiveRecord | |
module ConnectionAdapters | |
class Column | |
class Literal < ::String | |
def quoted_id() self end | |
end | |
def type_cast_with_literal(value) | |
if Literal === value | |
value | |
else | |
type_cast_without_literal(value) | |
end | |
end | |
alias_method_chain :type_cast, :literal | |
end | |
end | |
class Base | |
def self.Literal(*args, &block) | |
::ActiveRecord::ConnectionAdapters::Column::Literal.new(*args, &block) | |
end | |
end | |
def self.Literal(*args, &block) | |
Base.Literal(*args, &block) | |
end | |
end | |
module ActiveRecord | |
class Base | |
cattr_accessor :db_default_columns | |
# force these columns to use DEFAULT on create | |
def self.use_db_default_in_columns(*column_names) | |
self.db_default_columns ||= Set.new | |
column_names.each { |c| self.db_default_columns << c } | |
end | |
before_create :force_default_where_necessary | |
after_save :reload_if_default_forced | |
# force the sql keyword DEFAULT into those columns where there must be a | |
# default value, that is, columns with a default that are not nullable, those | |
# should have the DEFAULT keyword set | |
def force_default_where_necessary | |
@default_forced = false | |
@attributes.each_pair do |name, value| | |
unless value | |
column = column_for_attribute(name) | |
if self.class.db_default_columns.include?(column.name) || (column.has_default? && !column.null) | |
write_attribute name, ActiveRecord::Literal('DEFAULT') | |
@default_forced = true | |
end | |
end | |
end | |
end | |
# if a default was forced, then we have to load the value that was stored in | |
# the db into the record, AR won't, it'll just leave it as 'DEFAULT' which | |
# really won't help much. | |
# | |
def reload_if_default_forced | |
if defined?( @default_forced ) && @default_forced then | |
# force a save of targets ? | |
self.reload | |
@default_forced = false | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment