datamapper 0.2.4 → 0.2.5
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 +16 -1
- data/README +10 -8
- data/environment.rb +1 -1
- data/example.rb +13 -5
- data/lib/data_mapper.rb +2 -0
- data/lib/data_mapper/adapters/abstract_adapter.rb +61 -0
- data/lib/data_mapper/adapters/data_object_adapter.rb +33 -6
- data/lib/data_mapper/adapters/mysql_adapter.rb +5 -0
- data/lib/data_mapper/adapters/postgresql_adapter.rb +12 -0
- data/lib/data_mapper/adapters/sql/commands/load_command.rb +6 -14
- data/lib/data_mapper/adapters/sql/mappings/column.rb +37 -26
- data/lib/data_mapper/adapters/sql/mappings/table.rb +50 -8
- data/lib/data_mapper/associations.rb +4 -5
- data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +2 -2
- data/lib/data_mapper/associations/has_many_association.rb +40 -13
- data/lib/data_mapper/associations/has_n_association.rb +1 -1
- data/lib/data_mapper/base.rb +5 -448
- data/lib/data_mapper/callbacks.rb +12 -2
- data/lib/data_mapper/context.rb +4 -0
- data/lib/data_mapper/database.rb +1 -1
- data/lib/data_mapper/identity_map.rb +2 -2
- data/lib/data_mapper/persistence.rb +538 -0
- data/lib/data_mapper/support/active_record_impersonation.rb +21 -3
- data/lib/data_mapper/support/errors.rb +2 -0
- data/lib/data_mapper/support/serialization.rb +7 -10
- data/lib/data_mapper/support/struct.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_uniqueness_of.rb +11 -4
- data/performance.rb +23 -10
- data/rakefile.rb +1 -1
- data/spec/active_record_impersonation_spec.rb +2 -6
- data/spec/acts_as_tree_spec.rb +3 -1
- data/spec/associations_spec.rb +40 -160
- data/spec/attributes_spec.rb +1 -1
- data/spec/base_spec.rb +41 -13
- data/spec/callbacks_spec.rb +32 -0
- data/spec/coersion_spec.rb +1 -1
- data/spec/column_spec.rb +22 -12
- data/spec/dependency_spec.rb +5 -3
- data/spec/embedded_value_spec.rb +33 -17
- data/spec/has_many_association_spec.rb +173 -0
- data/spec/legacy_spec.rb +2 -2
- data/spec/load_command_spec.rb +59 -7
- data/spec/models/animal.rb +6 -2
- data/spec/models/animals_exhibit.rb +3 -1
- data/spec/models/career.rb +2 -1
- data/spec/models/comment.rb +3 -1
- data/spec/models/exhibit.rb +3 -1
- data/spec/models/fruit.rb +3 -1
- data/spec/models/person.rb +10 -1
- data/spec/models/post.rb +3 -1
- data/spec/models/project.rb +3 -1
- data/spec/models/section.rb +3 -1
- data/spec/models/serializer.rb +3 -1
- data/spec/models/user.rb +3 -1
- data/spec/models/zoo.rb +3 -1
- data/spec/paranoia_spec.rb +3 -1
- data/spec/postgres_spec.rb +54 -0
- data/spec/save_command_spec.rb +9 -5
- data/spec/schema_spec.rb +0 -91
- data/spec/single_table_inheritance_spec.rb +8 -0
- data/spec/table_spec.rb +46 -0
- data/spec/validates_uniqueness_of_spec.rb +19 -1
- metadata +8 -10
- data/lib/data_mapper/associations/has_one_association.rb +0 -77
- data/plugins/dataobjects/do_rb +0 -0
data/spec/attributes_spec.rb
CHANGED
data/spec/base_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.dirname(__FILE__) + "/spec_helper"
|
2
2
|
|
3
|
-
describe DataMapper::
|
3
|
+
describe DataMapper::Persistence do
|
4
4
|
|
5
5
|
it "attributes method should load all lazy-loaded values" do
|
6
6
|
Animal.first(:name => 'Cup').attributes[:notes].should == 'I am a Cup!'
|
@@ -137,7 +137,7 @@ describe 'A new record' do
|
|
137
137
|
|
138
138
|
it "should have original values when loaded from the database" do
|
139
139
|
Person.create(:name => 'a')
|
140
|
-
x = Person
|
140
|
+
x = Person.first(:name => 'a')
|
141
141
|
x.original_values.should_not be_empty
|
142
142
|
x.original_values.keys.should include(:name)
|
143
143
|
x.original_values[:name].should == "a"
|
@@ -165,7 +165,9 @@ end
|
|
165
165
|
describe 'Properties' do
|
166
166
|
|
167
167
|
it 'should default to public method visibility' do
|
168
|
-
class SoftwareEngineer
|
168
|
+
class SoftwareEngineer
|
169
|
+
include DataMapper::Persistence
|
170
|
+
|
169
171
|
set_table_name 'people'
|
170
172
|
property :name, :string
|
171
173
|
end
|
@@ -175,7 +177,9 @@ describe 'Properties' do
|
|
175
177
|
end
|
176
178
|
|
177
179
|
it 'should respect protected property options' do
|
178
|
-
class SanitationEngineer
|
180
|
+
class SanitationEngineer
|
181
|
+
include DataMapper::Persistence
|
182
|
+
|
179
183
|
set_table_name 'people'
|
180
184
|
property :name, :string, :reader => :protected
|
181
185
|
property :age, :integer, :writer => :protected
|
@@ -186,7 +190,9 @@ describe 'Properties' do
|
|
186
190
|
end
|
187
191
|
|
188
192
|
it 'should respect private property options' do
|
189
|
-
class ElectricalEngineer
|
193
|
+
class ElectricalEngineer
|
194
|
+
include DataMapper::Persistence
|
195
|
+
|
190
196
|
set_table_name 'people'
|
191
197
|
property :name, :string, :reader => :private
|
192
198
|
property :age, :integer, :writer => :private
|
@@ -197,7 +203,9 @@ describe 'Properties' do
|
|
197
203
|
end
|
198
204
|
|
199
205
|
it 'should set both reader and writer visibiliy when accessor option is passed' do
|
200
|
-
class TrainEngineer
|
206
|
+
class TrainEngineer
|
207
|
+
include DataMapper::Persistence
|
208
|
+
|
201
209
|
property :name, :string, :accessor => :private
|
202
210
|
end
|
203
211
|
|
@@ -206,32 +214,38 @@ describe 'Properties' do
|
|
206
214
|
end
|
207
215
|
|
208
216
|
it 'should only be listed in attributes if they have public getters' do
|
209
|
-
class SalesEngineer
|
217
|
+
class SalesEngineer
|
218
|
+
include DataMapper::Persistence
|
219
|
+
|
210
220
|
set_table_name 'people'
|
211
221
|
property :name, :string
|
212
222
|
property :age, :integer, :reader => :private
|
213
223
|
end
|
214
224
|
|
215
|
-
@sam = SalesEngineer
|
225
|
+
@sam = SalesEngineer.first(:name => 'Sam')
|
216
226
|
# note: id default key gets a public reader by default (but writer is protected)
|
217
227
|
@sam.attributes.should == {:id => @sam.id, :name => @sam.name}
|
218
228
|
end
|
219
229
|
|
220
230
|
it 'should not allow mass assignment if private or protected' do
|
221
|
-
class ChemicalEngineer
|
231
|
+
class ChemicalEngineer
|
232
|
+
include DataMapper::Persistence
|
233
|
+
|
222
234
|
set_table_name 'people'
|
223
235
|
property :name, :string, :writer => :private
|
224
236
|
property :age, :integer
|
225
237
|
end
|
226
238
|
|
227
|
-
@sam = ChemicalEngineer
|
239
|
+
@sam = ChemicalEngineer.first(:name => 'Sam')
|
228
240
|
@sam.attributes = {:name => 'frank', :age => 101}
|
229
241
|
@sam.age.should == 101
|
230
242
|
@sam.name.should == 'Sam'
|
231
243
|
end
|
232
244
|
|
233
245
|
it 'should allow :protected to be passed as an alias for a public reader, protected writer' do
|
234
|
-
class CivilEngineer
|
246
|
+
class CivilEngineer
|
247
|
+
include DataMapper::Persistence
|
248
|
+
|
235
249
|
set_table_name 'people'
|
236
250
|
property :name, :string, :protected => true
|
237
251
|
end
|
@@ -241,7 +255,9 @@ describe 'Properties' do
|
|
241
255
|
end
|
242
256
|
|
243
257
|
it 'should allow :private to be passed as an alias for a public reader, private writer' do
|
244
|
-
class AudioEngineer
|
258
|
+
class AudioEngineer
|
259
|
+
include DataMapper::Persistence
|
260
|
+
|
245
261
|
set_table_name 'people'
|
246
262
|
property :name, :string, :private => true
|
247
263
|
end
|
@@ -252,11 +268,23 @@ describe 'Properties' do
|
|
252
268
|
|
253
269
|
it 'should raise an error when invalid options are passsed' do
|
254
270
|
lambda do
|
255
|
-
class JumpyCow
|
271
|
+
class JumpyCow
|
272
|
+
include DataMapper::Persistence
|
273
|
+
|
256
274
|
set_table_name 'animals'
|
257
275
|
property :name, :string, :laze => true
|
258
276
|
end
|
259
277
|
end.should raise_error(ArgumentError)
|
260
278
|
end
|
261
279
|
|
280
|
+
it 'should raise an error when the first argument to index isnt an array' do
|
281
|
+
lambda do
|
282
|
+
class JumpyCow
|
283
|
+
include DataMapper::Persistence
|
284
|
+
|
285
|
+
set_table_name 'animals'
|
286
|
+
index :name, :parent
|
287
|
+
end
|
288
|
+
end.should raise_error(ArgumentError)
|
289
|
+
end
|
262
290
|
end
|
data/spec/callbacks_spec.rb
CHANGED
@@ -44,4 +44,36 @@ describe DataMapper::Callbacks do
|
|
44
44
|
|
45
45
|
end
|
46
46
|
|
47
|
+
it "should execute before_save regardless of dirty state" do
|
48
|
+
|
49
|
+
Post.before_save do |post|
|
50
|
+
post.instance_variable_set("@one", 'moo')
|
51
|
+
end
|
52
|
+
|
53
|
+
Post.before_save do |post|
|
54
|
+
post.instance_variable_set("@two", 'cow')
|
55
|
+
end
|
56
|
+
|
57
|
+
Post.before_save :red_cow
|
58
|
+
|
59
|
+
class Post
|
60
|
+
def red_cow
|
61
|
+
@three = "blue_cow"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
post = Post.new(:title => 'bob')
|
66
|
+
post.save
|
67
|
+
|
68
|
+
post = Post.first(:title => 'bob')
|
69
|
+
post.instance_variable_get("@one").should be_nil
|
70
|
+
post.instance_variable_get("@two").should be_nil
|
71
|
+
post.instance_variable_get("@three").should be_nil
|
72
|
+
|
73
|
+
post.save
|
74
|
+
post.instance_variable_get("@one").should eql('moo')
|
75
|
+
post.instance_variable_get("@two").should eql('cow')
|
76
|
+
post.instance_variable_get("@three").should eql('blue_cow')
|
77
|
+
end
|
78
|
+
|
47
79
|
end
|
data/spec/coersion_spec.rb
CHANGED
@@ -18,7 +18,7 @@ describe DataMapper::Adapters::Sql::Coersion do
|
|
18
18
|
dob = Date::today
|
19
19
|
bob = Person.create(:name => 'DateCoersionTest', :date_of_birth => dob)
|
20
20
|
|
21
|
-
bob2 = Person
|
21
|
+
bob2 = Person.first(:name => 'DateCoersionTest')
|
22
22
|
|
23
23
|
bob.date_of_birth.should eql(dob)
|
24
24
|
bob2.date_of_birth.should eql(dob)
|
data/spec/column_spec.rb
CHANGED
@@ -3,8 +3,13 @@ require File.dirname(__FILE__) + "/spec_helper"
|
|
3
3
|
describe DataMapper::Adapters::Sql::Mappings::Column do
|
4
4
|
|
5
5
|
before(:all) do
|
6
|
+
@mappings = DataMapper::Adapters::Sql::Mappings
|
6
7
|
fixtures(:zoos)
|
7
8
|
end
|
9
|
+
|
10
|
+
def table
|
11
|
+
@table ||= @mappings::Table.new(database(:mock).adapter, "Cow")
|
12
|
+
end
|
8
13
|
|
9
14
|
it "should only lazy loading text columns by default" do
|
10
15
|
table = database.table(Zoo)
|
@@ -18,21 +23,17 @@ describe DataMapper::Adapters::Sql::Mappings::Column do
|
|
18
23
|
end
|
19
24
|
|
20
25
|
it "should be unique within a set" do
|
21
|
-
|
22
|
-
mappings = DataMapper::Adapters::Sql::Mappings
|
23
|
-
|
24
26
|
columns = SortedSet.new
|
25
27
|
|
26
|
-
|
27
|
-
columns << mappings::Column.new(database(:mock).adapter, table, :
|
28
|
-
columns << mappings::Column.new(database(:mock).adapter, table, :
|
29
|
-
columns << mappings::Column.new(database(:mock).adapter, table, :three, :string, 3)
|
28
|
+
columns << @mappings::Column.new(database(:mock).adapter, table, :one, :string, 1)
|
29
|
+
columns << @mappings::Column.new(database(:mock).adapter, table, :two, :string, 2)
|
30
|
+
columns << @mappings::Column.new(database(:mock).adapter, table, :three, :string, 3)
|
30
31
|
columns.should have(3).entries
|
31
32
|
|
32
|
-
columns << mappings::Column.new(database(:mock).adapter, table, :two, :integer, 3)
|
33
|
+
columns << @mappings::Column.new(database(:mock).adapter, table, :two, :integer, 3)
|
33
34
|
columns.should have(3).entries
|
34
35
|
|
35
|
-
columns << mappings::Column.new(database(:mock).adapter, table, :id, :integer, -1)
|
36
|
+
columns << @mappings::Column.new(database(:mock).adapter, table, :id, :integer, -1)
|
36
37
|
columns.should have(4).entries
|
37
38
|
end
|
38
39
|
|
@@ -92,10 +93,19 @@ describe DataMapper::Adapters::Sql::Mappings::Column do
|
|
92
93
|
end
|
93
94
|
|
94
95
|
it "should default the size of an integer column to 11" do
|
95
|
-
mappings
|
96
|
-
table = mappings::Table.new(database(:mock).adapter, "Zebu")
|
97
|
-
integer = mappings::Column.new(database(:mock).adapter, table, :age, :integer, 1)
|
96
|
+
integer = @mappings::Column.new(database(:mock).adapter, table, :age, :integer, 1)
|
98
97
|
integer.size.should == 11
|
99
98
|
end
|
100
99
|
|
100
|
+
it "should be able to create a column with unique index" do
|
101
|
+
column = table.add_column("name", :string, :index => :unique)
|
102
|
+
column.unique?.should be_true
|
103
|
+
table.to_create_sql.should match(/UNIQUE/)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should be able to create an indexed column" do
|
107
|
+
column = table.add_column("age", :integer, :index => true)
|
108
|
+
column.index?.should be_true
|
109
|
+
table.to_create_index_sql[0].should match(/CREATE INDEX cow_age_index/)
|
110
|
+
end
|
101
111
|
end
|
data/spec/dependency_spec.rb
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
require File.dirname(__FILE__) + "/spec_helper"
|
2
2
|
|
3
|
-
describe DataMapper::
|
3
|
+
describe DataMapper::Persistence do
|
4
4
|
|
5
5
|
it "should be able to add a dependency for a class not yet defined" do
|
6
6
|
|
7
7
|
$happy_cow_defined = false
|
8
8
|
|
9
|
-
DataMapper::
|
9
|
+
DataMapper::Persistence.dependencies.add('HappyCow') do |klass|
|
10
10
|
klass.should eql(Object.const_get('HappyCow'))
|
11
11
|
database.table(klass).key.name.should eql(:name)
|
12
12
|
$happy_cow_defined = true
|
13
13
|
end
|
14
14
|
|
15
|
-
class HappyCow
|
15
|
+
class HappyCow
|
16
|
+
include DataMapper::Persistence
|
17
|
+
|
16
18
|
property :name, :string, :key => true
|
17
19
|
end
|
18
20
|
|
data/spec/embedded_value_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require File.dirname(__FILE__) + "/spec_helper"
|
|
3
3
|
describe DataMapper::EmbeddedValue do
|
4
4
|
|
5
5
|
before(:all) do
|
6
|
-
@bob = Person
|
6
|
+
@bob = Person.first(:name => 'Bob')
|
7
7
|
end
|
8
8
|
|
9
9
|
it 'should proxy getting values for you' do
|
@@ -19,7 +19,9 @@ describe DataMapper::EmbeddedValue do
|
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'should not require prefix' do
|
22
|
-
class PointyHeadedBoss
|
22
|
+
class PointyHeadedBoss
|
23
|
+
include DataMapper::Persistence
|
24
|
+
|
23
25
|
set_table_name 'people'
|
24
26
|
property :name, :string
|
25
27
|
|
@@ -31,12 +33,14 @@ describe DataMapper::EmbeddedValue do
|
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
34
|
-
@sam = PointyHeadedBoss
|
36
|
+
@sam = PointyHeadedBoss.first(:name => 'Sam')
|
35
37
|
@sam.address.address_street.should == '1337 Duck Way'
|
36
38
|
end
|
37
39
|
|
38
40
|
it 'should add convenience methods to the non-embedded base' do
|
39
|
-
class Employee
|
41
|
+
class Employee
|
42
|
+
include DataMapper::Persistence
|
43
|
+
|
40
44
|
set_table_name 'people'
|
41
45
|
property :name, :string
|
42
46
|
|
@@ -46,12 +50,14 @@ describe DataMapper::EmbeddedValue do
|
|
46
50
|
end
|
47
51
|
end
|
48
52
|
|
49
|
-
@sam = Employee
|
53
|
+
@sam = Employee.first(:name => 'Sam')
|
50
54
|
@sam.address_street.should == '1337 Duck Way'
|
51
55
|
end
|
52
56
|
|
53
57
|
it 'should support lazy loading of embedded properties' do
|
54
|
-
class Human
|
58
|
+
class Human
|
59
|
+
include DataMapper::Persistence
|
60
|
+
|
55
61
|
set_table_name 'people'
|
56
62
|
property :name, :string
|
57
63
|
|
@@ -61,12 +67,14 @@ describe DataMapper::EmbeddedValue do
|
|
61
67
|
end
|
62
68
|
end
|
63
69
|
|
64
|
-
@sam = Human
|
70
|
+
@sam = Human.first(:name => 'Sam')
|
65
71
|
@sam.address.street.should == '1337 Duck Way'
|
66
72
|
end
|
67
73
|
|
68
74
|
it 'should default to public method visibility for all' do
|
69
|
-
class SoftwareEngineer
|
75
|
+
class SoftwareEngineer
|
76
|
+
include DataMapper::Persistence
|
77
|
+
|
70
78
|
set_table_name 'people'
|
71
79
|
property :name, :string
|
72
80
|
|
@@ -75,13 +83,15 @@ describe DataMapper::EmbeddedValue do
|
|
75
83
|
end
|
76
84
|
end
|
77
85
|
|
78
|
-
@sam = SoftwareEngineer
|
86
|
+
@sam = SoftwareEngineer.first(:name => 'Sam')
|
79
87
|
public_properties = @sam.address.class.public_instance_methods.select { |m| ["city", "city="].include?(m) }
|
80
88
|
public_properties.length.should == 2
|
81
89
|
end
|
82
90
|
|
83
91
|
it 'should respect protected property options for all' do
|
84
|
-
class SanitationEngineer
|
92
|
+
class SanitationEngineer
|
93
|
+
include DataMapper::Persistence
|
94
|
+
|
85
95
|
set_table_name 'people'
|
86
96
|
property :name, :string
|
87
97
|
|
@@ -91,13 +101,15 @@ describe DataMapper::EmbeddedValue do
|
|
91
101
|
end
|
92
102
|
end
|
93
103
|
|
94
|
-
@sam = SanitationEngineer
|
104
|
+
@sam = SanitationEngineer.first(:name => 'Sam')
|
95
105
|
protected_properties = @sam.address.class.protected_instance_methods.select { |m| ["city", "street"].include?(m) }
|
96
106
|
protected_properties.length.should == 2
|
97
107
|
end
|
98
108
|
|
99
109
|
it 'should respect private property options for all' do
|
100
|
-
class ElectricalEngineer
|
110
|
+
class ElectricalEngineer
|
111
|
+
include DataMapper::Persistence
|
112
|
+
|
101
113
|
set_table_name 'people'
|
102
114
|
property :name, :string, :reader => :private
|
103
115
|
|
@@ -107,13 +119,15 @@ describe DataMapper::EmbeddedValue do
|
|
107
119
|
end
|
108
120
|
end
|
109
121
|
|
110
|
-
@sam = ElectricalEngineer
|
122
|
+
@sam = ElectricalEngineer.first(:name => 'Sam')
|
111
123
|
private_properties = @sam.address.class.private_instance_methods.select { |m| ["city=", "street="].include?(m) }
|
112
124
|
private_properties.length.should == 2
|
113
125
|
end
|
114
126
|
|
115
127
|
it 'should set both reader and writer visibiliy for all when accessor option is passed' do
|
116
|
-
class TrainEngineer
|
128
|
+
class TrainEngineer
|
129
|
+
include DataMapper::Persistence
|
130
|
+
|
117
131
|
set_table_name 'people'
|
118
132
|
property :name, :string, :reader => :private
|
119
133
|
|
@@ -122,13 +136,15 @@ describe DataMapper::EmbeddedValue do
|
|
122
136
|
end
|
123
137
|
end
|
124
138
|
|
125
|
-
@sam = TrainEngineer
|
139
|
+
@sam = TrainEngineer.first(:name => 'Sam')
|
126
140
|
private_properties = @sam.address.class.private_instance_methods.select { |m| ["city", "city="].include?(m) }
|
127
141
|
private_properties.length.should == 2
|
128
142
|
end
|
129
143
|
|
130
144
|
it 'should allow individual properties to override method visibility options passed on the block' do
|
131
|
-
class ChemicalEngineer
|
145
|
+
class ChemicalEngineer
|
146
|
+
include DataMapper::Persistence
|
147
|
+
|
132
148
|
set_table_name 'people'
|
133
149
|
property :name, :string
|
134
150
|
|
@@ -138,7 +154,7 @@ describe DataMapper::EmbeddedValue do
|
|
138
154
|
end
|
139
155
|
end
|
140
156
|
|
141
|
-
@sam = ChemicalEngineer
|
157
|
+
@sam = ChemicalEngineer.first(:name => 'Sam')
|
142
158
|
public_properties = @sam.address.class.public_instance_methods.select { |m| ["street", "street="].include?(m) }
|
143
159
|
public_properties.length.should == 2
|
144
160
|
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/spec_helper"
|
2
|
+
|
3
|
+
describe DataMapper::Associations::HasManyAssociation do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
fixtures(:zoos)
|
7
|
+
fixtures(:exhibits)
|
8
|
+
fixtures(:fruit)
|
9
|
+
fixtures(:animals)
|
10
|
+
end
|
11
|
+
|
12
|
+
after(:all) do
|
13
|
+
fixtures(:fruit)
|
14
|
+
fixtures(:animals)
|
15
|
+
end
|
16
|
+
|
17
|
+
before(:each) do
|
18
|
+
@zoo = Zoo.new(:name => "ZOO")
|
19
|
+
@zoo.save
|
20
|
+
end
|
21
|
+
|
22
|
+
after(:each) do
|
23
|
+
@zoo.destroy!
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return an empty Enumerable for new objects" do
|
27
|
+
project = Project.new
|
28
|
+
project.sections.should be_a_kind_of(Enumerable)
|
29
|
+
project.sections.should be_empty
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should display correctly when inspected" do
|
33
|
+
Zoo.first(:name => 'Dallas').exhibits.inspect.should match(/\#\<Exhibit\:0x.{7}/)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should lazily-load the association when Enumerable methods are called' do
|
37
|
+
database do |db|
|
38
|
+
san_diego = Zoo.first(:name => 'San Diego')
|
39
|
+
san_diego.exhibits.size.should == 2
|
40
|
+
san_diego.exhibits.should include(Exhibit.first(:name => 'Monkey Mayhem'))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should eager-load associations for an entire set' do
|
45
|
+
database do
|
46
|
+
zoos = Zoo.all
|
47
|
+
zoos.each do |zoo|
|
48
|
+
zoo.exhibits.each do |exhibit|
|
49
|
+
exhibit.zoo.should == zoo
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should be dirty even when clean objects are associated" do
|
56
|
+
zoo = Zoo.first(:name => 'New York')
|
57
|
+
zoo.exhibits << Exhibit.first
|
58
|
+
zoo.should be_dirty
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should proxy associations on the associated type" do
|
62
|
+
Zoo.first(:name => 'Miami').exhibits.animals.size.should == 1
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should have a valid zoo setup for testing" do
|
66
|
+
@zoo.should be_valid
|
67
|
+
@zoo.should_not be_a_new_record
|
68
|
+
@zoo.id.should_not be_nil
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should generate the SQL for a join statement" do
|
72
|
+
exhibits_association = database(:mock).schema[Zoo].associations.find { |a| a.name == :exhibits }
|
73
|
+
|
74
|
+
exhibits_association.to_sql.should == <<-EOS.compress_lines
|
75
|
+
JOIN `exhibits` ON `exhibits`.`zoo_id` = `zoos`.`id`
|
76
|
+
EOS
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should add an item to an association" do
|
80
|
+
bear = Exhibit.new( :name => "Bear")
|
81
|
+
@zoo.exhibits << bear
|
82
|
+
@zoo.exhibits.should include(bear)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should build a new item" do
|
86
|
+
bear = @zoo.exhibits.build( :name => "Bear" )
|
87
|
+
bear.should be_kind_of(Exhibit)
|
88
|
+
@zoo.exhibits.should include(bear)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should not save the item when building" do
|
92
|
+
bear = @zoo.exhibits.build( :name => "Bear" )
|
93
|
+
bear.should be_new_record
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should create a new item" do
|
97
|
+
bear = @zoo.exhibits.create( :name => "Bear" )
|
98
|
+
bear.should be_kind_of(Exhibit)
|
99
|
+
@zoo.exhibits.should include(bear)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should save the item when creating" do
|
103
|
+
bear = @zoo.exhibits.create( :name => "Bear" )
|
104
|
+
bear.should_not be_new_record
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should set the association to a saved target when added with <<" do
|
108
|
+
pirahna = Exhibit.new(:name => "Pirahna")
|
109
|
+
pirahna.zoo_id.should be_nil
|
110
|
+
|
111
|
+
@zoo.exhibits << pirahna
|
112
|
+
pirahna.zoo.should == @zoo
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should set the association to a non-saved target when added with <<" do
|
116
|
+
zoo = Zoo.new(:name => "My Zoo")
|
117
|
+
kangaroo = Exhibit.new(:name => "Kangaroo")
|
118
|
+
zoo.exhibits << kangaroo
|
119
|
+
kangaroo.zoo.should == zoo
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should set the id of the exhibit when the associated zoo is saved" do
|
123
|
+
snake = Exhibit.new(:name => "Snake")
|
124
|
+
@zoo.exhibits << snake
|
125
|
+
@zoo.save
|
126
|
+
@zoo.id.should == snake.zoo_id
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should set the id of an already saved exibit if it's added to a different zoo" do
|
130
|
+
beaver = Exhibit.new(:name => "Beaver")
|
131
|
+
beaver.save
|
132
|
+
beaver.should_not be_a_new_record
|
133
|
+
@zoo.exhibits << beaver
|
134
|
+
@zoo.save
|
135
|
+
beaver.zoo.should == @zoo
|
136
|
+
beaver.zoo_id.should == @zoo.id
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should set the size of the assocation" do
|
140
|
+
@zoo.exhibits << Exhibit.new(:name => "anonymous")
|
141
|
+
@zoo.exhibits.size.should == 1
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should give the association when an inspect is done on it" do
|
145
|
+
whale = Exhibit.new(:name => "Whale")
|
146
|
+
@zoo.exhibits << whale
|
147
|
+
@zoo.exhibits.should_not == "nil"
|
148
|
+
@zoo.exhibits.inspect.should_not be_nil
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should generate the SQL for a join statement" do
|
152
|
+
fruit_association = database(:mock).schema[Animal].associations.find { |a| a.name == :favourite_fruit }
|
153
|
+
|
154
|
+
fruit_association.to_sql.should == <<-EOS.compress_lines
|
155
|
+
JOIN `fruit` ON `fruit`.`devourer_id` = `animals`.`id`
|
156
|
+
EOS
|
157
|
+
end
|
158
|
+
|
159
|
+
it "is assigned a devourer_id" do
|
160
|
+
bob = Animal.new(:name => 'bob')
|
161
|
+
fruit = Fruit.first
|
162
|
+
bob.favourite_fruit = fruit
|
163
|
+
|
164
|
+
bob.save
|
165
|
+
|
166
|
+
bob.reload!
|
167
|
+
fruit.devourer_id.should eql(bob.id)
|
168
|
+
bob.favourite_fruit.should == fruit
|
169
|
+
|
170
|
+
fruit.reload!
|
171
|
+
fruit.devourer_of_souls.should == bob
|
172
|
+
end
|
173
|
+
end
|