dm-accepts_nested_attributes_for 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/CHANGELOG +970 -0
  2. data/Gemfile +84 -0
  3. data/LICENSE +20 -0
  4. data/README.textile +94 -0
  5. data/Rakefile +25 -0
  6. data/TODO +6 -0
  7. data/VERSION +1 -0
  8. data/dm-accepts_nested_attributes.gemspec +114 -0
  9. data/lib/dm-accepts_nested_attributes.rb +13 -0
  10. data/lib/dm-accepts_nested_attributes/model.rb +144 -0
  11. data/lib/dm-accepts_nested_attributes/relationship.rb +82 -0
  12. data/lib/dm-accepts_nested_attributes/resource.rb +382 -0
  13. data/lib/dm-accepts_nested_attributes/version.rb +7 -0
  14. data/spec/accepts_nested_attributes_for_spec.rb +408 -0
  15. data/spec/assign_nested_attributes_for_spec.rb +101 -0
  16. data/spec/comb/1-1_disjoint_spec.rb +67 -0
  17. data/spec/comb/1-1_overlapping_spec.rb +66 -0
  18. data/spec/comb/1-1_subset_spec.rb +65 -0
  19. data/spec/comb/1-1_superset_spec.rb +67 -0
  20. data/spec/comb/1-m_disjoint_spec.rb +71 -0
  21. data/spec/comb/1-m_overlapping_spec.rb +70 -0
  22. data/spec/comb/1-m_subset_spec.rb +65 -0
  23. data/spec/comb/1-m_superset_spec.rb +71 -0
  24. data/spec/comb/m-1_disjoint_spec.rb +71 -0
  25. data/spec/comb/m-1_overlapping_spec.rb +70 -0
  26. data/spec/comb/m-1_subset_spec.rb +65 -0
  27. data/spec/comb/m-1_superset_spec.rb +71 -0
  28. data/spec/comb/n-m_composite_spec.rb +141 -0
  29. data/spec/comb/n-m_surrogate_spec.rb +154 -0
  30. data/spec/many_to_many_composite_spec.rb +120 -0
  31. data/spec/many_to_many_spec.rb +129 -0
  32. data/spec/many_to_one_composite_spec.rb +120 -0
  33. data/spec/many_to_one_spec.rb +101 -0
  34. data/spec/one_to_many_composite_spec.rb +120 -0
  35. data/spec/one_to_many_spec.rb +100 -0
  36. data/spec/one_to_one_composite_spec.rb +150 -0
  37. data/spec/one_to_one_spec.rb +115 -0
  38. data/spec/rcov.opts +6 -0
  39. data/spec/resource_spec.rb +65 -0
  40. data/spec/shared/many_to_many_composite_spec.rb +149 -0
  41. data/spec/shared/many_to_many_spec.rb +146 -0
  42. data/spec/shared/many_to_one_composite_spec.rb +160 -0
  43. data/spec/shared/many_to_one_spec.rb +130 -0
  44. data/spec/shared/one_to_many_composite_spec.rb +159 -0
  45. data/spec/shared/one_to_many_spec.rb +107 -0
  46. data/spec/shared/one_to_one_composite_spec.rb +114 -0
  47. data/spec/shared/one_to_one_spec.rb +111 -0
  48. data/spec/spec.opts +4 -0
  49. data/spec/spec_helper.rb +50 -0
  50. data/spec/update_dirty_spec.rb +113 -0
  51. data/spec/update_multiple_spec.rb +79 -0
  52. data/tasks/changelog.rake +20 -0
  53. data/tasks/ci.rake +1 -0
  54. data/tasks/local_gemfile.rake +18 -0
  55. data/tasks/spec.rake +22 -0
  56. data/tasks/yard.rake +9 -0
  57. data/tasks/yardstick.rake +19 -0
  58. metadata +216 -0
@@ -0,0 +1,107 @@
1
+ describe "every accessible one_to_many association", :shared => true do
2
+
3
+ it "should allow to update an existing task via Project#tasks_attributes" do
4
+ project = Project.create :name => 'trippings'
5
+ task = Task.create(:project => project, :name => 'write specs')
6
+
7
+ Project.all.size.should == 1
8
+ Task.all.size.should == 1
9
+
10
+ project.tasks_attributes = [{ :id => task.id, :name => 'write more specs' }]
11
+ project.save.should be_true
12
+
13
+ Project.all.size.should == 1
14
+ Task.all.size.should == 1
15
+ Task.first.name.should == 'write more specs'
16
+ end
17
+
18
+ it "should return the attributes written with Project#task_attributes= from the Project#task_attributes reader" do
19
+ project = Project.new :name => 'trippings'
20
+ project.tasks_attributes.should be_nil
21
+ project.tasks_attributes = [{ :name => 'write specs' }]
22
+ project.tasks_attributes.should == [{ :name => 'write specs' }]
23
+ end
24
+
25
+ end
26
+
27
+ describe "every accessible one_to_many association with a valid reject_if proc", :shared => true do
28
+
29
+ it "should not allow to create a new task via Project#tasks_attributes" do
30
+ project = Project.create :name => 'trippings'
31
+
32
+ Project.all.size.should == 1
33
+ Task.all.size.should == 0
34
+
35
+ project.tasks_attributes = [{ :name => 'write specs' }]
36
+ project.save.should be_true
37
+
38
+ Project.all.size.should == 1
39
+ Task.all.size.should == 0
40
+ end
41
+
42
+ end
43
+
44
+ describe "every accessible one_to_many association with no reject_if proc", :shared => true do
45
+
46
+ it "should allow to create a new task via Project#tasks_attributes" do
47
+ project = Project.create :name => 'trippings'
48
+
49
+ Project.all.size.should == 1
50
+ Task.all.size.should == 0
51
+
52
+ project.tasks_attributes = [{ :name => 'write specs' }]
53
+
54
+ Project.all.size.should == 1
55
+ Task.all.size.should == 0
56
+
57
+ project.save.should be_true
58
+
59
+ Project.all.size.should == 1
60
+ Task.all.size.should == 1
61
+ Task.first.name.should == 'write specs'
62
+ end
63
+
64
+ end
65
+
66
+ describe "every accessible one_to_many association with :allow_destroy => false", :shared => true do
67
+
68
+ it "should not allow to delete an existing task via Profile#tasks_attributes" do
69
+ project = Project.create :name => 'trippings'
70
+
71
+ task = Task.create(:project => project, :name => 'write specs')
72
+
73
+ Project.all.size.should == 1
74
+ Task.all.size.should == 1
75
+
76
+ project.tasks_attributes = [{ :id => task.id, :_delete => true }]
77
+ project.save
78
+
79
+ Project.all.size.should == 1
80
+ Task.all.size.should == 1
81
+ end
82
+
83
+ end
84
+
85
+ describe "every accessible one_to_many association with :allow_destroy => true", :shared => true do
86
+
87
+ it "should allow to delete an existing task via Profile#tasks_attributes" do
88
+ project = Project.create :name => 'trippings'
89
+
90
+ task = Task.create(:project => project, :name => 'write specs')
91
+ project.tasks.reload
92
+
93
+ Project.all.size.should == 1
94
+ Task.all.size.should == 1
95
+
96
+ project.tasks_attributes = [{ :id => task.id, :_delete => true }]
97
+
98
+ Project.all.size.should == 1
99
+ Task.all.size.should == 1
100
+
101
+ project.save
102
+
103
+ Project.all.size.should == 1
104
+ Task.all.size.should == 0
105
+ end
106
+
107
+ end
@@ -0,0 +1,114 @@
1
+ describe "every accessible one_to_one composite association", :shared => true do
2
+
3
+ it "should allow to update an existing profile via Person#profile_attributes" do
4
+ person = Person.create(:audit_id => 1, :name => 'Martin')
5
+ profile = Profile.create(:person => person, :nick => 'snusnu')
6
+ person.reload
7
+
8
+ Person.all.size.should == 1
9
+ Profile.all.size.should == 1
10
+
11
+ person.profile_attributes = { :nick => 'still snusnu somehow' }
12
+ person.save.should be_true
13
+
14
+ Person.all.size.should == 1
15
+ Profile.all.size.should == 1
16
+ Profile.first.nick.should == 'still snusnu somehow'
17
+ end
18
+
19
+ it "should return the attributes written to Person#profile_attributes from the Person#profile_attributes reader" do
20
+ person = Person.new :name => 'Martin'
21
+ person.profile_attributes.should be_nil
22
+ person.profile_attributes = { :nick => 'snusnu' }
23
+ person.profile_attributes.should == { :nick => 'snusnu' }
24
+ end
25
+
26
+ end
27
+
28
+ describe "every accessible one_to_one composite association with a valid reject_if proc", :shared => true do
29
+
30
+ it "should not allow to create a new profile via Person#profile_attributes" do
31
+ Person.all.size.should == 0
32
+ Profile.all.size.should == 0
33
+
34
+ person = Person.new(:audit_id => 1, :name => 'Martin')
35
+ person.profile_attributes = { :nick => 'snusnu' }
36
+ person.save
37
+
38
+ Person.all.size.should == 1
39
+ Profile.all.size.should == 0
40
+ end
41
+
42
+ end
43
+
44
+ describe "every accessible one_to_one composite association with no reject_if proc", :shared => true do
45
+
46
+ [[:new, 'a new'], [:create, 'an existing']].each do |ctor, ctor_desc|
47
+ it "should allow to create a new profile via Person#profile_attributes on #{ctor_desc} person" do
48
+ Person.all.size.should == 0
49
+ Profile.all.size.should == 0
50
+
51
+ person = Person.send(ctor, :audit_id => 1, :name => 'Martin')
52
+ person.profile_attributes = { :nick => 'snusnu' }
53
+
54
+ Person.all.size.should == (ctor == :new ? 0 : 1)
55
+ Profile.all.size.should == 0
56
+
57
+ person.save.should be_true
58
+
59
+ Person.all.size.should == 1
60
+ Profile.all.size.should == 1
61
+ Profile.first.nick.should == 'snusnu'
62
+ Profile.first.person_audit_id.should == person.audit_id
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ describe "every accessible one_to_one composite association with :allow_destroy => false", :shared => true do
69
+
70
+ it "should not allow to delete an existing profile via Person#profile_attributes" do
71
+ person = Person.create(:audit_id => 1, :name => 'Martin')
72
+
73
+ profile = Profile.create(:person => person, :nick => 'snusnu')
74
+ person.reload
75
+
76
+ Person.all.size.should == 1
77
+ Profile.all.size.should == 1
78
+
79
+ person.profile_attributes = { :_delete => true }
80
+
81
+ Person.all.size.should == 1
82
+ Profile.all.size.should == 1
83
+
84
+ person.save
85
+
86
+ Person.all.size.should == 1
87
+ Profile.all.size.should == 1
88
+ end
89
+
90
+ end
91
+
92
+ describe "every accessible one_to_one composite association with :allow_destroy => true", :shared => true do
93
+
94
+ it "should allow to delete an existing profile via Person#profile_attributes" do
95
+ person = Person.create(:audit_id => 1, :name => 'Martin')
96
+
97
+ profile = Profile.create(:person => person, :nick => 'snusnu')
98
+ person.profile = profile
99
+
100
+ Person.all.size.should == 1
101
+ Profile.all.size.should == 1
102
+
103
+ person.profile_attributes = { :_delete => true }
104
+
105
+ Person.all.size.should == 1
106
+ Profile.all.size.should == 1
107
+
108
+ person.save
109
+
110
+ Person.all.size.should == 1
111
+ Profile.all.size.should == 0
112
+ end
113
+
114
+ end
@@ -0,0 +1,111 @@
1
+ describe "every accessible one_to_one association", :shared => true do
2
+
3
+ it "should allow to update an existing profile via Person#profile_attributes" do
4
+ person = Person.create :name => 'Martin'
5
+ profile = Profile.create(:person => person, :nick => 'snusnu')
6
+ person.reload
7
+
8
+ Person.all.size.should == 1
9
+ Profile.all.size.should == 1
10
+
11
+ person.profile_attributes = { :id => profile.id, :nick => 'still snusnu somehow' }
12
+ person.save.should be_true
13
+
14
+ Person.all.size.should == 1
15
+ Profile.all.size.should == 1
16
+ Profile.first.nick.should == 'still snusnu somehow'
17
+ end
18
+
19
+ it "should return the attributes written to Person#profile_attributes from the Person#profile_attributes reader" do
20
+ person = Person.new :name => 'Martin'
21
+ person.profile_attributes.should be_nil
22
+ person.profile_attributes = { :nick => 'snusnu' }
23
+ person.profile_attributes.should == { :nick => 'snusnu' }
24
+ end
25
+
26
+ end
27
+
28
+ describe "every accessible one_to_one association with a valid reject_if proc", :shared => true do
29
+
30
+ it "should not allow to create a new profile via Person#profile_attributes" do
31
+ Person.all.size.should == 0
32
+ Profile.all.size.should == 0
33
+
34
+ person = Person.new :name => 'Martin'
35
+ person.profile_attributes = { :nick => 'snusnu' }
36
+ person.save
37
+
38
+ Person.all.size.should == 1
39
+ Profile.all.size.should == 0
40
+ end
41
+
42
+ end
43
+
44
+ describe "every accessible one_to_one association with no reject_if proc", :shared => true do
45
+
46
+ it "should allow to create a new profile via Person#profile_attributes" do
47
+ Person.all.size.should == 0
48
+ Profile.all.size.should == 0
49
+
50
+ person = Person.new :name => 'Martin'
51
+ person.profile_attributes = { :nick => 'snusnu' }
52
+
53
+ Person.all.size.should == 0
54
+ Profile.all.size.should == 0
55
+
56
+ person.save.should be_true
57
+
58
+ Person.all.size.should == 1
59
+ Profile.all.size.should == 1
60
+ Profile.first.nick.should == 'snusnu'
61
+ end
62
+
63
+ end
64
+
65
+ describe "every accessible one_to_one association with :allow_destroy => false", :shared => true do
66
+
67
+ it "should not allow to delete an existing profile via Person#profile_attributes" do
68
+ person = Person.create :name => 'Martin'
69
+
70
+ profile = Profile.create(:person => person, :nick => 'snusnu')
71
+ person.reload
72
+
73
+ Person.all.size.should == 1
74
+ Profile.all.size.should == 1
75
+
76
+ person.profile_attributes = { :id => profile.id, :_delete => true }
77
+
78
+ Person.all.size.should == 1
79
+ Profile.all.size.should == 1
80
+
81
+ person.save
82
+
83
+ Person.all.size.should == 1
84
+ Profile.all.size.should == 1
85
+ end
86
+
87
+ end
88
+
89
+ describe "every accessible one_to_one association with :allow_destroy => true", :shared => true do
90
+
91
+ it "should allow to delete an existing profile via Person#profile_attributes" do
92
+ person = Person.create :name => 'Martin'
93
+
94
+ profile = Profile.create(:person => person, :nick => 'snusnu')
95
+ person.profile = profile
96
+
97
+ Person.all.size.should == 1
98
+ Profile.all.size.should == 1
99
+
100
+ person.profile_attributes = { :id => profile.id, :_delete => true }
101
+
102
+ Person.all.size.should == 1
103
+ Profile.all.size.should == 1
104
+
105
+ person.save
106
+
107
+ Person.all.size.should == 1
108
+ Profile.all.size.should == 0
109
+ end
110
+
111
+ end
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --loadby random
3
+ --format profile
4
+ --backtrace
@@ -0,0 +1,50 @@
1
+ require 'dm-core/spec/setup'
2
+ require 'dm-core/spec/lib/pending_helpers'
3
+ require 'dm-core/spec/lib/spec_helper'
4
+
5
+ require 'dm-accepts_nested_attributes'
6
+
7
+ require 'shared/many_to_many_spec'
8
+ require 'shared/many_to_one_spec'
9
+ require 'shared/one_to_many_spec'
10
+ require 'shared/one_to_one_spec'
11
+ require 'shared/many_to_many_composite_spec'
12
+ require 'shared/many_to_one_composite_spec'
13
+ require 'shared/one_to_many_composite_spec'
14
+ require 'shared/one_to_one_composite_spec'
15
+
16
+ DataMapper::Spec.setup
17
+
18
+ HAS_M2M_SUPPORT = !%w[in_memory yaml].include?(DataMapper::Spec.adapter_name)
19
+
20
+ module ConstraintSupport
21
+
22
+ def constraint(type)
23
+ if DataMapper.const_defined?('Constraints')
24
+ { :constraint => type }
25
+ else
26
+ {}
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ Spec::Runner.configure do |config|
33
+
34
+ config.include(DataMapper::Spec::PendingHelpers)
35
+
36
+ config.after(:all) do
37
+ if DataMapper.respond_to?(:auto_migrate_down!, true)
38
+ DataMapper.send(:auto_migrate_down!, DataMapper::Spec.adapter.name)
39
+ end
40
+
41
+ DataMapper::Spec.cleanup_models
42
+ end
43
+
44
+ config.after(:suite) do
45
+ if DataMapper.respond_to?(:auto_migrate_down!, true)
46
+ DataMapper.send(:auto_migrate_down!, DataMapper::Spec.adapter.name)
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,113 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Person.has(n, :memberships)" do
4
+
5
+ before(:all) do
6
+ class ::Project
7
+ include DataMapper::Resource
8
+ extend ConstraintSupport
9
+
10
+ property :id, Integer, :key => true, :min => 0
11
+ property :name, String, :required => true
12
+
13
+ has n, :memberships, constraint(:destroy)
14
+ has n, :people, :through => :memberships
15
+ end
16
+
17
+ class ::Person
18
+ include DataMapper::Resource
19
+ extend ConstraintSupport
20
+
21
+ property :id, Integer, :key => true, :min => 0
22
+ property :name, String, :required => true
23
+
24
+ has n, :memberships, constraint(:destroy)
25
+ has n, :projects, :through => :memberships
26
+ accepts_nested_attributes_for :memberships, :allow_destroy => true
27
+ end
28
+
29
+ class ::Membership
30
+ include DataMapper::Resource
31
+
32
+ property :person_id, Integer, :key => true, :min => 0
33
+ property :project_id, Integer, :key => true, :min => 0
34
+ property :role, String, :required => true
35
+
36
+ belongs_to :person
37
+ belongs_to :project
38
+
39
+ accepts_nested_attributes_for :person
40
+ accepts_nested_attributes_for :project
41
+ end
42
+
43
+ DataMapper.auto_migrate!
44
+ end
45
+
46
+ before(:each) do
47
+ Membership.all.destroy!
48
+ Person.all.destroy!
49
+ Project.all.destroy!
50
+ end
51
+
52
+ it "should allow to update an existing person, membership and project at the same time" do
53
+ person = Person.create(:id => 1, :name => 'snusnu')
54
+ project = Project.create(:id => 10, :name => 'dm-accepts_nested_attributes')
55
+ membership = Membership.create(:person => person, :project => project, :role => 'maintainer')
56
+
57
+ params = {
58
+ :name => 'Martin',
59
+ :memberships_attributes => [{
60
+ :project_id => project.id,
61
+ :role => 'still maintainer',
62
+ :project_attributes => { :name => 'tripping' }
63
+ }]
64
+ }
65
+
66
+ # Quick ordered hash hack: make sure the name attribute is updated first,
67
+ # making the person dirty.
68
+ class << params
69
+ def each
70
+ [:name, :memberships_attributes].each { |key| yield key, self[key] }
71
+ end
72
+ end
73
+
74
+ person.update params
75
+
76
+ Person.all.size.should == 1
77
+ Project.all.size.should == 1
78
+ Membership.all.size.should == 1
79
+
80
+ Person.first.attributes.should == { :id => 1, :name => 'Martin' }
81
+ Project.first.attributes.should == { :id => 10, :name => 'tripping' }
82
+ Membership.first.attributes.should == { :person_id => 1, :project_id => 10, :role => 'still maintainer' }
83
+ end
84
+
85
+ it "should not allow to update a dirty parent resource with nested attributes" do
86
+ person = Person.create(:id => 1, :name => 'snusnu')
87
+ project = Project.create(:id => 10, :name => 'dm-accepts_nested_attributes')
88
+ membership = Membership.create(:person => person, :project => project, :role => 'maintainer')
89
+
90
+ params = {
91
+ :name => 'Martin',
92
+ :memberships_attributes => [{
93
+ :project_id => project.id,
94
+ :role => 'still maintainer',
95
+ :person_attributes => { :name => 'Martin Gamsjaeger' }
96
+ }]
97
+ }
98
+ class <<params
99
+ def each
100
+ [:name, :memberships_attributes].each { |key| yield key, self[key] }
101
+ end
102
+ end
103
+ lambda { person.update(params) }.should raise_error(DataMapper::UpdateConflictError)
104
+
105
+ Person.all.size.should == 1
106
+ Project.all.size.should == 1
107
+ Membership.all.size.should == 1
108
+
109
+ Person.first.attributes.should == { :id => 1, :name => 'snusnu' }
110
+ Project.first.attributes.should == { :id => 10, :name => 'dm-accepts_nested_attributes' }
111
+ Membership.first.attributes.should == { :person_id => 1, :project_id => 10, :role => 'maintainer' }
112
+ end
113
+ end