snusnu-dm-accepts_nested_attributes 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +513 -0
- data/Manifest.txt +1 -2
- data/README.textile +20 -186
- data/TODO +3 -5
- data/lib/dm-accepts_nested_attributes.rb +0 -2
- data/lib/dm-accepts_nested_attributes/model.rb +46 -18
- data/lib/dm-accepts_nested_attributes/resource.rb +115 -39
- data/lib/dm-accepts_nested_attributes/transactional_save.rb +12 -1
- data/lib/dm-accepts_nested_attributes/version.rb +1 -1
- data/spec/fixtures/person.rb +15 -3
- data/spec/fixtures/profile.rb +2 -0
- data/spec/fixtures/project.rb +10 -4
- data/spec/lib/constraint_support.rb +11 -0
- data/spec/shared/belongs_to_spec.rb +18 -3
- data/spec/shared/has_1_spec.rb +41 -20
- data/spec/shared/has_n_spec.rb +32 -24
- data/spec/shared/has_n_through_spec.rb +45 -28
- data/spec/spec_helper.rb +3 -21
- metadata +5 -5
- data/lib/dm-accepts_nested_attributes/save.rb +0 -13
- data/spec/lib/rspec_tmbundle_support.rb +0 -35
@@ -3,7 +3,18 @@ module DataMapper
|
|
3
3
|
|
4
4
|
module TransactionalSave
|
5
5
|
|
6
|
-
|
6
|
+
##
|
7
|
+
# Overrides @see DataMapper::Resource#save to perform inside a transaction.
|
8
|
+
# The current implementation simply wraps the saving of the complete object tree
|
9
|
+
# inside a transaction and rolls back in case any exceptions are raised,
|
10
|
+
# or any of the calls to
|
11
|
+
#
|
12
|
+
# @see DataMapper::Resource#save
|
13
|
+
#
|
14
|
+
# @return [true, false]
|
15
|
+
# true if all related resources were saved properly
|
16
|
+
#
|
17
|
+
def save(*)
|
7
18
|
saved = false
|
8
19
|
transaction { |t| t.rollback unless saved = super }
|
9
20
|
saved
|
data/spec/fixtures/person.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
class Person
|
2
2
|
|
3
3
|
include DataMapper::Resource
|
4
|
+
extend ConstraintSupport
|
4
5
|
|
5
6
|
# properties
|
6
7
|
|
@@ -9,8 +10,19 @@ class Person
|
|
9
10
|
|
10
11
|
# associations
|
11
12
|
|
12
|
-
has 1, :profile
|
13
|
-
|
14
|
-
|
13
|
+
has 1, :profile,
|
14
|
+
constraint_options(:destroy)
|
15
|
+
|
16
|
+
# has 1, :address,
|
17
|
+
# :through => :profile
|
18
|
+
|
19
|
+
has n, :project_memberships,
|
20
|
+
constraint_options(:destroy)
|
21
|
+
|
22
|
+
has n, :projects,
|
23
|
+
:through => :project_memberships
|
24
|
+
|
25
|
+
# has n, :tasks,
|
26
|
+
# :through => :projects
|
15
27
|
|
16
28
|
end
|
data/spec/fixtures/profile.rb
CHANGED
data/spec/fixtures/project.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
class Project
|
2
2
|
|
3
3
|
include DataMapper::Resource
|
4
|
+
extend ConstraintSupport
|
4
5
|
|
5
6
|
# properties
|
6
7
|
|
@@ -9,8 +10,13 @@ class Project
|
|
9
10
|
|
10
11
|
# associations
|
11
12
|
|
12
|
-
has n, :tasks
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
has n, :tasks,
|
14
|
+
constraint_options(:destroy)
|
15
|
+
|
16
|
+
has n, :project_memberships,
|
17
|
+
constraint_options(:destroy)
|
18
|
+
|
19
|
+
has n, :people,
|
20
|
+
:through => :project_memberships
|
21
|
+
|
16
22
|
end
|
@@ -55,6 +55,10 @@ describe "every accessible belongs_to association with a valid reject_if proc",
|
|
55
55
|
Person.all.size.should == 0
|
56
56
|
|
57
57
|
@profile.person_attributes = { :name => 'Martin' }
|
58
|
+
|
59
|
+
Profile.all.size.should == 0
|
60
|
+
Person.all.size.should == 0
|
61
|
+
|
58
62
|
@profile.save.should be_false
|
59
63
|
|
60
64
|
Profile.all.size.should == 0
|
@@ -70,6 +74,10 @@ describe "every accessible belongs_to association with no reject_if proc", :shar
|
|
70
74
|
Person.all.size.should == 0
|
71
75
|
|
72
76
|
@profile.person_attributes = { :name => 'Martin' }
|
77
|
+
|
78
|
+
Profile.all.size.should == 0
|
79
|
+
Person.all.size.should == 0
|
80
|
+
|
73
81
|
@profile.save.should be_true
|
74
82
|
|
75
83
|
Profile.all.size.should == 1
|
@@ -87,12 +95,16 @@ describe "every accessible belongs_to association with :allow_destroy => false",
|
|
87
95
|
|
88
96
|
person = Person.create(:name => 'Martin')
|
89
97
|
@profile.person = person
|
90
|
-
@profile.save
|
98
|
+
@profile.save
|
91
99
|
|
92
100
|
Profile.all.size.should == 1
|
93
101
|
Person.all.size.should == 1
|
94
102
|
|
95
103
|
@profile.person_attributes = { :id => person.id, :_delete => true }
|
104
|
+
|
105
|
+
Profile.all.size.should == 1
|
106
|
+
Person.all.size.should == 1
|
107
|
+
|
96
108
|
@profile.save
|
97
109
|
|
98
110
|
Profile.all.size.should == 1
|
@@ -115,10 +127,13 @@ describe "every accessible belongs_to association with :allow_destroy => true",
|
|
115
127
|
Person.all.size.should == 1
|
116
128
|
|
117
129
|
@profile.person_attributes = { :id => person.id, :_delete => true }
|
130
|
+
|
131
|
+
Profile.all.size.should == 1
|
132
|
+
Person.all.size.should == 1
|
133
|
+
|
118
134
|
@profile.save
|
119
135
|
|
120
|
-
|
121
|
-
Person.all.size.should == 1
|
136
|
+
Person.all.size.should == 0
|
122
137
|
|
123
138
|
# TODO also test this behavior in situations where setting the FK to nil is allowed
|
124
139
|
|
data/spec/shared/has_1_spec.rb
CHANGED
@@ -5,6 +5,9 @@ describe "every accessible has(1) association", :shared => true do
|
|
5
5
|
profile = Profile.create(:person_id => @person.id, :nick => 'snusnu')
|
6
6
|
@person.reload
|
7
7
|
|
8
|
+
Person.all.size.should == 1
|
9
|
+
Profile.all.size.should == 1
|
10
|
+
|
8
11
|
@person.profile_attributes = { :id => profile.id, :nick => 'still snusnu somehow' }
|
9
12
|
@person.save.should be_true
|
10
13
|
|
@@ -13,24 +16,6 @@ describe "every accessible has(1) association", :shared => true do
|
|
13
16
|
Profile.first.nick.should == 'still snusnu somehow'
|
14
17
|
end
|
15
18
|
|
16
|
-
it "should perform atomic commits" do
|
17
|
-
|
18
|
-
# related resource is invalid
|
19
|
-
@person.profile_attributes = { :nick => nil } # will fail because of validations
|
20
|
-
@person.save.should be_false
|
21
|
-
|
22
|
-
Person.all.size.should == 0
|
23
|
-
Profile.all.size.should == 0
|
24
|
-
|
25
|
-
# self is invalid
|
26
|
-
@person.name = nil # will fail because of validations
|
27
|
-
@person.profile_attributes = { :nick => 'snusnu' }
|
28
|
-
@person.save.should be_false
|
29
|
-
|
30
|
-
Person.all.size.should == 0
|
31
|
-
Profile.all.size.should == 0
|
32
|
-
end
|
33
|
-
|
34
19
|
it "should return the attributes written to Person#profile_attributes from the Person#profile_attributes reader" do
|
35
20
|
@person.profile_attributes.should be_nil
|
36
21
|
@person.profile_attributes = { :nick => 'snusnu' }
|
@@ -61,6 +46,10 @@ describe "every accessible has(1) association with no reject_if proc", :shared =
|
|
61
46
|
Profile.all.size.should == 0
|
62
47
|
|
63
48
|
@person.profile_attributes = { :nick => 'snusnu' }
|
49
|
+
|
50
|
+
Person.all.size.should == 0
|
51
|
+
Profile.all.size.should == 0
|
52
|
+
|
64
53
|
@person.save.should be_true
|
65
54
|
|
66
55
|
Person.all.size.should == 1
|
@@ -68,6 +57,24 @@ describe "every accessible has(1) association with no reject_if proc", :shared =
|
|
68
57
|
Profile.first.nick.should == 'snusnu'
|
69
58
|
end
|
70
59
|
|
60
|
+
it "should perform atomic commits" do
|
61
|
+
|
62
|
+
# related resource is invalid
|
63
|
+
@person.profile_attributes = { :nick => nil } # will fail because of validations
|
64
|
+
@person.save.should be_false
|
65
|
+
|
66
|
+
Person.all.size.should == 0
|
67
|
+
Profile.all.size.should == 0
|
68
|
+
|
69
|
+
# self is invalid
|
70
|
+
@person.name = nil # will fail because of validations
|
71
|
+
@person.profile_attributes = { :nick => 'snusnu' }
|
72
|
+
@person.save.should be_false
|
73
|
+
|
74
|
+
Person.all.size.should == 0
|
75
|
+
Profile.all.size.should == 0
|
76
|
+
end
|
77
|
+
|
71
78
|
end
|
72
79
|
|
73
80
|
describe "every accessible has(1) association with :allow_destroy => false", :shared => true do
|
@@ -76,9 +83,17 @@ describe "every accessible has(1) association with :allow_destroy => false", :sh
|
|
76
83
|
@person.save
|
77
84
|
profile = Profile.create(:person_id => @person.id, :nick => 'snusnu')
|
78
85
|
@person.reload
|
79
|
-
|
86
|
+
|
87
|
+
Person.all.size.should == 1
|
88
|
+
Profile.all.size.should == 1
|
89
|
+
|
80
90
|
@person.profile_attributes = { :id => profile.id, :_delete => true }
|
91
|
+
|
92
|
+
Person.all.size.should == 1
|
93
|
+
Profile.all.size.should == 1
|
94
|
+
|
81
95
|
@person.save
|
96
|
+
|
82
97
|
Person.all.size.should == 1
|
83
98
|
Profile.all.size.should == 1
|
84
99
|
end
|
@@ -90,10 +105,16 @@ describe "every accessible has(1) association with :allow_destroy => true", :sha
|
|
90
105
|
it "should allow to delete an existing profile via Person#profile_attributes" do
|
91
106
|
@person.save
|
92
107
|
profile = Profile.create(:person_id => @person.id, :nick => 'snusnu')
|
93
|
-
|
94
108
|
@person.profile = profile
|
109
|
+
|
110
|
+
Person.all.size.should == 1
|
111
|
+
Profile.all.size.should == 1
|
112
|
+
|
95
113
|
@person.profile_attributes = { :id => profile.id, :_delete => true }
|
96
114
|
|
115
|
+
Person.all.size.should == 1
|
116
|
+
Profile.all.size.should == 1
|
117
|
+
|
97
118
|
@person.save
|
98
119
|
|
99
120
|
Person.all.size.should == 1
|
data/spec/shared/has_n_spec.rb
CHANGED
@@ -14,27 +14,6 @@ describe "every accessible has(n) association", :shared => true do
|
|
14
14
|
Task.first.name.should == 'write more specs'
|
15
15
|
end
|
16
16
|
|
17
|
-
it "should perform atomic commits" do
|
18
|
-
Project.all.size.should == 0
|
19
|
-
Task.all.size.should == 0
|
20
|
-
|
21
|
-
# self is invalid
|
22
|
-
@project.name = nil # will fail because of validations
|
23
|
-
@project.tasks_attributes = { 'new_1' => { :name => 'write specs' } }
|
24
|
-
@project.save
|
25
|
-
|
26
|
-
Project.all.size.should == 0
|
27
|
-
Task.all.size.should == 0
|
28
|
-
|
29
|
-
# related resource is invalid
|
30
|
-
@project.name = 'dm-accepts_nested_attributes'
|
31
|
-
@project.tasks_attributes = { 'new_1' => { :name => nil } } # will fail because of validations
|
32
|
-
@project.save
|
33
|
-
|
34
|
-
Project.all.size.should == 0
|
35
|
-
Task.all.size.should == 0
|
36
|
-
end
|
37
|
-
|
38
17
|
it "should return the attributes written to Project#task_attributes from the Project#task_attributes reader" do
|
39
18
|
@project.tasks_attributes.should be_nil
|
40
19
|
@project.tasks_attributes = { 'new_1' => { :name => 'write specs' } }
|
@@ -67,6 +46,10 @@ describe "every accessible has(n) association with no reject_if proc", :shared =
|
|
67
46
|
Task.all.size.should == 0
|
68
47
|
|
69
48
|
@project.tasks_attributes = { 'new_1' => { :name => 'write specs' } }
|
49
|
+
|
50
|
+
Project.all.size.should == 1
|
51
|
+
Task.all.size.should == 0
|
52
|
+
|
70
53
|
@project.save.should be_true
|
71
54
|
|
72
55
|
Project.all.size.should == 1
|
@@ -74,6 +57,27 @@ describe "every accessible has(n) association with no reject_if proc", :shared =
|
|
74
57
|
Task.first.name.should == 'write specs'
|
75
58
|
end
|
76
59
|
|
60
|
+
it "should perform atomic commits" do
|
61
|
+
Project.all.size.should == 0
|
62
|
+
Task.all.size.should == 0
|
63
|
+
|
64
|
+
# self is invalid
|
65
|
+
@project.name = nil # will fail because of validations
|
66
|
+
@project.tasks_attributes = { 'new_1' => { :name => 'write specs' } }
|
67
|
+
@project.save
|
68
|
+
|
69
|
+
Project.all.size.should == 0
|
70
|
+
Task.all.size.should == 0
|
71
|
+
|
72
|
+
# related resource is invalid
|
73
|
+
@project.name = 'dm-accepts_nested_attributes'
|
74
|
+
@project.tasks_attributes = { 'new_1' => { :name => nil } } # will fail because of validations
|
75
|
+
@project.save
|
76
|
+
|
77
|
+
Project.all.size.should == 0
|
78
|
+
Task.all.size.should == 0
|
79
|
+
end
|
80
|
+
|
77
81
|
end
|
78
82
|
|
79
83
|
describe "every accessible has(n) association with :allow_destroy => false", :shared => true do
|
@@ -99,14 +103,18 @@ describe "every accessible has(n) association with :allow_destroy => true", :sha
|
|
99
103
|
it "should allow to delete an existing task via Profile#tasks_attributes" do
|
100
104
|
@project.save
|
101
105
|
task = Task.create(:project => @project, :name => 'write specs')
|
106
|
+
@project.tasks.reload
|
102
107
|
|
103
108
|
Project.all.size.should == 1
|
104
109
|
Task.all.size.should == 1
|
105
|
-
|
106
|
-
@project.tasks << task
|
110
|
+
|
107
111
|
@project.tasks_attributes = { '1' => { :id => task.id, :_delete => true } }
|
112
|
+
|
113
|
+
Project.all.size.should == 1
|
114
|
+
Task.all.size.should == 1
|
115
|
+
|
108
116
|
@project.save
|
109
|
-
|
117
|
+
|
110
118
|
Project.all.size.should == 1
|
111
119
|
Task.all.size.should == 0
|
112
120
|
end
|
@@ -23,25 +23,6 @@ describe "every accessible has(n, :through) association", :shared => true do
|
|
23
23
|
Project.first.name.should == 'still dm-accepts_nested_attributes'
|
24
24
|
end
|
25
25
|
|
26
|
-
it "should perform atomic commits" do
|
27
|
-
|
28
|
-
@person.projects_attributes = { 'new_1' => { :name => nil } } # should fail because of validations
|
29
|
-
@person.save
|
30
|
-
|
31
|
-
Person.all.size.should == 0 # TODO think more if this should be '1'
|
32
|
-
ProjectMembership.all.size.should == 0
|
33
|
-
Project.all.size.should == 0
|
34
|
-
|
35
|
-
@person.name = nil # should fail because of validations
|
36
|
-
@person.projects_attributes = { 'new_1' => { :name => nil } }
|
37
|
-
@person.save
|
38
|
-
|
39
|
-
Person.all.size.should == 0
|
40
|
-
ProjectMembership.all.size.should == 0
|
41
|
-
Project.all.size.should == 0
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
26
|
it "should return the attributes written to Person#projects_attributes from the Person#projects_attributes reader" do
|
46
27
|
@person.projects_attributes.should be_nil
|
47
28
|
@person.projects_attributes = { 'new_1' => { :name => 'write specs' } }
|
@@ -85,6 +66,11 @@ describe "every accessible has(n, :through) association with no reject_if proc",
|
|
85
66
|
Project.all.size.should == 0
|
86
67
|
|
87
68
|
@person.projects_attributes = { 'new_1' => { :name => 'dm-accepts_nested_attributes' } }
|
69
|
+
|
70
|
+
Person.all.size.should == 1
|
71
|
+
ProjectMembership.all.size.should == 0
|
72
|
+
Project.all.size.should == 0
|
73
|
+
|
88
74
|
@person.save
|
89
75
|
|
90
76
|
Person.all.size.should == 1
|
@@ -94,6 +80,25 @@ describe "every accessible has(n, :through) association with no reject_if proc",
|
|
94
80
|
Project.first.name.should == 'dm-accepts_nested_attributes'
|
95
81
|
end
|
96
82
|
|
83
|
+
it "should perform atomic commits" do
|
84
|
+
|
85
|
+
@person.projects_attributes = { 'new_1' => { :name => nil } } # should fail because of validations
|
86
|
+
@person.save
|
87
|
+
|
88
|
+
Person.all.size.should == 0 # TODO think more if this should be '1'
|
89
|
+
ProjectMembership.all.size.should == 0
|
90
|
+
Project.all.size.should == 0
|
91
|
+
|
92
|
+
@person.name = nil # should fail because of validations
|
93
|
+
@person.projects_attributes = { 'new_1' => { :name => nil } }
|
94
|
+
@person.save
|
95
|
+
|
96
|
+
Person.all.size.should == 0
|
97
|
+
ProjectMembership.all.size.should == 0
|
98
|
+
Project.all.size.should == 0
|
99
|
+
|
100
|
+
end
|
101
|
+
|
97
102
|
end
|
98
103
|
|
99
104
|
describe "every accessible has(n, :through) association with :allow_destroy => false", :shared => true do
|
@@ -108,6 +113,11 @@ describe "every accessible has(n, :through) association with :allow_destroy => f
|
|
108
113
|
Project.all.size.should == 1
|
109
114
|
|
110
115
|
@person.projects_attributes = { '1' => { :id => project.id, :_delete => true } }
|
116
|
+
|
117
|
+
Person.all.size.should == 1
|
118
|
+
ProjectMembership.all.size.should == 1
|
119
|
+
Project.all.size.should == 1
|
120
|
+
|
111
121
|
@person.save
|
112
122
|
|
113
123
|
Person.all.size.should == 1
|
@@ -121,19 +131,26 @@ describe "every accessible has(n, :through) association with :allow_destroy => t
|
|
121
131
|
|
122
132
|
it "should allow to delete an existing project via Person#projects_attributes" do
|
123
133
|
@person.save
|
124
|
-
|
125
|
-
|
134
|
+
project_1 = Project.create(:name => 'dm-accepts_nested_attributes')
|
135
|
+
project_2 = Project.create(:name => 'dm-is-localizable')
|
136
|
+
project_membership_1 = ProjectMembership.create(:person => @person, :project => project_1)
|
137
|
+
project_membership_2 = ProjectMembership.create(:person => @person, :project => project_2)
|
126
138
|
|
127
139
|
Person.all.size.should == 1
|
128
|
-
ProjectMembership.all.size.should ==
|
129
|
-
Project.all.size.should ==
|
130
|
-
|
131
|
-
@person.projects_attributes = { '1' => { :id =>
|
140
|
+
ProjectMembership.all.size.should == 2
|
141
|
+
Project.all.size.should == 2
|
142
|
+
|
143
|
+
@person.projects_attributes = { '1' => { :id => project_1.id, :_delete => true } }
|
144
|
+
|
145
|
+
Person.all.size.should == 1
|
146
|
+
ProjectMembership.all.size.should == 2
|
147
|
+
Project.all.size.should == 2
|
148
|
+
|
132
149
|
@person.save
|
133
|
-
|
150
|
+
|
134
151
|
Person.all.size.should == 1
|
135
|
-
ProjectMembership.all.size.should ==
|
136
|
-
Project.all.size.should ==
|
152
|
+
ProjectMembership.all.size.should == 1
|
153
|
+
Project.all.size.should == 1
|
137
154
|
end
|
138
155
|
|
139
156
|
end
|