dm-accepts_nested_attributes_for 1.2.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.
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