snusnu-dm-accepts_nested_attributes 0.10.0 → 0.11.0
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/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
|