Skip to content

Instantly share code, notes, and snippets.

@glytch
Created March 18, 2014 20:29
Show Gist options
  • Save glytch/9628863 to your computer and use it in GitHub Desktop.
Save glytch/9628863 to your computer and use it in GitHub Desktop.
postgis/posgresql composite primary key patch
module CompositePrimaryKeys
class CompositeKeys
def to_sym
join('_').to_sym
end
end
module ActiveRecord
module Persistence
def update_record(attribute_names = @attributes.keys)
return super(attribute_names) unless composite?
klass = self.class
if should_record_timestamps?
current_time = current_time_from_proper_timezone
timestamp_attributes_for_update_in_model.each do |column|
column = column.to_s
next if attribute_changed?(column)
write_attribute(column, current_time)
end
end
attributes_with_values = arel_attributes_with_values_for_update(attribute_names)
return 0 if attributes_with_values.empty?
column_hash = klass.connection.schema_cache.columns_hash klass.table_name
db_columns_with_values = attributes_with_values.map { |attr,value|
real_column = column_hash[attr.name]
[real_column, value]
}
bind_attrs = attributes_with_values.dup
bind_attrs.keys.each_with_index do |column, i|
real_column = db_columns_with_values[i].first
bind_attrs[column] = klass.connection.substitute_at(real_column, i)
end
if !can_change_primary_key? and primary_key_changed?
raise ActiveRecord::CompositeKeyError, "Cannot update primary key values without ActiveModel::Dirty"
elsif primary_key_changed?
stmt = klass.unscoped.where(primary_key_was).arel.compile_update(bind_attrs)
else
stmt = klass.unscoped.where(ids_hash).arel.compile_update(bind_attrs)
end
klass.connection.update stmt.to_sql, 'SQL', db_columns_with_values
end
end
end
end
module ActiveRecord
module Persistence
private
alias_method :original_create_record, :create_record
def create_record(attribute_names = @attributes.keys)
return(original_create_record(attribute_names)) unless composite?
attributes_values = arel_attributes_with_values_for_create(attribute_names)
new_id = self.class.unscoped.insert attributes_values
# uncomment for rails > 4.0.0
#
#default_function = self.class.columns_hash[self.class.primary_keys.first].default_function
#
#if default_function && default_function =~ /nextval/
# self[self.class.primary_keys.first] ||= new_id
#elsif self.class.primary_key
# self.id ||= new_id
#end
self[self.class.primary_keys.first] = new_id if self.class.primary_keys.is_a?(Array)
self.id ||= new_id
@new_record = false
id
end
end
end
module ActiveRecord::Associations::Builder
class BelongsTo < SingularAssociation
def add_touch_callbacks(reflection)
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
def belongs_to_touch_after_save_or_destroy_for_#{name}
foreign_key_field = #{reflection.foreign_key.inspect}
if foreign_key_field.is_a?(Array)
old_foreign_id = foreign_key_field.map{ |field| attribute_was(field) }
else
old_foreign_id = attribute_was(foreign_key_field)
end
if old_foreign_id
klass = association(#{name.inspect}).klass
old_record = klass.find_by(klass.primary_key => old_foreign_id)
if old_record
old_record.touch #{options[:touch].inspect if options[:touch] != true}
end
end
record = #{name}
unless record.nil? || record.new_record?
record.touch #{options[:touch].inspect if options[:touch] != true}
end
end
CODE
model.after_save "belongs_to_touch_after_save_or_destroy_for_#{name}"
model.after_touch "belongs_to_touch_after_save_or_destroy_for_#{name}"
model.after_destroy "belongs_to_touch_after_save_or_destroy_for_#{name}"
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment