dependent_protect 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -8,4 +8,5 @@ group :development, :test do
8
8
  gem 'simplecov'
9
9
  gem 'simplecov-rcov'
10
10
  gem 'sqlite3'
11
+ gem 'activerecord', '2.3.14'
11
12
  end
@@ -13,8 +13,8 @@ Gem::Specification.new do |s|
13
13
  ## If your rubyforge_project name is different, then edit it and comment out
14
14
  ## the sub! line in the Rakefile
15
15
  s.name = 'dependent_protect'
16
- s.version = '0.0.3'
17
- s.date = '2012-04-29'
16
+ s.version = '0.0.4'
17
+ s.date = '2012-06-10'
18
18
  s.rubyforge_project = 'dependent_protect'
19
19
 
20
20
  ## Make sure your summary is short. The description may be as long
@@ -66,6 +66,7 @@ Gem::Specification.new do |s|
66
66
  gemfiles/rails2.gemfile
67
67
  gemfiles/rails3.gemfile
68
68
  lib/dependent_protect.rb
69
+ lib/dependent_protect/delete_restriction_error.rb
69
70
  spec/dependent_protect_spec.rb
70
71
  spec/schema.rb
71
72
  spec/spec_helper.rb
@@ -0,0 +1,31 @@
1
+ module ActiveRecord
2
+ # This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations
3
+ # (has_many, has_one) when there is at least 1 child associated instance.
4
+ # ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
5
+ class DeleteRestrictionError < ActiveRecordError #:nodoc:
6
+ def initialize(reflection, record)
7
+ @reflection = reflection
8
+ @record = record
9
+ super(basic_message)
10
+ end
11
+
12
+ def basic_message
13
+ single = @reflection.class_name.to_s.underscore.gsub('_', ' ')
14
+ if [:has_many, :has_and_belongs_to_many].include?(@reflection.macro)
15
+ count = @record.send(@reflection.name).count
16
+ type = count == 1 ? single : single.pluralize
17
+ exist = (count == 1 ? 'exists' : 'exist')
18
+ "Cannot delete record because #{count} dependent #{type} #{exist}"
19
+ else
20
+ "Cannot delete record because dependent #{single} exists"
21
+ end
22
+ end
23
+
24
+ def detailed_message
25
+ count = @record.send(@reflection.name).count
26
+ examples = @record.send(@reflection.name).all(:limit => 5).map{|o| "#{o.id}: #{o.to_s}"}
27
+ examples[4] = "...and #{count - 4} more" if count > 5
28
+ basic_message + "\n\n\nThese include:\n#{examples.join("\n")}"
29
+ end
30
+ end
31
+ end
@@ -1,19 +1,15 @@
1
1
  # DependentProtect
2
2
  #
3
3
  require 'active_record'
4
+ require 'dependent_protect/delete_restriction_error'
4
5
 
5
6
  module DependentProtect
6
- VERSION = '0.0.3'
7
-
8
- DESTROY_PROTECT_ERROR_MESSAGE = 'Cant destroy because there are dependent_count dependent_type dependent on dependee_type dependee.\n\n\nThese include:\ndependent_examples'
7
+ VERSION = '0.0.4'
9
8
 
10
9
  def self.included(base)
11
10
  super
12
11
  base.extend(ClassMethods)
13
12
 
14
- klass = Class.new(ActiveRecord::ActiveRecordError)
15
- ActiveRecord.const_set('DependencyError', klass)
16
-
17
13
  base.class_eval do
18
14
  class << self
19
15
  alias_method_chain :has_one, :protect
@@ -47,49 +43,30 @@ module DependentProtect
47
43
 
48
44
  private
49
45
  def add_dependency_callback!(reflection, options)
50
- # This would break if has_many :dependent behaviour changes. One
51
- # solution is removing both the second when and the else branches but
52
- # the exception message wouldn't be exact.
53
- condition = if reflection.collection?
54
- "record.#{reflection.name}.empty?"
55
- else
56
- "record.#{reflection.name}.nil?"
57
- end
58
-
59
46
  case reflection.options[:dependent]
60
47
  when :rollback
61
48
  options.delete(:dependent)
62
- module_eval "before_destroy, :protect_rollback, :unless => proc{ |record| #{condition} }"
63
- when :raise
49
+ method_name = "dependent_rollback_for_#{reflection.name}".to_sym
50
+ define_method(method_name) do
51
+ method = reflection.collection? ? :empty? : :nil?
52
+ unless send(reflection.name).send(method)
53
+ raise ActiveRecord::Rollback
54
+ end
55
+ end
56
+ before_destroy method_name
57
+ when :restrict
64
58
  options.delete(:dependent)
65
- error = reflection.collection? ? dependency_error(reflection) : "Cannot remove as the associated object is dependent on this."
66
- module_eval <<-METHOD
67
- def protect_raise_#{reflection.name}
68
- raise ActiveRecord::DependencyError, "#{error}"
69
- end
70
- METHOD
71
- module_eval "before_destroy :protect_raise_#{reflection.name}, :unless => proc{ |record| #{condition} }"
59
+ method_name = "dependent_restrict_for_#{reflection.name}".to_sym
60
+ define_method(method_name) do
61
+ method = reflection.collection? ? :empty? : :nil?
62
+ unless send(reflection.name).send(method)
63
+ raise ActiveRecord::DeleteRestrictionError.new(reflection, self)
64
+ end
65
+ end
66
+ before_destroy method_name
72
67
  end
73
68
  end
74
-
75
- def protect_rollback
76
- raise ActiveRecord::Rollback
77
- end
78
-
79
- def dependency_error(reflection)
80
- # TODO: gotta be a more easier approach!
81
- count_code = "#{reflection.name}.count"
82
- first_five_code = reflection.name.to_s+'.first(5).map{|o| "#{o.id}: #{o.to_s}"}'
83
- DESTROY_PROTECT_ERROR_MESSAGE.
84
- gsub('dependent_type', reflection.class_name.to_s.underscore.gsub('_', ' ').pluralize).
85
- gsub('dependent_examples', '#{(' + first_five_code + ' + [("...and #{' + count_code + ' - 5} more" if ' + count_code + ' > 5)]).join("\n")}').
86
- gsub('dependent_count', '#{' + count_code + '}').
87
- gsub('dependee_type', '#{self.class.to_s.underscore.gsub(\'_\', \' \')}').
88
- gsub('dependee', '#{self}')
89
- end
90
-
91
69
  end
92
70
  end
93
71
 
94
-
95
72
  ActiveRecord::Base.send(:include, DependentProtect)
@@ -14,27 +14,44 @@ end
14
14
 
15
15
  class Order < ActiveRecord::Base
16
16
  belongs_to :category
17
- has_one :order_invoice, :dependent => :raise
17
+ has_one :order_invoice, :dependent => :restrict
18
+ def to_s
19
+ "Order #{id}"
20
+ end
18
21
  end
19
22
 
20
23
  class Category < ActiveRecord::Base
21
- has_many :orders, :dependent => :raise
24
+ has_many :orders, :dependent => :restrict
25
+ def to_s
26
+ "Category #{id}"
27
+ end
22
28
  end
23
29
 
24
30
  describe DependentProtect do
25
31
  it 'should restrict has_many relationships' do
26
32
  category = Category.create!
27
- order = Order.create!(:category => category)
28
- lambda{category.reload.destroy}.should raise_error(ActiveRecord::DependencyError)
29
-
30
- order.destroy
33
+ 5.times { Order.create!(:category => category) }
34
+ lambda{category.reload.destroy}.should raise_error(ActiveRecord::DeleteRestrictionError, 'Cannot delete record because 5 dependent orders exist')
35
+ begin
36
+ category.destroy
37
+ rescue ActiveRecord::DeleteRestrictionError => e
38
+ e.detailed_message.should == "Cannot delete record because 5 dependent orders exist\n\n\nThese include:\n1: Order 1\n2: Order 2\n3: Order 3\n4: Order 4\n5: Order 5"
39
+ end
40
+ 1.times { Order.create!(:category => category) }
41
+ begin
42
+ category.destroy
43
+ rescue ActiveRecord::DeleteRestrictionError => e
44
+ e.detailed_message.should == "Cannot delete record because 6 dependent orders exist\n\n\nThese include:\n1: Order 1\n2: Order 2\n3: Order 3\n4: Order 4\n...and 2 more"
45
+ end
46
+
47
+ Order.destroy_all
31
48
  lambda{category.reload.destroy}.should_not raise_error
32
49
  end
33
50
 
34
51
  it 'should restrict has_one relationships' do
35
52
  order = Order.create!
36
53
  order_invoice = OrderInvoice.create!(:order => order)
37
- lambda{order.reload.destroy}.should raise_error(ActiveRecord::DependencyError)
54
+ lambda{order.reload.destroy}.should raise_error(ActiveRecord::DeleteRestrictionError, 'Cannot delete record because dependent order invoice exists')
38
55
 
39
56
  order_invoice.destroy
40
57
  lambda{order.reload.destroy}.should_not raise_error
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependent_protect
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-29 00:00:00.000000000 Z
12
+ date: 2012-06-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -50,6 +50,7 @@ files:
50
50
  - gemfiles/rails2.gemfile
51
51
  - gemfiles/rails3.gemfile
52
52
  - lib/dependent_protect.rb
53
+ - lib/dependent_protect/delete_restriction_error.rb
53
54
  - spec/dependent_protect_spec.rb
54
55
  - spec/schema.rb
55
56
  - spec/spec_helper.rb