dm-core 0.9.2

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 (101) hide show
  1. data/CHANGELOG +144 -0
  2. data/FAQ +74 -0
  3. data/MIT-LICENSE +22 -0
  4. data/QUICKLINKS +12 -0
  5. data/README +143 -0
  6. data/lib/dm-core.rb +213 -0
  7. data/lib/dm-core/adapters.rb +4 -0
  8. data/lib/dm-core/adapters/abstract_adapter.rb +202 -0
  9. data/lib/dm-core/adapters/data_objects_adapter.rb +701 -0
  10. data/lib/dm-core/adapters/mysql_adapter.rb +132 -0
  11. data/lib/dm-core/adapters/postgres_adapter.rb +179 -0
  12. data/lib/dm-core/adapters/sqlite3_adapter.rb +105 -0
  13. data/lib/dm-core/associations.rb +172 -0
  14. data/lib/dm-core/associations/many_to_many.rb +138 -0
  15. data/lib/dm-core/associations/many_to_one.rb +101 -0
  16. data/lib/dm-core/associations/one_to_many.rb +275 -0
  17. data/lib/dm-core/associations/one_to_one.rb +61 -0
  18. data/lib/dm-core/associations/relationship.rb +116 -0
  19. data/lib/dm-core/associations/relationship_chain.rb +74 -0
  20. data/lib/dm-core/auto_migrations.rb +64 -0
  21. data/lib/dm-core/collection.rb +604 -0
  22. data/lib/dm-core/hook.rb +11 -0
  23. data/lib/dm-core/identity_map.rb +45 -0
  24. data/lib/dm-core/is.rb +16 -0
  25. data/lib/dm-core/logger.rb +233 -0
  26. data/lib/dm-core/migrations/destructive_migrations.rb +17 -0
  27. data/lib/dm-core/migrator.rb +29 -0
  28. data/lib/dm-core/model.rb +399 -0
  29. data/lib/dm-core/naming_conventions.rb +52 -0
  30. data/lib/dm-core/property.rb +611 -0
  31. data/lib/dm-core/property_set.rb +158 -0
  32. data/lib/dm-core/query.rb +590 -0
  33. data/lib/dm-core/repository.rb +159 -0
  34. data/lib/dm-core/resource.rb +618 -0
  35. data/lib/dm-core/scope.rb +35 -0
  36. data/lib/dm-core/support.rb +7 -0
  37. data/lib/dm-core/support/array.rb +13 -0
  38. data/lib/dm-core/support/assertions.rb +8 -0
  39. data/lib/dm-core/support/errors.rb +23 -0
  40. data/lib/dm-core/support/kernel.rb +7 -0
  41. data/lib/dm-core/support/symbol.rb +41 -0
  42. data/lib/dm-core/transaction.rb +267 -0
  43. data/lib/dm-core/type.rb +160 -0
  44. data/lib/dm-core/type_map.rb +80 -0
  45. data/lib/dm-core/types.rb +19 -0
  46. data/lib/dm-core/types/boolean.rb +7 -0
  47. data/lib/dm-core/types/discriminator.rb +32 -0
  48. data/lib/dm-core/types/object.rb +20 -0
  49. data/lib/dm-core/types/paranoid_boolean.rb +23 -0
  50. data/lib/dm-core/types/paranoid_datetime.rb +22 -0
  51. data/lib/dm-core/types/serial.rb +9 -0
  52. data/lib/dm-core/types/text.rb +10 -0
  53. data/spec/integration/association_spec.rb +1215 -0
  54. data/spec/integration/association_through_spec.rb +150 -0
  55. data/spec/integration/associations/many_to_many_spec.rb +171 -0
  56. data/spec/integration/associations/many_to_one_spec.rb +123 -0
  57. data/spec/integration/associations/one_to_many_spec.rb +66 -0
  58. data/spec/integration/auto_migrations_spec.rb +398 -0
  59. data/spec/integration/collection_spec.rb +1015 -0
  60. data/spec/integration/data_objects_adapter_spec.rb +32 -0
  61. data/spec/integration/model_spec.rb +68 -0
  62. data/spec/integration/mysql_adapter_spec.rb +85 -0
  63. data/spec/integration/postgres_adapter_spec.rb +732 -0
  64. data/spec/integration/property_spec.rb +224 -0
  65. data/spec/integration/query_spec.rb +376 -0
  66. data/spec/integration/repository_spec.rb +57 -0
  67. data/spec/integration/resource_spec.rb +324 -0
  68. data/spec/integration/sqlite3_adapter_spec.rb +352 -0
  69. data/spec/integration/sti_spec.rb +185 -0
  70. data/spec/integration/transaction_spec.rb +75 -0
  71. data/spec/integration/type_spec.rb +149 -0
  72. data/spec/lib/mock_adapter.rb +27 -0
  73. data/spec/spec_helper.rb +112 -0
  74. data/spec/unit/adapters/abstract_adapter_spec.rb +133 -0
  75. data/spec/unit/adapters/adapter_shared_spec.rb +15 -0
  76. data/spec/unit/adapters/data_objects_adapter_spec.rb +627 -0
  77. data/spec/unit/adapters/postgres_adapter_spec.rb +125 -0
  78. data/spec/unit/associations/many_to_many_spec.rb +14 -0
  79. data/spec/unit/associations/many_to_one_spec.rb +138 -0
  80. data/spec/unit/associations/one_to_many_spec.rb +385 -0
  81. data/spec/unit/associations/one_to_one_spec.rb +7 -0
  82. data/spec/unit/associations/relationship_spec.rb +67 -0
  83. data/spec/unit/associations_spec.rb +205 -0
  84. data/spec/unit/auto_migrations_spec.rb +110 -0
  85. data/spec/unit/collection_spec.rb +174 -0
  86. data/spec/unit/data_mapper_spec.rb +21 -0
  87. data/spec/unit/identity_map_spec.rb +126 -0
  88. data/spec/unit/is_spec.rb +80 -0
  89. data/spec/unit/migrator_spec.rb +33 -0
  90. data/spec/unit/model_spec.rb +339 -0
  91. data/spec/unit/naming_conventions_spec.rb +28 -0
  92. data/spec/unit/property_set_spec.rb +96 -0
  93. data/spec/unit/property_spec.rb +447 -0
  94. data/spec/unit/query_spec.rb +485 -0
  95. data/spec/unit/repository_spec.rb +93 -0
  96. data/spec/unit/resource_spec.rb +557 -0
  97. data/spec/unit/scope_spec.rb +131 -0
  98. data/spec/unit/transaction_spec.rb +493 -0
  99. data/spec/unit/type_map_spec.rb +114 -0
  100. data/spec/unit/type_spec.rb +119 -0
  101. metadata +187 -0
@@ -0,0 +1,125 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', "..", 'spec_helper'))
2
+
3
+ if HAS_POSTGRES
4
+ describe DataMapper::Adapters::PostgresAdapter do
5
+ before :all do
6
+ @adapter = repository(:postgres).adapter
7
+ end
8
+
9
+ describe '#upgrade_model_storage' do
10
+ before do
11
+ @repository = mock('repository', :kind_of? => true, :name => :postgres)
12
+ @model = mock('model', :kind_of? => true, :storage_name => 'models')
13
+ @property = mock('property', :kind_of? => true, :model => @model, :serial? => true, :field => 'property')
14
+
15
+ @model.should_receive(:properties).with(:postgres).any_number_of_times.and_return([@property])
16
+
17
+ @command = mock('command')
18
+ @connection = mock('connection', :create_command => @command, :close => true)
19
+ @result = mock('result', :to_i => 0)
20
+
21
+ DataObjects::Connection.stub!(:new).and_return(@connection)
22
+
23
+ @adapter.stub!(:execute).and_return(@result)
24
+ @adapter.stub!(:storage_exists?).and_return(true)
25
+ @adapter.stub!(:query).and_return([ 0 ])
26
+
27
+ @original_method = @adapter.class.superclass.instance_method(:upgrade_model_storage)
28
+ @adapter.class.superclass.send(:define_method, :upgrade_model_storage) {}
29
+ end
30
+
31
+ after do
32
+ method = @original_method
33
+ @adapter.class.superclass.send(:define_method, :upgrade_model_storage) do |*args|
34
+ method.bind(self).call(*args)
35
+ end
36
+ end
37
+
38
+ it 'should check to make sure the sequences exist' do
39
+ statement = %q[SELECT COUNT(*) FROM "pg_class" WHERE "relkind" = 'S' AND "relname" = ?]
40
+ @adapter.should_receive(:query).with(statement, 'models_property_seq').and_return([ 0 ])
41
+ @adapter.upgrade_model_storage(@repository, @model)
42
+ end
43
+
44
+ it 'should add sequences' do
45
+ statement = %q[CREATE SEQUENCE "models_property_seq"]
46
+ @adapter.should_receive(:execute).with(statement)
47
+ @adapter.upgrade_model_storage(@repository, @model)
48
+ end
49
+
50
+ it 'should execute the superclass upgrade_model_storage' do
51
+ rv = mock('inside super')
52
+ @adapter.class.superclass.send(:define_method, :upgrade_model_storage) { rv }
53
+ @adapter.upgrade_model_storage(@repository, @model).should == rv
54
+ end
55
+ end
56
+
57
+ describe '#create_model_storage' do
58
+ before do
59
+ @repository = mock('repository', :kind_of? => true, :name => :postgres)
60
+ @model = mock('model', :kind_of? => true, :storage_name => 'models')
61
+ @property = mock('property', :kind_of? => true, :model => @model, :serial? => true, :field => 'property')
62
+
63
+ @model.should_receive(:properties).with(:postgres).any_number_of_times.and_return([@property])
64
+
65
+ @adapter.stub!(:execute).and_return(@result)
66
+ @adapter.stub!(:storage_exists?).and_return(true)
67
+ @adapter.stub!(:query).and_return([ 0 ])
68
+
69
+ @original_method = @adapter.class.superclass.instance_method(:create_table_statement)
70
+ @adapter.class.superclass.send(:define_method, :create_table_statement) {}
71
+ end
72
+
73
+ after do
74
+ method = @original_method
75
+ @adapter.class.superclass.send(:define_method, :create_table_statement) do |*args|
76
+ method.bind(self).call(*args)
77
+ end
78
+ end
79
+
80
+ it 'should check to make sure the sequences exist' do
81
+ statement = %q[SELECT COUNT(*) FROM "pg_class" WHERE "relkind" = 'S' AND "relname" = ?]
82
+ @adapter.should_receive(:query).with(statement, 'models_property_seq').and_return([ 0 ])
83
+ @adapter.create_model_storage(@repository, @model)
84
+ end
85
+
86
+ it 'should add sequences' do
87
+ statement = %q[CREATE SEQUENCE "models_property_seq"]
88
+ @adapter.should_receive(:execute).with(statement)
89
+ @adapter.create_model_storage(@repository, @model)
90
+ end
91
+
92
+ it 'should execute the superclass upgrade_model_storage' do
93
+ rv = mock('inside super')
94
+ @adapter.class.superclass.send(:define_method, :create_table_statement) { rv }
95
+ @adapter.create_table_statement(@repository, @model).should == rv
96
+ end
97
+ end
98
+
99
+ describe '#destroy_model_storage' do
100
+ before do
101
+ @repository = mock('repository', :kind_of? => true, :name => :postgres)
102
+ @model = mock('model', :kind_of? => true, :storage_name => 'models')
103
+ @property = mock('property', :kind_of? => true, :model => @model, :serial? => true, :field => 'property')
104
+
105
+ @model.should_receive(:properties).with(:postgres).any_number_of_times.and_return([@property])
106
+
107
+ @original_method = @adapter.class.superclass.instance_method(:destroy_model_storage)
108
+ @adapter.class.superclass.send(:define_method, :destroy_model_storage) {}
109
+ end
110
+
111
+ after do
112
+ method = @original_method
113
+ @adapter.class.superclass.send(:define_method, :destroy_model_storage) do |*args|
114
+ method.bind(self).call(*args)
115
+ end
116
+ end
117
+
118
+ it 'should execute the superclass destroy_model_storage' do
119
+ rv = mock('inside super')
120
+ @adapter.class.superclass.send(:define_method, :destroy_model_storage) { rv }
121
+ @adapter.destroy_model_storage(@repository, @model).should == rv
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,14 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ describe DataMapper::Associations::ManyToMany do
4
+ it 'should allow a declaration' do
5
+ lambda do
6
+ class Supplier
7
+ has n, :manufacturers, :through => Resource
8
+ end
9
+ end.should_not raise_error
10
+ end
11
+ end
12
+
13
+ describe DataMapper::Associations::ManyToMany::Proxy do
14
+ end
@@ -0,0 +1,138 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ describe DataMapper::Associations::ManyToOne do
4
+ it 'should allow a declaration' do
5
+ lambda do
6
+ class Vehicle
7
+ belongs_to :manufacturer
8
+ end
9
+ end.should_not raise_error
10
+ end
11
+ end
12
+
13
+ describe DataMapper::Associations::ManyToOne::Proxy do
14
+ before do
15
+ @child = mock('child', :kind_of? => true)
16
+ @parent = mock('parent')
17
+ @relationship = mock('relationship', :kind_of? => true, :repository_name => :default, :get_parent => @parent, :attach_parent => nil)
18
+ @association = DataMapper::Associations::ManyToOne::Proxy.new(@relationship, @child)
19
+
20
+ @association.replace(@parent)
21
+ end
22
+
23
+ it 'should provide #replace' do
24
+ @association.should respond_to(:replace)
25
+ end
26
+
27
+ describe '#replace' do
28
+ before do
29
+ @other = mock('other parent')
30
+ end
31
+
32
+ before do
33
+ @relationship.should_receive(:attach_parent).with(@child, @other)
34
+ end
35
+
36
+ it 'should remove the resource from the collection' do
37
+ @association.should == @parent
38
+ @association.replace(@other)
39
+ @association.should == @other
40
+ end
41
+
42
+ it 'should not automatically save that the resource was removed from the association' do
43
+ @other.should_not_receive(:save)
44
+ @association.replace(@other)
45
+ end
46
+
47
+ it 'should return the association' do
48
+ @association.replace(@other).object_id.should == @association.object_id
49
+ end
50
+ end
51
+
52
+ it 'should provide #save' do
53
+ @association.should respond_to(:replace)
54
+ end
55
+
56
+ describe '#save' do
57
+ describe 'when the parent is nil' do
58
+ before do
59
+ @parent.should_receive(:nil?).with(no_args).and_return(true)
60
+ end
61
+
62
+ it 'should not save the parent' do
63
+ @association.save
64
+ end
65
+
66
+ it 'should return false' do
67
+ @association.save.should == false
68
+ end
69
+ end
70
+
71
+ describe 'when the parent is not a new record' do
72
+ before do
73
+ @parent.should_receive(:new_record?).with(no_args).and_return(false)
74
+ end
75
+
76
+ it 'should not save the parent' do
77
+ @parent.should_not_receive(:save)
78
+ @association.save
79
+ end
80
+
81
+ it 'should return true' do
82
+ @association.save.should == true
83
+ end
84
+ end
85
+
86
+ describe 'when the parent is a new record' do
87
+ before do
88
+ @parent.should_receive(:new_record?).with(no_args).and_return(true)
89
+ end
90
+
91
+ it 'should save the parent' do
92
+ @parent.should_receive(:save).with(no_args)
93
+ @association.save
94
+ end
95
+
96
+ it 'should return the result of the save' do
97
+ save_results = mock('save results')
98
+ @parent.should_receive(:save).with(no_args).and_return(save_results)
99
+ @association.save.object_id.should == save_results.object_id
100
+ end
101
+ end
102
+ end
103
+
104
+ it 'should provide #reload' do
105
+ @association.should respond_to(:reload)
106
+ end
107
+
108
+ describe '#reload' do
109
+ before(:each) do
110
+ @mock_parent = mock('#reload test parent')
111
+ @association.replace(@mock_parent)
112
+ end
113
+
114
+ it 'should set the @parent ivar to nil' do
115
+ @association.__send__(:parent).should == @mock_parent # Sanity check.
116
+
117
+ # We can't test the value of the instance variable since
118
+ # #instance_variable_get will be run on the @parent (thanks to
119
+ # Proxy#method_missing). Instead, test that Relationship#get_parent is
120
+ # run -- if @parent wasn't set to nil, this expectation should fail.
121
+ @relationship.should_receive(:get_parent).once.and_return(@mock_parent)
122
+ @association.reload
123
+
124
+ # Trigger #get_parent on the relationship.
125
+ @association.__send__(:parent)
126
+ end
127
+
128
+ it 'should not change the foreign key in the child' do
129
+ @relationship.should_not_receive(:attach_parent)
130
+ @association.reload
131
+ end
132
+
133
+ it 'should return self' do
134
+ @association.reload.should be_kind_of(DataMapper::Associations::ManyToOne::Proxy)
135
+ @association.reload.object_id.should == @association.object_id
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,385 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ describe DataMapper::Associations::OneToMany do
4
+ before do
5
+ @class = Class.new do
6
+ def self.name
7
+ 'User'
8
+ end
9
+
10
+ include DataMapper::Resource
11
+
12
+ property :user_id, Integer, :key => true
13
+ end
14
+ end
15
+
16
+ it 'should provide #has' do
17
+ @class.should respond_to(:has)
18
+ end
19
+
20
+ describe '#has' do
21
+ it 'should return a Relationship' do
22
+ @class.has(@class.n, :orders).should be_kind_of(DataMapper::Associations::Relationship)
23
+ end
24
+
25
+ describe 'relationship' do
26
+ before do
27
+ @relationship = mock('relationship')
28
+ DataMapper::Associations::Relationship.stub!(:new).and_return(@relationship)
29
+ end
30
+
31
+ it 'should receive the name' do
32
+ DataMapper::Associations::Relationship.should_receive(:new) do |name,_,_,_,_|
33
+ name.should == :user
34
+ end
35
+ @class.has(@class.n, :orders)
36
+ end
37
+
38
+ it 'should receive the repository name' do
39
+ DataMapper::Associations::Relationship.should_receive(:new) do |_,repository_name,_,_,_|
40
+ repository_name.should == :mock
41
+ end
42
+ repository(:mock) do
43
+ @class.has(@class.n, :orders)
44
+ end
45
+ end
46
+
47
+ it 'should receive the child model name when passed in as class_name' do
48
+ DataMapper::Associations::Relationship.should_receive(:new) do |_,_,child_model_name,_,_|
49
+ child_model_name.should == 'Company::Order'
50
+ end
51
+ @class.has(@class.n, :orders, :class_name => 'Company::Order')
52
+ end
53
+
54
+ it 'should receive the child model name when class_name not passed in' do
55
+ DataMapper::Associations::Relationship.should_receive(:new) do |_,_,child_model_name,_,_|
56
+ child_model_name.should == 'Order'
57
+ end
58
+ @class.has(@class.n, :orders)
59
+ end
60
+
61
+ it 'should receive the parent model name' do
62
+ DataMapper::Associations::Relationship.should_receive(:new) do |_,_,_,parent_model_name,_|
63
+ parent_model_name.should == 'User'
64
+ end
65
+ @class.has(@class.n, :orders)
66
+ end
67
+
68
+ it 'should receive the parent model name' do
69
+ options = { :min => 0, :max => 100 }
70
+ DataMapper::Associations::Relationship.should_receive(:new) do |_,_,_,parent_model_name,_|
71
+ options.object_id.should == options.object_id
72
+ end
73
+ @class.has(@class.n, :orders, options)
74
+ end
75
+ end
76
+
77
+ it 'should add an accessor for the proxy' do
78
+ @class.new.should_not respond_to(:orders)
79
+ @class.has(@class.n, :orders)
80
+ @class.new.should respond_to(:orders)
81
+ end
82
+
83
+ describe 'proxy accessor' do
84
+ before :all do
85
+ class User
86
+ include DataMapper::Resource
87
+ end
88
+
89
+ class Order
90
+ include DataMapper::Resource
91
+ end
92
+ end
93
+
94
+ it 'should return a OneToMany::Proxy' do
95
+ @class.has(@class.n, :orders)
96
+ @class.new.orders.should be_kind_of(DataMapper::Associations::OneToMany::Proxy)
97
+ end
98
+ end
99
+ end
100
+
101
+ it 'should work with classes inside modules'
102
+ end
103
+
104
+ describe DataMapper::Associations::OneToMany::Proxy do
105
+ before do
106
+ @parent = mock('parent', :new_record? => true, :kind_of? => true)
107
+ @resource = mock('resource', :null_object => true)
108
+ @collection = []
109
+ @repository = mock('repository', :save => nil, :kind_of? => true)
110
+ @relationship = mock('relationship', :get_children => @collection, :repository_name => :mock, :query => {}, :kind_of? => true)
111
+ @association = DataMapper::Associations::OneToMany::Proxy.new(@relationship, @parent)
112
+ end
113
+
114
+ describe 'a method that relates the resource', :shared => true do
115
+ it 'should add the resource to the collection' do
116
+ @association.should_not include(@resource)
117
+ do_add.should == return_value
118
+ @association.should include(@resource)
119
+ end
120
+
121
+ it 'should not automatically save that the resource was added to the association' do
122
+ @relationship.should_not_receive(:attach_parent)
123
+ do_add.should == return_value
124
+ end
125
+
126
+ it 'should persist the addition after saving the association' do
127
+ do_add.should == return_value
128
+ @relationship.should_receive(:attach_parent).with(@resource, @parent)
129
+ @association.save
130
+ end
131
+ end
132
+
133
+ describe 'a method that orphans the resource', :shared => true do
134
+ before do
135
+ @association << @resource
136
+ end
137
+
138
+ it 'should remove the resource from the collection' do
139
+ @association.should include(@resource)
140
+ do_remove.should == return_value
141
+ @association.should_not include(@resource)
142
+ end
143
+
144
+ it 'should not automatically save that the resource was removed from the association' do
145
+ @relationship.should_not_receive(:attach_parent)
146
+ do_remove.should == return_value
147
+ end
148
+
149
+ it 'should persist the removal after saving the association' do
150
+ do_remove.should == return_value
151
+ @relationship.should_receive(:attach_parent).with(@resource, nil)
152
+ @association.save
153
+ end
154
+ end
155
+
156
+ it 'should provide #<<' do
157
+ @association.should respond_to(:<<)
158
+ end
159
+
160
+ describe '#<<' do
161
+ def do_add
162
+ @association << @resource
163
+ end
164
+
165
+ def return_value
166
+ @association
167
+ end
168
+
169
+ it_should_behave_like 'a method that relates the resource'
170
+ end
171
+
172
+ it 'should provide #push' do
173
+ @association.should respond_to(:push)
174
+ end
175
+
176
+ describe '#push' do
177
+ def do_add
178
+ @association.push(@resource)
179
+ end
180
+
181
+ def return_value
182
+ @association
183
+ end
184
+
185
+ it_should_behave_like 'a method that relates the resource'
186
+ end
187
+
188
+ it 'should provide #unshift' do
189
+ @association.should respond_to(:unshift)
190
+ end
191
+
192
+ describe '#unshift' do
193
+ def do_add
194
+ @association.unshift(@resource)
195
+ end
196
+
197
+ def return_value
198
+ @association
199
+ end
200
+
201
+ it_should_behave_like 'a method that relates the resource'
202
+ end
203
+
204
+ it 'should provide #replace' do
205
+ @association.should respond_to(:replace)
206
+ end
207
+
208
+ describe '#replace' do
209
+ before do
210
+ @children = [
211
+ mock('child 1', :save => true),
212
+ mock('child 2', :save => true),
213
+ ]
214
+ @collection << @resource
215
+ @collection.stub!(:loaded?).and_return(true)
216
+ @relationship.stub!(:attach_parent)
217
+ end
218
+
219
+ def do_replace
220
+ @association.replace(@children)
221
+ end
222
+
223
+ def return_value
224
+ @association
225
+ end
226
+
227
+ it 'should remove the resource from the collection' do
228
+ @association.should include(@resource)
229
+ do_replace.should == return_value
230
+ @association.should_not include(@resource)
231
+ end
232
+
233
+ it 'should not automatically save that the resource was removed from the association' do
234
+ @relationship.should_not_receive(:attach_parent)
235
+ do_replace.should == return_value
236
+ end
237
+
238
+ it 'should persist the removal after saving the association' do
239
+ do_replace.should == return_value
240
+ @relationship.should_receive(:attach_parent).with(@resource, nil)
241
+ @association.save
242
+ end
243
+
244
+ it 'should not automatically save that the children were added to the association' do
245
+ @relationship.should_not_receive(:attach_parent)
246
+ do_replace.should == return_value
247
+ end
248
+
249
+ it 'should persist the addition after saving the association' do
250
+ do_replace.should == return_value
251
+ @relationship.should_receive(:attach_parent).with(@children[0], @parent)
252
+ @relationship.should_receive(:attach_parent).with(@children[1], @parent)
253
+ @association.save
254
+ end
255
+ end
256
+
257
+ it 'should provide #pop' do
258
+ @association.should respond_to(:pop)
259
+ end
260
+
261
+ describe '#pop' do
262
+ def do_remove
263
+ @association.pop
264
+ end
265
+
266
+ def return_value
267
+ @resource
268
+ end
269
+
270
+ it_should_behave_like 'a method that orphans the resource'
271
+ end
272
+
273
+ it 'should provide #shift' do
274
+ @association.should respond_to(:shift)
275
+ end
276
+
277
+ describe '#shift' do
278
+ def do_remove
279
+ @association.shift
280
+ end
281
+
282
+ def return_value
283
+ @resource
284
+ end
285
+
286
+ it_should_behave_like 'a method that orphans the resource'
287
+ end
288
+
289
+ it 'should provide #delete' do
290
+ @association.should respond_to(:delete)
291
+ end
292
+
293
+ describe '#delete' do
294
+ def do_remove
295
+ @association.delete(@resource)
296
+ end
297
+
298
+ def return_value
299
+ @resource
300
+ end
301
+
302
+ it_should_behave_like 'a method that orphans the resource'
303
+ end
304
+
305
+ it 'should provide #delete_at' do
306
+ @association.should respond_to(:delete_at)
307
+ end
308
+
309
+ describe '#delete_at' do
310
+ def do_remove
311
+ @association.delete_at(0)
312
+ end
313
+
314
+ def return_value
315
+ @resource
316
+ end
317
+
318
+ it_should_behave_like 'a method that orphans the resource'
319
+ end
320
+
321
+ it 'should provide #clear' do
322
+ @association.should respond_to(:clear)
323
+ end
324
+
325
+ describe '#clear' do
326
+ def do_remove
327
+ @association.clear
328
+ end
329
+
330
+ def return_value
331
+ @association
332
+ end
333
+
334
+ it_should_behave_like 'a method that orphans the resource'
335
+
336
+ it 'should empty the collection' do
337
+ @association << mock('other resource')
338
+ @association.should have(2).entries
339
+ do_remove
340
+ @association.should be_empty
341
+ end
342
+ end
343
+
344
+ it 'should provide #reload' do
345
+ @association.should respond_to(:reload)
346
+ end
347
+
348
+ describe '#reload' do
349
+ before do
350
+ @children = [ mock('child 1', :save => true), mock('child 2', :save => true) ]
351
+ @relationship.stub!(:get_children).and_return(@children)
352
+ end
353
+
354
+ it 'should set the @children ivar to nil' do
355
+ @association.__send__(:children).should == @children # Sanity check.
356
+
357
+ # We can't test the value of the @children instance variable since
358
+ # #instance_variable_get will be run on @children (thanks to
359
+ # Proxy#method_missing). Instead, test that Relationship#get_children is
360
+ # run -- if @children wasn't set to nil, this expectation should fail.
361
+ @relationship.should_receive(:get_children).once.and_return(@children)
362
+ @association.reload
363
+
364
+ # Trigger #get_children on the relationship.
365
+ @association.__send__(:children).should == @children
366
+ end
367
+
368
+ it 'should return self' do
369
+ @association.reload.should be_kind_of(DataMapper::Associations::OneToMany::Proxy)
370
+ @association.reload.object_id.should == @association.object_id
371
+ end
372
+ end
373
+
374
+ describe 'when deleting the parent' do
375
+ it 'should delete all the children without calling destroy if relationship :dependent is :delete_all'
376
+
377
+ it 'should destroy all the children if relationship :dependent is :destroy'
378
+
379
+ it 'should set the parent key for each child to nil if relationship :dependent is :nullify'
380
+
381
+ it 'should restrict the parent from being deleted if a child remains if relationship :dependent is restrict'
382
+
383
+ it 'should be restrict by default if relationship :dependent is not specified'
384
+ end
385
+ end