Skip to content

Instantly share code, notes, and snippets.

@zerowidth
Created June 22, 2010 23:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zerowidth/449251 to your computer and use it in GitHub Desktop.
Save zerowidth/449251 to your computer and use it in GitHub Desktop.
DEFAULT literals in AR for postgres defaults (via @copiousfreetime)
# 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