datamapper 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/CHANGELOG +16 -1
  2. data/README +10 -8
  3. data/environment.rb +1 -1
  4. data/example.rb +13 -5
  5. data/lib/data_mapper.rb +2 -0
  6. data/lib/data_mapper/adapters/abstract_adapter.rb +61 -0
  7. data/lib/data_mapper/adapters/data_object_adapter.rb +33 -6
  8. data/lib/data_mapper/adapters/mysql_adapter.rb +5 -0
  9. data/lib/data_mapper/adapters/postgresql_adapter.rb +12 -0
  10. data/lib/data_mapper/adapters/sql/commands/load_command.rb +6 -14
  11. data/lib/data_mapper/adapters/sql/mappings/column.rb +37 -26
  12. data/lib/data_mapper/adapters/sql/mappings/table.rb +50 -8
  13. data/lib/data_mapper/associations.rb +4 -5
  14. data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +2 -2
  15. data/lib/data_mapper/associations/has_many_association.rb +40 -13
  16. data/lib/data_mapper/associations/has_n_association.rb +1 -1
  17. data/lib/data_mapper/base.rb +5 -448
  18. data/lib/data_mapper/callbacks.rb +12 -2
  19. data/lib/data_mapper/context.rb +4 -0
  20. data/lib/data_mapper/database.rb +1 -1
  21. data/lib/data_mapper/identity_map.rb +2 -2
  22. data/lib/data_mapper/persistence.rb +538 -0
  23. data/lib/data_mapper/support/active_record_impersonation.rb +21 -3
  24. data/lib/data_mapper/support/errors.rb +2 -0
  25. data/lib/data_mapper/support/serialization.rb +7 -10
  26. data/lib/data_mapper/support/struct.rb +7 -0
  27. data/lib/data_mapper/validatable_extensions/validations/validates_uniqueness_of.rb +11 -4
  28. data/performance.rb +23 -10
  29. data/rakefile.rb +1 -1
  30. data/spec/active_record_impersonation_spec.rb +2 -6
  31. data/spec/acts_as_tree_spec.rb +3 -1
  32. data/spec/associations_spec.rb +40 -160
  33. data/spec/attributes_spec.rb +1 -1
  34. data/spec/base_spec.rb +41 -13
  35. data/spec/callbacks_spec.rb +32 -0
  36. data/spec/coersion_spec.rb +1 -1
  37. data/spec/column_spec.rb +22 -12
  38. data/spec/dependency_spec.rb +5 -3
  39. data/spec/embedded_value_spec.rb +33 -17
  40. data/spec/has_many_association_spec.rb +173 -0
  41. data/spec/legacy_spec.rb +2 -2
  42. data/spec/load_command_spec.rb +59 -7
  43. data/spec/models/animal.rb +6 -2
  44. data/spec/models/animals_exhibit.rb +3 -1
  45. data/spec/models/career.rb +2 -1
  46. data/spec/models/comment.rb +3 -1
  47. data/spec/models/exhibit.rb +3 -1
  48. data/spec/models/fruit.rb +3 -1
  49. data/spec/models/person.rb +10 -1
  50. data/spec/models/post.rb +3 -1
  51. data/spec/models/project.rb +3 -1
  52. data/spec/models/section.rb +3 -1
  53. data/spec/models/serializer.rb +3 -1
  54. data/spec/models/user.rb +3 -1
  55. data/spec/models/zoo.rb +3 -1
  56. data/spec/paranoia_spec.rb +3 -1
  57. data/spec/postgres_spec.rb +54 -0
  58. data/spec/save_command_spec.rb +9 -5
  59. data/spec/schema_spec.rb +0 -91
  60. data/spec/single_table_inheritance_spec.rb +8 -0
  61. data/spec/table_spec.rb +46 -0
  62. data/spec/validates_uniqueness_of_spec.rb +19 -1
  63. metadata +8 -10
  64. data/lib/data_mapper/associations/has_one_association.rb +0 -77
  65. data/plugins/dataobjects/do_rb +0 -0
@@ -1,6 +1,6 @@
1
1
  require File.dirname(__FILE__) + "/spec_helper"
2
2
 
3
- describe DataMapper::Base do
3
+ describe DataMapper::Persistence do
4
4
 
5
5
  it 'should allow mass-assignment of attributes' do
6
6
  zoo = Zoo.new(:name => 'MassAssignment', :notes => 'This is a test.')
data/spec/base_spec.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require File.dirname(__FILE__) + "/spec_helper"
2
2
 
3
- describe DataMapper::Base do
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[:name => 'a']
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 < DataMapper::Base
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 < DataMapper::Base
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 < DataMapper::Base
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 < DataMapper::Base
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 < DataMapper::Base
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[:name => 'Sam']
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 < DataMapper::Base
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[:name => 'Sam']
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 < DataMapper::Base
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 < DataMapper::Base
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 < DataMapper::Base
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
@@ -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
@@ -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[:name => 'DateCoersionTest']
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
- table = mappings::Table.new(database(:mock).adapter, "Cow")
27
- columns << mappings::Column.new(database(:mock).adapter, table, :one, :string, 1)
28
- columns << mappings::Column.new(database(:mock).adapter, table, :two, :string, 2)
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 = DataMapper::Adapters::Sql::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
@@ -1,18 +1,20 @@
1
1
  require File.dirname(__FILE__) + "/spec_helper"
2
2
 
3
- describe DataMapper::Base do
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::Base.dependencies.add('HappyCow') do |klass|
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 < DataMapper::Base
15
+ class HappyCow
16
+ include DataMapper::Persistence
17
+
16
18
  property :name, :string, :key => true
17
19
  end
18
20
 
@@ -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[:name => 'Bob']
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 < DataMapper::Base
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[:name => 'Sam']
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 < DataMapper::Base
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[:name => 'Sam']
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 < DataMapper::Base
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[:name => 'Sam']
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 < DataMapper::Base
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[:name => 'Sam']
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 < DataMapper::Base
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[:name => 'Sam']
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 < DataMapper::Base
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[:name => 'Sam']
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 < DataMapper::Base
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[:name => 'Sam']
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 < DataMapper::Base
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[:name => 'Sam']
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