Skip to content

Commit

Permalink
Add dependent protect to belongs_to and has_one
Browse files Browse the repository at this point in the history
Keep dependent protect option in reflection
  • Loading branch information
Sergio committed Jul 1, 2009
1 parent d3de3f6 commit c353529
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 26 deletions.
22 changes: 11 additions & 11 deletions README
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
= dependent => :protect option

Adds a new option :protect for the parameter <tt>:depends</tt> from +has_many+
method. This option forbids destroying records with associated records in a
association created with <tt>:dependent => :protect</tt> option, more or less
like <tt>ON DELETE RESTRICT</tt> SQL statement. If you try to destroy a record with
associated records it will raise a
Adds a new option :protect for the parameter <tt>:depends</tt> from +has_many+,
+has_one+ and +belongs_to+ methods. This option forbids destroying records with
associated records in an association created with <tt>:dependent => :protect</tt>
option, more or less like <tt>ON DELETE RESTRICT</tt> SQL statement. If you try
to destroy a record with associated records it will raise an
ActiveRecord::ReferentialIntegrityProtectionError (defined also in this
plugin).

Based on the idea and the code from [email protected] in Ruby on Rails
ticket #3837 (http://dev.rubyonrails.org/ticket/3837).
ticket #3837 (http://dev.rubyonrails.org/ticket/3837) and plugin from Daniel
Rodríguez Troitiño <[email protected]> in
http://svn.ruido-blanco.net/dependent_protect/

You can download this plugin at:

http://svn.ruido-blanco.net/dependent_protect/trunk

== Author

Daniel Rodr�guez Troiti�o <[email protected]>, based on the ideas and
the code from <[email protected]>.
Sergio Cambra, based in a plugin from Daniel Rodríguez Troitiño
<[email protected]>, and the ideas and the code from
<[email protected]>.
57 changes: 42 additions & 15 deletions lib/dependent_protect.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,66 @@ module DependentProtect
def self.append_features(base)
super
base.extend(ClassMethods)

ripe_class = Class.new(ActiveRecord::ActiveRecordError)
k = ActiveRecord.const_set('ReferentialIntegrityProtectionError', ripe_class)

base.class_eval do
class << self
alias_method :has_many_without_protect, :has_many
alias_method :has_many, :has_many_with_protect
alias_method_chain :has_many, :protect
alias_method_chain :has_one, :protect
alias_method_chain :belongs_to, :protect
end
end
end

module ClassMethods
# We should be aliasing configure_dependency_for_has_many but that method
# is private so we can't. We alias has_many instead trying to be as fair
# as we can to the original behaviour.
def has_many_with_protect(association_id, options = {}, &extension) #:nodoc:
reflection = create_reflection(:has_many, association_id, options, self)

# This would break if has_many :dependent behaviour changes. One
# solution is removing both the second when and the else branches but
# the exception message wouldn't be exact.
case reflection.options[:dependent]
when :protect

if reflection.options[:dependent] == :protect
module_eval "before_destroy 'raise ActiveRecord::ReferentialIntegrityProtectionError, \"Can\\'t destroy because there\\'s at least one #{reflection.class_name} in this #{self.class_name}\" if self.#{reflection.name}.find(:first)'"
options = options.clone
options.delete(:dependent)
when true, :destroy, :delete_all, :nullify, nil, false
#pass
else
raise ArgumentError, 'The :dependent option expects either true, :destroy, :delete_all, :nullify or :protect'
end

has_many_without_protect(association_id, options, &extension)
write_inheritable_hash :reflections, association_id => reflection
end

# We should be aliasing configure_dependency_for_has_one but that method
# is private so we can't. We alias has_many instead trying to be as fair
# as we can to the original behaviour.
def has_one_with_protect(association_id, options = {}, &extension) #:nodoc:
reflection = create_reflection(:has_one, association_id, options, self)

if reflection.options[:dependent] == :protect
module_eval "before_destroy 'raise ActiveRecord::ReferentialIntegrityProtectionError, \"Can\\'t destroy because there\\'s a #{reflection.class_name} in this #{self.class_name}\" if self.#{reflection.name}'"
options = options.clone
options.delete(:dependent)
end

has_one_without_protect(association_id, options, &extension)
write_inheritable_hash :reflections, association_id => reflection
end

# We should be aliasing configure_dependency_for_belongs_to but that method
# is private so we can't. We alias has_many instead trying to be as fair
# as we can to the original behaviour.
def belongs_to_with_protect(association_id, options = {}, &extension) #:nodoc:
reflection = create_reflection(:belongs_to, association_id, options, self)

if reflection.options[:dependent] == :protect
module_eval "before_destroy 'raise ActiveRecord::ReferentialIntegrityProtectionError, \"Can\\'t destroy because there\\'s a #{reflection.class_name} in this #{self.class_name}\" if self.#{reflection.name}'"
options = options.clone
options.delete(:dependent)
end

belongs_to_without_protect(association_id, options, &extension)
write_inheritable_hash :reflections, association_id => reflection
end
end
end

0 comments on commit c353529

Please sign in to comment.