dependent_protect 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +1 -0
- data/dependent_protect.gemspec +3 -2
- data/lib/dependent_protect/delete_restriction_error.rb +31 -0
- data/lib/dependent_protect.rb +19 -42
- data/spec/dependent_protect_spec.rb +24 -7
- metadata +3 -2
data/Gemfile
CHANGED
data/dependent_protect.gemspec
CHANGED
@@ -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.
|
17
|
-
s.date = '2012-
|
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
|
data/lib/dependent_protect.rb
CHANGED
@@ -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.
|
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
|
-
|
63
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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 => :
|
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 => :
|
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
|
-
|
28
|
-
lambda{category.reload.destroy}.should raise_error(ActiveRecord::
|
29
|
-
|
30
|
-
|
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::
|
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.
|
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-
|
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
|