sam-dm-core 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. data/.autotest +26 -0
  2. data/CONTRIBUTING +51 -0
  3. data/FAQ +92 -0
  4. data/History.txt +145 -0
  5. data/MIT-LICENSE +22 -0
  6. data/Manifest.txt +125 -0
  7. data/QUICKLINKS +12 -0
  8. data/README.txt +143 -0
  9. data/Rakefile +30 -0
  10. data/SPECS +63 -0
  11. data/TODO +1 -0
  12. data/lib/dm-core.rb +224 -0
  13. data/lib/dm-core/adapters.rb +4 -0
  14. data/lib/dm-core/adapters/abstract_adapter.rb +202 -0
  15. data/lib/dm-core/adapters/data_objects_adapter.rb +707 -0
  16. data/lib/dm-core/adapters/mysql_adapter.rb +136 -0
  17. data/lib/dm-core/adapters/postgres_adapter.rb +188 -0
  18. data/lib/dm-core/adapters/sqlite3_adapter.rb +105 -0
  19. data/lib/dm-core/associations.rb +199 -0
  20. data/lib/dm-core/associations/many_to_many.rb +147 -0
  21. data/lib/dm-core/associations/many_to_one.rb +107 -0
  22. data/lib/dm-core/associations/one_to_many.rb +309 -0
  23. data/lib/dm-core/associations/one_to_one.rb +61 -0
  24. data/lib/dm-core/associations/relationship.rb +218 -0
  25. data/lib/dm-core/associations/relationship_chain.rb +81 -0
  26. data/lib/dm-core/auto_migrations.rb +113 -0
  27. data/lib/dm-core/collection.rb +638 -0
  28. data/lib/dm-core/dependency_queue.rb +31 -0
  29. data/lib/dm-core/hook.rb +11 -0
  30. data/lib/dm-core/identity_map.rb +45 -0
  31. data/lib/dm-core/is.rb +16 -0
  32. data/lib/dm-core/logger.rb +232 -0
  33. data/lib/dm-core/migrations/destructive_migrations.rb +17 -0
  34. data/lib/dm-core/migrator.rb +29 -0
  35. data/lib/dm-core/model.rb +471 -0
  36. data/lib/dm-core/naming_conventions.rb +84 -0
  37. data/lib/dm-core/property.rb +673 -0
  38. data/lib/dm-core/property_set.rb +162 -0
  39. data/lib/dm-core/query.rb +625 -0
  40. data/lib/dm-core/repository.rb +159 -0
  41. data/lib/dm-core/resource.rb +637 -0
  42. data/lib/dm-core/scope.rb +58 -0
  43. data/lib/dm-core/support.rb +7 -0
  44. data/lib/dm-core/support/array.rb +13 -0
  45. data/lib/dm-core/support/assertions.rb +8 -0
  46. data/lib/dm-core/support/errors.rb +23 -0
  47. data/lib/dm-core/support/kernel.rb +7 -0
  48. data/lib/dm-core/support/symbol.rb +41 -0
  49. data/lib/dm-core/transaction.rb +267 -0
  50. data/lib/dm-core/type.rb +160 -0
  51. data/lib/dm-core/type_map.rb +80 -0
  52. data/lib/dm-core/types.rb +19 -0
  53. data/lib/dm-core/types/boolean.rb +7 -0
  54. data/lib/dm-core/types/discriminator.rb +34 -0
  55. data/lib/dm-core/types/object.rb +24 -0
  56. data/lib/dm-core/types/paranoid_boolean.rb +34 -0
  57. data/lib/dm-core/types/paranoid_datetime.rb +33 -0
  58. data/lib/dm-core/types/serial.rb +9 -0
  59. data/lib/dm-core/types/text.rb +10 -0
  60. data/lib/dm-core/version.rb +3 -0
  61. data/script/all +5 -0
  62. data/script/performance.rb +203 -0
  63. data/script/profile.rb +87 -0
  64. data/spec/integration/association_spec.rb +1371 -0
  65. data/spec/integration/association_through_spec.rb +203 -0
  66. data/spec/integration/associations/many_to_many_spec.rb +449 -0
  67. data/spec/integration/associations/many_to_one_spec.rb +163 -0
  68. data/spec/integration/associations/one_to_many_spec.rb +151 -0
  69. data/spec/integration/auto_migrations_spec.rb +398 -0
  70. data/spec/integration/collection_spec.rb +1069 -0
  71. data/spec/integration/data_objects_adapter_spec.rb +32 -0
  72. data/spec/integration/dependency_queue_spec.rb +58 -0
  73. data/spec/integration/model_spec.rb +127 -0
  74. data/spec/integration/mysql_adapter_spec.rb +85 -0
  75. data/spec/integration/postgres_adapter_spec.rb +731 -0
  76. data/spec/integration/property_spec.rb +233 -0
  77. data/spec/integration/query_spec.rb +506 -0
  78. data/spec/integration/repository_spec.rb +57 -0
  79. data/spec/integration/resource_spec.rb +475 -0
  80. data/spec/integration/sqlite3_adapter_spec.rb +352 -0
  81. data/spec/integration/sti_spec.rb +208 -0
  82. data/spec/integration/strategic_eager_loading_spec.rb +138 -0
  83. data/spec/integration/transaction_spec.rb +75 -0
  84. data/spec/integration/type_spec.rb +271 -0
  85. data/spec/lib/logging_helper.rb +18 -0
  86. data/spec/lib/mock_adapter.rb +27 -0
  87. data/spec/lib/model_loader.rb +91 -0
  88. data/spec/lib/publicize_methods.rb +28 -0
  89. data/spec/models/vehicles.rb +34 -0
  90. data/spec/models/zoo.rb +47 -0
  91. data/spec/spec.opts +3 -0
  92. data/spec/spec_helper.rb +86 -0
  93. data/spec/unit/adapters/abstract_adapter_spec.rb +133 -0
  94. data/spec/unit/adapters/adapter_shared_spec.rb +15 -0
  95. data/spec/unit/adapters/data_objects_adapter_spec.rb +628 -0
  96. data/spec/unit/adapters/postgres_adapter_spec.rb +133 -0
  97. data/spec/unit/associations/many_to_many_spec.rb +17 -0
  98. data/spec/unit/associations/many_to_one_spec.rb +152 -0
  99. data/spec/unit/associations/one_to_many_spec.rb +393 -0
  100. data/spec/unit/associations/one_to_one_spec.rb +7 -0
  101. data/spec/unit/associations/relationship_spec.rb +71 -0
  102. data/spec/unit/associations_spec.rb +242 -0
  103. data/spec/unit/auto_migrations_spec.rb +111 -0
  104. data/spec/unit/collection_spec.rb +182 -0
  105. data/spec/unit/data_mapper_spec.rb +35 -0
  106. data/spec/unit/identity_map_spec.rb +126 -0
  107. data/spec/unit/is_spec.rb +80 -0
  108. data/spec/unit/migrator_spec.rb +33 -0
  109. data/spec/unit/model_spec.rb +339 -0
  110. data/spec/unit/naming_conventions_spec.rb +36 -0
  111. data/spec/unit/property_set_spec.rb +83 -0
  112. data/spec/unit/property_spec.rb +753 -0
  113. data/spec/unit/query_spec.rb +530 -0
  114. data/spec/unit/repository_spec.rb +93 -0
  115. data/spec/unit/resource_spec.rb +626 -0
  116. data/spec/unit/scope_spec.rb +142 -0
  117. data/spec/unit/transaction_spec.rb +493 -0
  118. data/spec/unit/type_map_spec.rb +114 -0
  119. data/spec/unit/type_spec.rb +119 -0
  120. data/tasks/ci.rb +68 -0
  121. data/tasks/dm.rb +63 -0
  122. data/tasks/doc.rb +20 -0
  123. data/tasks/gemspec.rb +23 -0
  124. data/tasks/hoe.rb +46 -0
  125. data/tasks/install.rb +20 -0
  126. metadata +216 -0
@@ -0,0 +1,352 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ if HAS_SQLITE3
4
+ describe DataMapper::Adapters::Sqlite3Adapter do
5
+ before :all do
6
+ @adapter = repository(:sqlite3).adapter
7
+ end
8
+
9
+ describe "auto migrating" do
10
+ before :all do
11
+ class Sputnik
12
+ include DataMapper::Resource
13
+
14
+ property :id, Serial
15
+ property :name, DM::Text
16
+ end
17
+ end
18
+
19
+ it "#upgrade_model should work" do
20
+ @adapter.destroy_model_storage(repository(:sqlite3), Sputnik)
21
+ @adapter.storage_exists?("sputniks").should == false
22
+ Sputnik.auto_migrate!(:sqlite3)
23
+ @adapter.storage_exists?("sputniks").should == true
24
+ @adapter.field_exists?("sputniks", "new_prop").should == false
25
+ Sputnik.property :new_prop, Integer
26
+ Sputnik.auto_upgrade!(:sqlite3)
27
+ @adapter.field_exists?("sputniks", "new_prop").should == true
28
+ end
29
+ end
30
+
31
+ describe "querying metadata" do
32
+ before :all do
33
+ class Sputnik
34
+ include DataMapper::Resource
35
+
36
+ property :id, Serial
37
+ property :name, DM::Text
38
+ end
39
+ end
40
+
41
+ before do
42
+ Sputnik.auto_migrate!(:sqlite3)
43
+ end
44
+
45
+ it "#storage_exists? should return true for tables that exist" do
46
+ @adapter.storage_exists?("sputniks").should == true
47
+ end
48
+
49
+ it "#storage_exists? should return false for tables that don't exist" do
50
+ @adapter.storage_exists?("space turds").should == false
51
+ end
52
+
53
+ it "#field_exists? should return true for columns that exist" do
54
+ @adapter.field_exists?("sputniks", "name").should == true
55
+ end
56
+
57
+ it "#storage_exists? should return false for tables that don't exist" do
58
+ @adapter.field_exists?("sputniks", "plur").should == false
59
+ end
60
+ end
61
+
62
+ describe "database file handling" do
63
+ it "should preserve the file path for file-based databases" do
64
+ file = 'newfile.db'
65
+ DataMapper.setup(:sqlite3file, "sqlite3:#{file}")
66
+ adapter = repository(:sqlite3file).adapter
67
+ adapter.uri.path.should == file
68
+ end
69
+
70
+ it "should have a path of just :memory: when using memory databases" do
71
+ DataMapper.setup(:sqlite3memory, "sqlite3::memory:")
72
+ adapter = repository(:sqlite3memory).adapter
73
+ adapter.uri.path.should == ':memory:'
74
+ end
75
+ end
76
+
77
+ describe "handling transactions" do
78
+ before :all do
79
+ class Sputnik
80
+ include DataMapper::Resource
81
+
82
+ property :id, Serial
83
+ property :name, DM::Text
84
+ end
85
+ end
86
+
87
+ before do
88
+ Sputnik.auto_migrate!(:sqlite3)
89
+
90
+ @transaction = DataMapper::Transaction.new(@adapter)
91
+ end
92
+
93
+ it "should rollback changes when #rollback_transaction is called" do
94
+ @transaction.commit do |transaction|
95
+ @adapter.execute("INSERT INTO sputniks (name) VALUES ('my pretty sputnik')")
96
+ transaction.rollback
97
+ end
98
+ @adapter.query("SELECT * FROM sputniks WHERE name = 'my pretty sputnik'").empty?.should == true
99
+ end
100
+
101
+ it "should commit changes when #commit_transaction is called" do
102
+ @transaction.commit do
103
+ @adapter.execute("INSERT INTO sputniks (name) VALUES ('my pretty sputnik')")
104
+ end
105
+ @adapter.query("SELECT * FROM sputniks WHERE name = 'my pretty sputnik'").size.should == 1
106
+ end
107
+ end
108
+
109
+ describe "reading & writing a database" do
110
+ before :all do
111
+ class User
112
+ include DataMapper::Resource
113
+
114
+ property :id, Serial
115
+ property :name, DM::Text
116
+ end
117
+ end
118
+
119
+ before do
120
+ User.auto_migrate!(:sqlite3)
121
+
122
+ @adapter.execute("INSERT INTO users (name) VALUES ('Paul')")
123
+ end
124
+
125
+ it 'should be able to #execute an arbitrary query' do
126
+ result = @adapter.execute("INSERT INTO users (name) VALUES ('Sam')")
127
+
128
+ result.affected_rows.should == 1
129
+ end
130
+
131
+ it 'should be able to #query' do
132
+ result = @adapter.query("SELECT * FROM users")
133
+
134
+ result.should be_kind_of(Array)
135
+ row = result.first
136
+ row.should be_kind_of(Struct)
137
+ row.members.should == %w{id name}
138
+
139
+ row.id.should == 1
140
+ row.name.should == 'Paul'
141
+ end
142
+
143
+ it 'should return an empty array if #query found no rows' do
144
+ @adapter.execute("DELETE FROM users")
145
+
146
+ result = nil
147
+ lambda { result = @adapter.query("SELECT * FROM users") }.should_not raise_error
148
+
149
+ result.should be_kind_of(Array)
150
+ result.size.should == 0
151
+ end
152
+ end
153
+
154
+ describe "CRUD for serial Key" do
155
+ before :all do
156
+ class VideoGame
157
+ include DataMapper::Resource
158
+
159
+ property :id, Serial
160
+ property :name, String
161
+ property :object, Object
162
+ property :notes, Text
163
+ end
164
+ end
165
+
166
+ before do
167
+ VideoGame.auto_migrate!(:sqlite3)
168
+ end
169
+
170
+ it 'should be able to create a record' do
171
+ time = Time.now
172
+ game = repository(:sqlite3) do
173
+ game = VideoGame.new(:name => 'System Shock', :object => time, :notes => "Test")
174
+ game.save
175
+ game.should_not be_a_new_record
176
+ game.should_not be_dirty
177
+ game
178
+ end
179
+ repository(:sqlite3) do
180
+ saved = VideoGame.first(:name => 'System Shock')
181
+ saved.id.should == game.id
182
+ saved.notes.should == game.notes
183
+ saved.object.should == time
184
+ end
185
+ end
186
+
187
+ it 'should be able to read a record' do
188
+ name = 'Wing Commander: Privateer'
189
+ id = @adapter.execute('INSERT INTO "video_games" ("name") VALUES (?)', name).insert_id
190
+
191
+ game = repository(:sqlite3) do
192
+ VideoGame.get(id)
193
+ end
194
+
195
+ game.name.should == name
196
+ game.should_not be_dirty
197
+ game.should_not be_a_new_record
198
+ end
199
+
200
+ it 'should be able to update a record' do
201
+ name = 'Resistance: Fall of Mon'
202
+ id = @adapter.execute('INSERT INTO "video_games" ("name") VALUES (?)', name).insert_id
203
+
204
+ game = repository(:sqlite3) do
205
+ VideoGame.get(id)
206
+ end
207
+
208
+ game.name = game.name.sub(/Mon/, 'Man')
209
+
210
+ game.should_not be_a_new_record
211
+ game.should be_dirty
212
+
213
+ repository(:sqlite3) do
214
+ game.save
215
+ end
216
+
217
+ game.should_not be_dirty
218
+
219
+ clone = repository(:sqlite3) do
220
+ VideoGame.get(id)
221
+ end
222
+
223
+ clone.name.should == game.name
224
+ end
225
+
226
+ it 'should be able to delete a record' do
227
+ name = 'Zelda'
228
+ id = @adapter.execute('INSERT INTO "video_games" ("name") VALUES (?)', name).insert_id
229
+
230
+ game = repository(:sqlite3) do
231
+ VideoGame.get(id)
232
+ end
233
+
234
+ game.name.should == name
235
+
236
+ repository(:sqlite3) do
237
+ game.destroy.should be_true
238
+ end
239
+ game.should be_a_new_record
240
+ game.should be_dirty
241
+ end
242
+
243
+ it 'should respond to Resource#get' do
244
+ name = 'Contra'
245
+ id = @adapter.execute('INSERT INTO "video_games" ("name") VALUES (?)', name).insert_id
246
+
247
+ contra = repository(:sqlite3) { VideoGame.get(id) }
248
+
249
+ contra.should_not be_nil
250
+ contra.should_not be_dirty
251
+ contra.should_not be_a_new_record
252
+ contra.id.should == id
253
+ end
254
+ end
255
+
256
+ describe "CRUD for Composite Key" do
257
+ before :all do
258
+ class BankCustomer
259
+ include DataMapper::Resource
260
+
261
+ property :bank, String, :key => true
262
+ property :account_number, String, :key => true
263
+ property :name, String
264
+ end
265
+ end
266
+
267
+ before do
268
+ BankCustomer.auto_migrate!(:sqlite3)
269
+ end
270
+
271
+ it 'should be able to create a record' do
272
+ customer = BankCustomer.new(:bank => 'Community Bank', :account_number => '123456', :name => 'David Hasselhoff')
273
+ repository(:sqlite3) do
274
+ customer.save
275
+ end
276
+
277
+ customer.should_not be_a_new_record
278
+ customer.should_not be_dirty
279
+
280
+ row = @adapter.query('SELECT "bank", "account_number" FROM "bank_customers" WHERE "name" = ?', customer.name).first
281
+ row.bank.should == customer.bank
282
+ row.account_number.should == customer.account_number
283
+ end
284
+
285
+ it 'should be able to read a record' do
286
+ bank, account_number, name = 'Chase', '4321', 'Super Wonderful'
287
+ @adapter.execute('INSERT INTO "bank_customers" ("bank", "account_number", "name") VALUES (?, ?, ?)', bank, account_number, name)
288
+
289
+ repository(:sqlite3) do
290
+ BankCustomer.get(bank, account_number).name.should == name
291
+ end
292
+ end
293
+
294
+ it 'should be able to update a record' do
295
+ bank, account_number, name = 'Wells Fargo', '00101001', 'Spider Pig'
296
+ @adapter.execute('INSERT INTO "bank_customers" ("bank", "account_number", "name") VALUES (?, ?, ?)', bank, account_number, name)
297
+
298
+ customer = repository(:sqlite3) do
299
+ BankCustomer.get(bank, account_number)
300
+ end
301
+
302
+ customer.name = 'Bat-Pig'
303
+
304
+ customer.should_not be_a_new_record
305
+ customer.should be_dirty
306
+
307
+ repository(:sqlite3) do
308
+ customer.save
309
+ end
310
+
311
+ customer.should_not be_dirty
312
+
313
+ clone = repository(:sqlite3) do
314
+ BankCustomer.get(bank, account_number)
315
+ end
316
+
317
+ clone.name.should == customer.name
318
+ end
319
+
320
+ it 'should be able to delete a record' do
321
+ bank, account_number, name = 'Megacorp', 'ABC', 'Flash Gordon'
322
+ @adapter.execute('INSERT INTO "bank_customers" ("bank", "account_number", "name") VALUES (?, ?, ?)', bank, account_number, name)
323
+
324
+ customer = repository(:sqlite3) do
325
+ BankCustomer.get(bank, account_number)
326
+ end
327
+
328
+ customer.name.should == name
329
+
330
+ repository(:sqlite3) do
331
+ customer.destroy.should be_true
332
+ end
333
+
334
+ customer.should be_a_new_record
335
+ customer.should be_dirty
336
+ end
337
+
338
+ it 'should respond to Resource#get' do
339
+ bank, account_number, name = 'Conchords', '1100101', 'Robo Boogie'
340
+ @adapter.execute('INSERT INTO "bank_customers" ("bank", "account_number", "name") VALUES (?, ?, ?)', bank, account_number, name)
341
+
342
+ robots = repository(:sqlite3) { BankCustomer.get(bank, account_number) }
343
+
344
+ robots.should_not be_nil
345
+ robots.should_not be_dirty
346
+ robots.should_not be_a_new_record
347
+ robots.bank.should == bank
348
+ robots.account_number.should == account_number
349
+ end
350
+ end
351
+ end
352
+ end
@@ -0,0 +1,208 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
+
4
+ if HAS_SQLITE3
5
+ describe DataMapper::AutoMigrations, '.auto_migrate! on STI models with sqlite3' do
6
+ before :all do
7
+ @adapter = repository(:sqlite3).adapter
8
+
9
+ @property_class = Struct.new(:name, :type, :nullable, :default, :serial)
10
+
11
+ class Book
12
+ include DataMapper::Resource
13
+
14
+ property :id, Serial
15
+ property :title, String, :nullable => false
16
+ property :isbn, Integer, :nullable => false
17
+ property :class_type, Discriminator
18
+ end
19
+
20
+ class Propaganda < Book
21
+ property :marxist, Boolean, :nullable => false, :default => false
22
+ end
23
+
24
+ class Fiction < Book
25
+ property :series, String
26
+ end
27
+
28
+ class ShortStory < Fiction
29
+ property :moral, String
30
+ end
31
+
32
+ class ScienceFiction < Fiction
33
+ property :aliens, Boolean
34
+ end
35
+
36
+ class SpaceWestern < ScienceFiction
37
+ property :cowboys, Boolean
38
+ end
39
+ end
40
+
41
+ describe "with the identity map" do
42
+ before :all do
43
+ Book.auto_migrate!(:sqlite3)
44
+ repository(:sqlite3) do
45
+ Propaganda.create(:title => "Something", :isbn => "129038")
46
+ end
47
+ end
48
+
49
+ it "should find the base model in the identity map" do
50
+ repository(:sqlite3) do
51
+ book = Book.first
52
+ book.object_id.should == Propaganda.first.object_id
53
+ end
54
+ end
55
+
56
+ it "should find the child model in the identity map" do
57
+ repository(:sqlite3) do
58
+ book = Propaganda.first
59
+ book.object_id.should == Book.first.object_id
60
+ end
61
+ end
62
+ end
63
+
64
+ describe "with a parent class" do
65
+ before :all do
66
+ Book.auto_migrate!(:sqlite3).should be_true
67
+
68
+ @table_set = @adapter.query('PRAGMA table_info("books")').inject({}) do |ts,column|
69
+ default = if 'NULL' == column.dflt_value || column.dflt_value.nil?
70
+ nil
71
+ else
72
+ /^(['"]?)(.*)\1$/.match(column.dflt_value)[2]
73
+ end
74
+
75
+ property = @property_class.new(
76
+ column.name,
77
+ column.type.upcase,
78
+ column.notnull == 0,
79
+ default,
80
+ column.pk == 1 # in SQLite3 the serial key is also primary
81
+ )
82
+
83
+ ts.update(property.name => property)
84
+ end
85
+
86
+ @index_list = @adapter.query('PRAGMA index_list("books")')
87
+ end
88
+
89
+ it "should create the child class property columns" do
90
+ @table_set.keys.should include("series", "marxist")
91
+ end
92
+
93
+ it "should create all property columns of the child classes in the inheritance tree" do
94
+ @table_set.keys.should include("moral")
95
+ end
96
+ end
97
+
98
+ describe "with a child class" do
99
+ before :all do
100
+ Propaganda.auto_migrate!(:sqlite3).should be_true
101
+
102
+ @table_set = @adapter.query('PRAGMA table_info("books")').inject({}) do |ts,column|
103
+ default = if 'NULL' == column.dflt_value || column.dflt_value.nil?
104
+ nil
105
+ else
106
+ /^(['"]?)(.*)\1$/.match(column.dflt_value)[2]
107
+ end
108
+
109
+ property = @property_class.new(
110
+ column.name,
111
+ column.type.upcase,
112
+ column.notnull == 0,
113
+ default,
114
+ column.pk == 1 # in SQLite3 the serial key is also primary
115
+ )
116
+
117
+ ts.update(property.name => property)
118
+ end
119
+
120
+ @index_list = @adapter.query('PRAGMA index_list("books")')
121
+ end
122
+
123
+ it "should create the parent class' property columns" do
124
+ @table_set.keys.should include("id", "title", "isbn")
125
+ end
126
+ end
127
+
128
+ describe "with a child class with it's own child class" do
129
+ before :all do
130
+ Fiction.auto_migrate!(:sqlite3).should be_true
131
+
132
+ @table_set = @adapter.query('PRAGMA table_info("books")').inject({}) do |ts,column|
133
+ default = if 'NULL' == column.dflt_value || column.dflt_value.nil?
134
+ nil
135
+ else
136
+ /^(['"]?)(.*)\1$/.match(column.dflt_value)[2]
137
+ end
138
+
139
+ property = @property_class.new(
140
+ column.name,
141
+ column.type.upcase,
142
+ column.notnull == 0,
143
+ default,
144
+ column.pk == 1 # in SQLite3 the serial key is also primary
145
+ )
146
+
147
+ ts.update(property.name => property)
148
+ end
149
+
150
+ @index_list = @adapter.query('PRAGMA index_list("books")')
151
+ end
152
+
153
+ it "should create the parent class' property columns" do
154
+ @table_set.keys.should include("id", "title", "isbn")
155
+ end
156
+
157
+ it "should create the child class' property columns" do
158
+ @table_set.keys.should include("moral")
159
+ end
160
+ end
161
+
162
+ describe "with a nephew class" do
163
+ before :all do
164
+ ShortStory.auto_migrate!(:sqlite3).should be_true
165
+
166
+ @table_set = @adapter.query('PRAGMA table_info("books")').inject({}) do |ts,column|
167
+ default = if 'NULL' == column.dflt_value || column.dflt_value.nil?
168
+ nil
169
+ else
170
+ /^(['"]?)(.*)\1$/.match(column.dflt_value)[2]
171
+ end
172
+
173
+ property = @property_class.new(
174
+ column.name,
175
+ column.type.upcase,
176
+ column.notnull == 0,
177
+ default,
178
+ column.pk == 1 # in SQLite3 the serial key is also primary
179
+ )
180
+
181
+ ts.update(property.name => property)
182
+ end
183
+ @index_list = @adapter.query('PRAGMA index_list("books")')
184
+ end
185
+
186
+
187
+ it "should create the grandparent class' property columns" do
188
+ @table_set.keys.should include("id", "title", "isbn")
189
+ end
190
+
191
+ it "should create the uncle class' property columns" do
192
+ @table_set.keys.should include("marxist")
193
+ end
194
+ end
195
+
196
+ describe "with a great-grandchild class" do
197
+ it "should inherit its parent's properties" do
198
+ SpaceWestern.properties[:aliens].should_not be_nil
199
+ end
200
+ it "should inherit its grandparent's properties" do
201
+ SpaceWestern.properties[:series].should_not be_nil
202
+ end
203
+ it "should inherit its great-granparent's properties" do
204
+ SpaceWestern.properties[:title].should_not be_nil
205
+ end
206
+ end
207
+ end
208
+ end