datamapper-dm-core 0.9.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. data/.autotest +26 -0
  2. data/.gitignore +18 -0
  3. data/CONTRIBUTING +51 -0
  4. data/FAQ +92 -0
  5. data/History.txt +41 -0
  6. data/MIT-LICENSE +22 -0
  7. data/Manifest.txt +130 -0
  8. data/QUICKLINKS +11 -0
  9. data/README.txt +143 -0
  10. data/Rakefile +30 -0
  11. data/SPECS +62 -0
  12. data/TODO +1 -0
  13. data/dm-core.gemspec +40 -0
  14. data/lib/dm-core.rb +217 -0
  15. data/lib/dm-core/adapters.rb +16 -0
  16. data/lib/dm-core/adapters/abstract_adapter.rb +209 -0
  17. data/lib/dm-core/adapters/data_objects_adapter.rb +716 -0
  18. data/lib/dm-core/adapters/in_memory_adapter.rb +87 -0
  19. data/lib/dm-core/adapters/mysql_adapter.rb +136 -0
  20. data/lib/dm-core/adapters/postgres_adapter.rb +189 -0
  21. data/lib/dm-core/adapters/sqlite3_adapter.rb +105 -0
  22. data/lib/dm-core/associations.rb +207 -0
  23. data/lib/dm-core/associations/many_to_many.rb +147 -0
  24. data/lib/dm-core/associations/many_to_one.rb +107 -0
  25. data/lib/dm-core/associations/one_to_many.rb +315 -0
  26. data/lib/dm-core/associations/one_to_one.rb +61 -0
  27. data/lib/dm-core/associations/relationship.rb +229 -0
  28. data/lib/dm-core/associations/relationship_chain.rb +81 -0
  29. data/lib/dm-core/auto_migrations.rb +105 -0
  30. data/lib/dm-core/collection.rb +670 -0
  31. data/lib/dm-core/dependency_queue.rb +32 -0
  32. data/lib/dm-core/hook.rb +11 -0
  33. data/lib/dm-core/identity_map.rb +42 -0
  34. data/lib/dm-core/is.rb +16 -0
  35. data/lib/dm-core/logger.rb +232 -0
  36. data/lib/dm-core/migrations/destructive_migrations.rb +17 -0
  37. data/lib/dm-core/migrator.rb +29 -0
  38. data/lib/dm-core/model.rb +526 -0
  39. data/lib/dm-core/naming_conventions.rb +84 -0
  40. data/lib/dm-core/property.rb +676 -0
  41. data/lib/dm-core/property_set.rb +169 -0
  42. data/lib/dm-core/query.rb +676 -0
  43. data/lib/dm-core/repository.rb +167 -0
  44. data/lib/dm-core/resource.rb +671 -0
  45. data/lib/dm-core/scope.rb +58 -0
  46. data/lib/dm-core/support.rb +7 -0
  47. data/lib/dm-core/support/array.rb +13 -0
  48. data/lib/dm-core/support/assertions.rb +8 -0
  49. data/lib/dm-core/support/errors.rb +23 -0
  50. data/lib/dm-core/support/kernel.rb +11 -0
  51. data/lib/dm-core/support/symbol.rb +41 -0
  52. data/lib/dm-core/transaction.rb +267 -0
  53. data/lib/dm-core/type.rb +160 -0
  54. data/lib/dm-core/type_map.rb +80 -0
  55. data/lib/dm-core/types.rb +19 -0
  56. data/lib/dm-core/types/boolean.rb +7 -0
  57. data/lib/dm-core/types/discriminator.rb +34 -0
  58. data/lib/dm-core/types/object.rb +24 -0
  59. data/lib/dm-core/types/paranoid_boolean.rb +34 -0
  60. data/lib/dm-core/types/paranoid_datetime.rb +33 -0
  61. data/lib/dm-core/types/serial.rb +9 -0
  62. data/lib/dm-core/types/text.rb +10 -0
  63. data/lib/dm-core/version.rb +3 -0
  64. data/script/all +4 -0
  65. data/script/performance.rb +282 -0
  66. data/script/profile.rb +87 -0
  67. data/spec/integration/association_spec.rb +1382 -0
  68. data/spec/integration/association_through_spec.rb +203 -0
  69. data/spec/integration/associations/many_to_many_spec.rb +449 -0
  70. data/spec/integration/associations/many_to_one_spec.rb +163 -0
  71. data/spec/integration/associations/one_to_many_spec.rb +188 -0
  72. data/spec/integration/auto_migrations_spec.rb +413 -0
  73. data/spec/integration/collection_spec.rb +1073 -0
  74. data/spec/integration/data_objects_adapter_spec.rb +32 -0
  75. data/spec/integration/dependency_queue_spec.rb +46 -0
  76. data/spec/integration/model_spec.rb +197 -0
  77. data/spec/integration/mysql_adapter_spec.rb +85 -0
  78. data/spec/integration/postgres_adapter_spec.rb +731 -0
  79. data/spec/integration/property_spec.rb +253 -0
  80. data/spec/integration/query_spec.rb +514 -0
  81. data/spec/integration/repository_spec.rb +61 -0
  82. data/spec/integration/resource_spec.rb +513 -0
  83. data/spec/integration/sqlite3_adapter_spec.rb +352 -0
  84. data/spec/integration/sti_spec.rb +273 -0
  85. data/spec/integration/strategic_eager_loading_spec.rb +156 -0
  86. data/spec/integration/transaction_spec.rb +75 -0
  87. data/spec/integration/type_spec.rb +275 -0
  88. data/spec/lib/logging_helper.rb +18 -0
  89. data/spec/lib/mock_adapter.rb +27 -0
  90. data/spec/lib/model_loader.rb +100 -0
  91. data/spec/lib/publicize_methods.rb +28 -0
  92. data/spec/models/content.rb +16 -0
  93. data/spec/models/vehicles.rb +34 -0
  94. data/spec/models/zoo.rb +48 -0
  95. data/spec/spec.opts +3 -0
  96. data/spec/spec_helper.rb +91 -0
  97. data/spec/unit/adapters/abstract_adapter_spec.rb +133 -0
  98. data/spec/unit/adapters/adapter_shared_spec.rb +15 -0
  99. data/spec/unit/adapters/data_objects_adapter_spec.rb +632 -0
  100. data/spec/unit/adapters/in_memory_adapter_spec.rb +98 -0
  101. data/spec/unit/adapters/postgres_adapter_spec.rb +133 -0
  102. data/spec/unit/associations/many_to_many_spec.rb +32 -0
  103. data/spec/unit/associations/many_to_one_spec.rb +159 -0
  104. data/spec/unit/associations/one_to_many_spec.rb +393 -0
  105. data/spec/unit/associations/one_to_one_spec.rb +7 -0
  106. data/spec/unit/associations/relationship_spec.rb +71 -0
  107. data/spec/unit/associations_spec.rb +242 -0
  108. data/spec/unit/auto_migrations_spec.rb +111 -0
  109. data/spec/unit/collection_spec.rb +182 -0
  110. data/spec/unit/data_mapper_spec.rb +35 -0
  111. data/spec/unit/identity_map_spec.rb +126 -0
  112. data/spec/unit/is_spec.rb +80 -0
  113. data/spec/unit/migrator_spec.rb +33 -0
  114. data/spec/unit/model_spec.rb +321 -0
  115. data/spec/unit/naming_conventions_spec.rb +36 -0
  116. data/spec/unit/property_set_spec.rb +90 -0
  117. data/spec/unit/property_spec.rb +753 -0
  118. data/spec/unit/query_spec.rb +571 -0
  119. data/spec/unit/repository_spec.rb +93 -0
  120. data/spec/unit/resource_spec.rb +649 -0
  121. data/spec/unit/scope_spec.rb +142 -0
  122. data/spec/unit/transaction_spec.rb +493 -0
  123. data/spec/unit/type_map_spec.rb +114 -0
  124. data/spec/unit/type_spec.rb +119 -0
  125. data/tasks/ci.rb +36 -0
  126. data/tasks/dm.rb +63 -0
  127. data/tasks/doc.rb +20 -0
  128. data/tasks/gemspec.rb +23 -0
  129. data/tasks/hoe.rb +46 -0
  130. data/tasks/install.rb +20 -0
  131. metadata +215 -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.map { |m| m.to_s }.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,273 @@
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 :content, Text
18
+ property :class_type, Discriminator
19
+ end
20
+
21
+ class ::Propaganda < Book
22
+ property :marxist, Boolean, :nullable => false, :default => false
23
+ end
24
+
25
+ class ::Fiction < Book
26
+ property :series, String
27
+ end
28
+
29
+ class ::ShortStory < Fiction
30
+ property :moral, String
31
+ end
32
+
33
+ class ::ScienceFiction < Fiction
34
+ property :aliens, Boolean
35
+ end
36
+
37
+ class ::SpaceWestern < ScienceFiction
38
+ property :cowboys, Boolean
39
+ end
40
+ end
41
+
42
+ describe "with the identity map" do
43
+ before :all do
44
+ Book.auto_migrate!(:sqlite3)
45
+ repository(:sqlite3) do
46
+ Propaganda.create(:title => "Something", :isbn => "129038")
47
+ end
48
+ end
49
+
50
+ it "should find the base model in the identity map" do
51
+ repository(:sqlite3) do
52
+ book = Book.first
53
+ book.object_id.should == Propaganda.first.object_id
54
+ end
55
+ end
56
+
57
+ it "should find the child model in the identity map" do
58
+ repository(:sqlite3) do
59
+ book = Propaganda.first
60
+ book.object_id.should == Book.first.object_id
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "with a parent class" do
66
+ before :all do
67
+ Book.auto_migrate!(:sqlite3).should be_true
68
+
69
+ @table_set = @adapter.query('PRAGMA table_info("books")').inject({}) do |ts,column|
70
+ default = if 'NULL' == column.dflt_value || column.dflt_value.nil?
71
+ nil
72
+ else
73
+ /^(['"]?)(.*)\1$/.match(column.dflt_value)[2]
74
+ end
75
+
76
+ property = @property_class.new(
77
+ column.name,
78
+ column.type.upcase,
79
+ column.notnull == 0,
80
+ default,
81
+ column.pk == 1 # in SQLite3 the serial key is also primary
82
+ )
83
+
84
+ ts.update(property.name => property)
85
+ end
86
+
87
+ @index_list = @adapter.query('PRAGMA index_list("books")')
88
+ end
89
+
90
+ it "should create the child class property columns" do
91
+ @table_set.keys.should include("series", "marxist")
92
+ end
93
+
94
+ it "should create all property columns of the child classes in the inheritance tree" do
95
+ @table_set.keys.should include("moral")
96
+ end
97
+ end
98
+
99
+ describe "with a child class" do
100
+ before :all do
101
+ Propaganda.auto_migrate!(:sqlite3).should be_true
102
+
103
+ @table_set = @adapter.query('PRAGMA table_info("books")').inject({}) do |ts,column|
104
+ default = if 'NULL' == column.dflt_value || column.dflt_value.nil?
105
+ nil
106
+ else
107
+ /^(['"]?)(.*)\1$/.match(column.dflt_value)[2]
108
+ end
109
+
110
+ property = @property_class.new(
111
+ column.name,
112
+ column.type.upcase,
113
+ column.notnull == 0,
114
+ default,
115
+ column.pk == 1 # in SQLite3 the serial key is also primary
116
+ )
117
+
118
+ ts.update(property.name => property)
119
+ end
120
+
121
+ @index_list = @adapter.query('PRAGMA index_list("books")')
122
+ end
123
+
124
+ it "should create the parent class' property columns" do
125
+ @table_set.keys.should include("id", "title", "isbn")
126
+ end
127
+ end
128
+
129
+ describe "with a child class with it's own child class" do
130
+ before :all do
131
+ Fiction.auto_migrate!(:sqlite3).should be_true
132
+
133
+ @table_set = @adapter.query('PRAGMA table_info("books")').inject({}) do |ts,column|
134
+ default = if 'NULL' == column.dflt_value || column.dflt_value.nil?
135
+ nil
136
+ else
137
+ /^(['"]?)(.*)\1$/.match(column.dflt_value)[2]
138
+ end
139
+
140
+ property = @property_class.new(
141
+ column.name,
142
+ column.type.upcase,
143
+ column.notnull == 0,
144
+ default,
145
+ column.pk == 1 # in SQLite3 the serial key is also primary
146
+ )
147
+
148
+ ts.update(property.name => property)
149
+ end
150
+
151
+ @index_list = @adapter.query('PRAGMA index_list("books")')
152
+ end
153
+
154
+ it "should create the parent class' property columns" do
155
+ @table_set.keys.should include("id", "title", "isbn")
156
+ end
157
+
158
+ it "should create the child class' property columns" do
159
+ @table_set.keys.should include("moral")
160
+ end
161
+ end
162
+
163
+ describe "with a nephew class" do
164
+ before :all do
165
+ ShortStory.auto_migrate!(:sqlite3).should be_true
166
+
167
+ @table_set = @adapter.query('PRAGMA table_info("books")').inject({}) do |ts,column|
168
+ default = if 'NULL' == column.dflt_value || column.dflt_value.nil?
169
+ nil
170
+ else
171
+ /^(['"]?)(.*)\1$/.match(column.dflt_value)[2]
172
+ end
173
+
174
+ property = @property_class.new(
175
+ column.name,
176
+ column.type.upcase,
177
+ column.notnull == 0,
178
+ default,
179
+ column.pk == 1 # in SQLite3 the serial key is also primary
180
+ )
181
+
182
+ ts.update(property.name => property)
183
+ end
184
+ @index_list = @adapter.query('PRAGMA index_list("books")')
185
+ end
186
+
187
+
188
+ it "should create the grandparent class' property columns" do
189
+ @table_set.keys.should include("id", "title", "isbn")
190
+ end
191
+
192
+ it "should create the uncle class' property columns" do
193
+ @table_set.keys.should include("marxist")
194
+ end
195
+ end
196
+
197
+ describe "with a great-grandchild class" do
198
+ it "should inherit its parent's properties" do
199
+ SpaceWestern.properties[:aliens].should_not be_nil
200
+ end
201
+ it "should inherit its grandparent's properties" do
202
+ SpaceWestern.properties[:series].should_not be_nil
203
+ end
204
+ it "should inherit its great-granparent's properties" do
205
+ SpaceWestern.properties[:title].should_not be_nil
206
+ end
207
+ end
208
+
209
+ describe "with a child class" do
210
+ before :all do
211
+ Book.auto_migrate!(:sqlite3)
212
+ repository(:sqlite3) do
213
+ ShortStory.create(
214
+ :title => "The Science of Happiness",
215
+ :isbn => "129038",
216
+ :moral => "Bullshit might get you to the top, but it won't keep you there.")
217
+ end
218
+ end
219
+
220
+ it "should be able to access the properties from the parent collection" do
221
+ repository(:sqlite3) do
222
+ Book.all.each do |book|
223
+ book.title.should_not be_nil
224
+ book.isbn.should_not be_nil
225
+ book.moral.should_not be_nil
226
+ end
227
+ end
228
+ end
229
+ end
230
+
231
+ describe "with lazy property" do
232
+ before :all do
233
+ Book.auto_migrate!(:sqlite3)
234
+ repository(:sqlite3) do
235
+ ShortStory.create(
236
+ :title => "The Science of Happiness",
237
+ :isbn => "129038",
238
+ :content => 'This doesn\'t feel like a normal academic conference. True, the three-day Positive Psychology Summit i',
239
+ :moral => "Bullshit might get you to the top, but it won't keep you there."
240
+ )
241
+
242
+ ShortStory.create(
243
+ :title => "Blank as white paper",
244
+ :isbn => "129039",
245
+ :content => '',
246
+ :moral => "none."
247
+ )
248
+ end
249
+ end
250
+
251
+ it "should be able to access the properties from the parent collection" do
252
+ repository(:sqlite3) do
253
+ books = Book.all
254
+
255
+ books.each do |book|
256
+ book.title.should_not be_nil
257
+ book.isbn.should_not be_nil
258
+ book.moral.should_not be_nil
259
+ end
260
+
261
+ books[0].content
262
+
263
+ books.each do |book|
264
+ book.title.should_not be_nil
265
+ book.isbn.should_not be_nil
266
+ book.moral.should_not be_nil
267
+ end
268
+
269
+ end
270
+ end
271
+ end
272
+ end
273
+ end