dm-core 0.10.1 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/.autotest +29 -0
  2. data/.document +5 -0
  3. data/.gitignore +27 -0
  4. data/LICENSE +20 -0
  5. data/{README.txt → README.rdoc} +14 -3
  6. data/Rakefile +23 -22
  7. data/VERSION +1 -0
  8. data/dm-core.gemspec +201 -10
  9. data/lib/dm-core.rb +32 -23
  10. data/lib/dm-core/adapters.rb +0 -1
  11. data/lib/dm-core/adapters/data_objects_adapter.rb +230 -151
  12. data/lib/dm-core/adapters/mysql_adapter.rb +7 -8
  13. data/lib/dm-core/adapters/oracle_adapter.rb +39 -59
  14. data/lib/dm-core/adapters/postgres_adapter.rb +0 -1
  15. data/lib/dm-core/adapters/sqlite3_adapter.rb +5 -0
  16. data/lib/dm-core/adapters/sqlserver_adapter.rb +114 -0
  17. data/lib/dm-core/adapters/yaml_adapter.rb +0 -5
  18. data/lib/dm-core/associations/many_to_many.rb +118 -56
  19. data/lib/dm-core/associations/many_to_one.rb +48 -21
  20. data/lib/dm-core/associations/one_to_many.rb +8 -30
  21. data/lib/dm-core/associations/one_to_one.rb +1 -5
  22. data/lib/dm-core/associations/relationship.rb +89 -97
  23. data/lib/dm-core/collection.rb +299 -184
  24. data/lib/dm-core/core_ext/enumerable.rb +28 -0
  25. data/lib/dm-core/core_ext/kernel.rb +0 -2
  26. data/lib/dm-core/migrations.rb +314 -170
  27. data/lib/dm-core/model.rb +97 -66
  28. data/lib/dm-core/model/descendant_set.rb +1 -1
  29. data/lib/dm-core/model/hook.rb +0 -3
  30. data/lib/dm-core/model/property.rb +7 -10
  31. data/lib/dm-core/model/relationship.rb +79 -26
  32. data/lib/dm-core/model/scope.rb +3 -4
  33. data/lib/dm-core/property.rb +152 -90
  34. data/lib/dm-core/property_set.rb +18 -37
  35. data/lib/dm-core/query.rb +452 -153
  36. data/lib/dm-core/query/conditions/comparison.rb +266 -173
  37. data/lib/dm-core/query/conditions/operation.rb +499 -57
  38. data/lib/dm-core/query/direction.rb +0 -3
  39. data/lib/dm-core/query/operator.rb +0 -4
  40. data/lib/dm-core/query/path.rb +10 -12
  41. data/lib/dm-core/query/sort.rb +4 -10
  42. data/lib/dm-core/repository.rb +10 -6
  43. data/lib/dm-core/resource.rb +343 -148
  44. data/lib/dm-core/spec/adapter_shared_spec.rb +17 -1
  45. data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +277 -17
  46. data/lib/dm-core/support/chainable.rb +0 -2
  47. data/lib/dm-core/support/equalizer.rb +27 -3
  48. data/lib/dm-core/transaction.rb +75 -75
  49. data/lib/dm-core/type.rb +19 -5
  50. data/lib/dm-core/types/discriminator.rb +4 -4
  51. data/lib/dm-core/types/object.rb +2 -7
  52. data/lib/dm-core/types/paranoid_boolean.rb +8 -2
  53. data/lib/dm-core/types/paranoid_datetime.rb +8 -2
  54. data/lib/dm-core/version.rb +1 -1
  55. data/script/performance.rb +7 -7
  56. data/script/profile.rb +6 -6
  57. data/spec/lib/collection_helpers.rb +2 -2
  58. data/spec/lib/pending_helpers.rb +22 -3
  59. data/spec/lib/rspec_immediate_feedback_formatter.rb +1 -0
  60. data/spec/public/associations/many_to_many_spec.rb +6 -4
  61. data/spec/public/associations/many_to_one_spec.rb +10 -1
  62. data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +39 -0
  63. data/spec/public/associations/one_to_many_spec.rb +4 -3
  64. data/spec/public/associations/one_to_one_spec.rb +19 -1
  65. data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +45 -0
  66. data/spec/public/collection_spec.rb +4 -3
  67. data/spec/public/migrations_spec.rb +144 -0
  68. data/spec/public/model/relationship_spec.rb +115 -55
  69. data/spec/public/model_spec.rb +13 -13
  70. data/spec/public/property/object_spec.rb +106 -0
  71. data/spec/public/property_spec.rb +18 -14
  72. data/spec/public/resource_spec.rb +10 -1
  73. data/spec/public/sel_spec.rb +16 -49
  74. data/spec/public/setup_spec.rb +1 -1
  75. data/spec/public/shared/association_collection_shared_spec.rb +6 -14
  76. data/spec/public/shared/collection_finder_shared_spec.rb +267 -0
  77. data/spec/public/shared/collection_shared_spec.rb +214 -217
  78. data/spec/public/shared/finder_shared_spec.rb +259 -365
  79. data/spec/public/shared/resource_shared_spec.rb +524 -248
  80. data/spec/public/transaction_spec.rb +27 -3
  81. data/spec/public/types/discriminator_spec.rb +1 -1
  82. data/spec/rcov.opts +6 -0
  83. data/spec/semipublic/adapters/sqlserver_adapter_spec.rb +17 -0
  84. data/spec/semipublic/associations/many_to_one_spec.rb +3 -20
  85. data/spec/semipublic/associations_spec.rb +2 -2
  86. data/spec/semipublic/collection_spec.rb +0 -32
  87. data/spec/semipublic/model_spec.rb +96 -0
  88. data/spec/semipublic/property_spec.rb +3 -3
  89. data/spec/semipublic/query/conditions/comparison_spec.rb +1719 -0
  90. data/spec/semipublic/query/conditions/operation_spec.rb +1292 -0
  91. data/spec/semipublic/query_spec.rb +1285 -144
  92. data/spec/semipublic/resource_spec.rb +0 -24
  93. data/spec/semipublic/shared/resource_shared_spec.rb +103 -38
  94. data/spec/spec.opts +1 -1
  95. data/spec/spec_helper.rb +15 -6
  96. data/tasks/ci.rake +1 -0
  97. data/tasks/metrics.rake +37 -0
  98. data/tasks/spec.rake +41 -0
  99. data/tasks/yard.rake +9 -0
  100. data/tasks/yardstick.rake +19 -0
  101. metadata +99 -29
  102. data/CONTRIBUTING +0 -51
  103. data/FAQ +0 -93
  104. data/History.txt +0 -27
  105. data/MIT-LICENSE +0 -22
  106. data/Manifest.txt +0 -121
  107. data/QUICKLINKS +0 -11
  108. data/SPECS +0 -35
  109. data/TODO +0 -1
  110. data/spec/semipublic/query/conditions_spec.rb +0 -528
  111. data/tasks/ci.rb +0 -24
  112. data/tasks/dm.rb +0 -58
  113. data/tasks/doc.rb +0 -17
  114. data/tasks/gemspec.rb +0 -23
  115. data/tasks/hoe.rb +0 -45
  116. data/tasks/install.rb +0 -18
@@ -0,0 +1,28 @@
1
+ module Enumerable
2
+ def empty?
3
+ each { return false }
4
+ true
5
+ end
6
+
7
+ def one?
8
+ return one? { |entry| entry } unless block_given?
9
+
10
+ matches = 0
11
+ each do |entry|
12
+ matches += 1 if yield(entry)
13
+ return false if matches > 1
14
+ end
15
+ matches == 1
16
+ end
17
+
18
+ def first
19
+ each { |entry| return entry }
20
+ nil
21
+ end
22
+
23
+ def size
24
+ size = 0
25
+ each { size += 1 }
26
+ size
27
+ end
28
+ end
@@ -3,8 +3,6 @@ module Kernel
3
3
 
4
4
  # Delegates to DataMapper.repository()
5
5
  #
6
- # TODO: document
7
- #
8
6
  # @api public
9
7
  def repository(*args, &block)
10
8
  DataMapper.repository(*args, &block)
@@ -22,7 +22,6 @@ module DataMapper
22
22
  auto_migrate_up!(repository_name)
23
23
  end
24
24
 
25
- # TODO: document
26
25
  # @api public
27
26
  def auto_upgrade!(repository_name = nil)
28
27
  repository_execute(:auto_upgrade!, repository_name)
@@ -30,19 +29,16 @@ module DataMapper
30
29
 
31
30
  private
32
31
 
33
- # TODO: document
34
32
  # @api private
35
33
  def auto_migrate_down!(repository_name)
36
34
  repository_execute(:auto_migrate_down!, repository_name)
37
35
  end
38
36
 
39
- # TODO: document
40
37
  # @api private
41
38
  def auto_migrate_up!(repository_name)
42
39
  repository_execute(:auto_migrate_up!, repository_name)
43
40
  end
44
41
 
45
- # TODO: document
46
42
  # @api private
47
43
  def repository_execute(method, repository_name)
48
44
  DataMapper::Model.descendants.each do |model|
@@ -52,7 +48,6 @@ module DataMapper
52
48
  end
53
49
 
54
50
  module DataObjectsAdapter
55
- # TODO: document
56
51
  # @api private
57
52
  def self.included(base)
58
53
  base.extend ClassMethods
@@ -82,7 +77,7 @@ module DataMapper
82
77
  AND "table_name" = ?
83
78
  SQL
84
79
 
85
- query(statement, schema_name, storage_name).first > 0
80
+ select(statement, schema_name, storage_name).first > 0
86
81
  end
87
82
 
88
83
  # Returns whether the field exists.
@@ -105,12 +100,12 @@ module DataMapper
105
100
  AND "column_name" = ?
106
101
  SQL
107
102
 
108
- query(statement, schema_name, storage_name, column_name).first > 0
103
+ select(statement, schema_name, storage_name, column_name).first > 0
109
104
  end
110
105
 
111
- # TODO: document
112
106
  # @api semipublic
113
107
  def upgrade_model_storage(model)
108
+ name = self.name
114
109
  properties = model.properties_with_subclasses(name)
115
110
 
116
111
  if success = create_model_storage(model)
@@ -133,20 +128,20 @@ module DataMapper
133
128
  end
134
129
  end
135
130
 
136
- # TODO: document
137
131
  # @api semipublic
138
132
  def create_model_storage(model)
133
+ name = self.name
139
134
  properties = model.properties_with_subclasses(name)
140
135
 
141
136
  return false if storage_exists?(model.storage_name(name))
142
137
  return false if properties.empty?
143
138
 
144
139
  with_connection do |connection|
145
- statement = create_table_statement(connection, model, properties)
146
- command = connection.create_command(statement)
147
- command.execute_non_query
140
+ statements = [ create_table_statement(connection, model, properties) ]
141
+ statements.concat(create_index_statements(model))
142
+ statements.concat(create_unique_index_statements(model))
148
143
 
149
- (create_index_statements(model) + create_unique_index_statements(model)).each do |statement|
144
+ statements.each do |statement|
150
145
  command = connection.create_command(statement)
151
146
  command.execute_non_query
152
147
  end
@@ -155,7 +150,6 @@ module DataMapper
155
150
  true
156
151
  end
157
152
 
158
- # TODO: document
159
153
  # @api semipublic
160
154
  def destroy_model_storage(model)
161
155
  return true unless supports_drop_table_if_exists? || storage_exists?(model.storage_name(name))
@@ -174,25 +168,21 @@ module DataMapper
174
168
  false
175
169
  end
176
170
 
177
- # TODO: document
178
171
  # @api private
179
172
  def supports_drop_table_if_exists?
180
173
  false
181
174
  end
182
175
 
183
- # TODO: document
184
176
  # @api private
185
177
  def schema_name
186
178
  raise NotImplementedError, "#{self.class}#schema_name not implemented"
187
179
  end
188
180
 
189
- # TODO: document
190
181
  # @api private
191
182
  def alter_table_add_column_statement(connection, table_name, schema_hash)
192
183
  "ALTER TABLE #{quote_name(table_name)} ADD COLUMN #{property_schema_statement(connection, schema_hash)}"
193
184
  end
194
185
 
195
- # TODO: document
196
186
  # @api private
197
187
  def create_table_statement(connection, model, properties)
198
188
  statement = <<-SQL.compress_lines
@@ -204,19 +194,19 @@ module DataMapper
204
194
  statement
205
195
  end
206
196
 
207
- # TODO: document
208
197
  # @api private
209
198
  def drop_table_statement(model)
199
+ table_name = quote_name(model.storage_name(name))
210
200
  if supports_drop_table_if_exists?
211
- "DROP TABLE IF EXISTS #{quote_name(model.storage_name(name))}"
201
+ "DROP TABLE IF EXISTS #{table_name}"
212
202
  else
213
- "DROP TABLE #{quote_name(model.storage_name(name))}"
203
+ "DROP TABLE #{table_name}"
214
204
  end
215
205
  end
216
206
 
217
- # TODO: document
218
207
  # @api private
219
208
  def create_index_statements(model)
209
+ name = self.name
220
210
  table_name = model.storage_name(name)
221
211
  model.properties(name).indexes.map do |index_name, fields|
222
212
  <<-SQL.compress_lines
@@ -226,9 +216,9 @@ module DataMapper
226
216
  end
227
217
  end
228
218
 
229
- # TODO: document
230
219
  # @api private
231
220
  def create_unique_index_statements(model)
221
+ name = self.name
232
222
  table_name = model.storage_name(name)
233
223
  model.properties(name).unique_indexes.map do |index_name, fields|
234
224
  <<-SQL.compress_lines
@@ -238,49 +228,59 @@ module DataMapper
238
228
  end
239
229
  end
240
230
 
241
- # TODO: document
242
231
  # @api private
243
232
  def property_schema_hash(property)
244
- schema = (self.class.type_map[property.type] || self.class.type_map[property.primitive]).merge(:name => property.field)
233
+ primitive = property.primitive
234
+ type = property.type
235
+ type_map = self.class.type_map
245
236
 
246
- if property.primitive == String && schema[:primitive] != 'TEXT' && schema[:primitive] != 'CLOB'
237
+ schema = (type_map[type] || type_map[primitive]).merge(:name => property.field)
238
+
239
+ schema_primitive = schema[:primitive]
240
+
241
+ if primitive == String && schema_primitive != 'TEXT' && schema_primitive != 'CLOB' && schema_primitive != 'NVARCHAR'
247
242
  schema[:length] = property.length
248
- elsif property.primitive == BigDecimal || property.primitive == Float
243
+ elsif primitive == BigDecimal || primitive == Float
249
244
  schema[:precision] = property.precision
250
245
  schema[:scale] = property.scale
251
246
  end
252
247
 
253
- schema[:nullable] = property.nullable?
254
- schema[:serial] = property.serial?
248
+ schema[:allow_nil] = property.allow_nil?
249
+ schema[:serial] = property.serial?
250
+
251
+ default = property.default
255
252
 
256
- if property.default.nil? || property.default.respond_to?(:call)
257
- # remove the default if the property is not nullable
258
- schema.delete(:default) unless property.nullable?
253
+ if default.nil? || default.respond_to?(:call)
254
+ # remove the default if the property does not allow nil
255
+ schema.delete(:default) unless schema[:allow_nil]
259
256
  else
260
- if property.type.respond_to?(:dump)
261
- schema[:default] = property.type.dump(property.default, property)
257
+ schema[:default] = if type.respond_to?(:dump)
258
+ type.dump(default, property)
262
259
  else
263
- schema[:default] = property.default
260
+ default
264
261
  end
265
262
  end
266
263
 
267
264
  schema
268
265
  end
269
266
 
270
- # TODO: document
271
267
  # @api private
272
268
  def property_schema_statement(connection, schema)
273
269
  statement = quote_name(schema[:name])
274
270
  statement << " #{schema[:primitive]}"
275
271
 
272
+ length = schema[:length]
273
+
276
274
  if schema[:precision] && schema[:scale]
277
275
  statement << "(#{[ :precision, :scale ].map { |key| connection.quote_value(schema[key]) }.join(', ')})"
278
- elsif schema[:length]
279
- statement << "(#{connection.quote_value(schema[:length])})"
276
+ elsif length == 'max'
277
+ statement << '(max)'
278
+ elsif length
279
+ statement << "(#{connection.quote_value(length)})"
280
280
  end
281
281
 
282
282
  statement << " DEFAULT #{connection.quote_value(schema[:default])}" if schema.key?(:default)
283
- statement << ' NOT NULL' unless schema[:nullable]
283
+ statement << ' NOT NULL' unless schema[:allow_nil]
284
284
  statement
285
285
  end
286
286
  end # module SQL
@@ -299,17 +299,16 @@ module DataMapper
299
299
  scale = Property::DEFAULT_SCALE_BIGDECIMAL
300
300
 
301
301
  @type_map ||= {
302
- Integer => { :primitive => 'INTEGER' },
303
- String => { :primitive => 'VARCHAR', :length => length },
304
- Class => { :primitive => 'VARCHAR', :length => length },
305
- BigDecimal => { :primitive => 'DECIMAL', :precision => precision, :scale => scale },
306
- Float => { :primitive => 'FLOAT', :precision => precision },
307
- DateTime => { :primitive => 'TIMESTAMP' },
308
- Date => { :primitive => 'DATE' },
309
- Time => { :primitive => 'TIMESTAMP' },
310
- TrueClass => { :primitive => 'BOOLEAN' },
311
- Types::Object => { :primitive => 'TEXT' },
312
- Types::Text => { :primitive => 'TEXT' },
302
+ Integer => { :primitive => 'INTEGER' },
303
+ String => { :primitive => 'VARCHAR', :length => length },
304
+ Class => { :primitive => 'VARCHAR', :length => length },
305
+ BigDecimal => { :primitive => 'DECIMAL', :precision => precision, :scale => scale },
306
+ Float => { :primitive => 'FLOAT', :precision => precision },
307
+ DateTime => { :primitive => 'TIMESTAMP' },
308
+ Date => { :primitive => 'DATE' },
309
+ Time => { :primitive => 'TIMESTAMP' },
310
+ TrueClass => { :primitive => 'BOOLEAN' },
311
+ Types::Text => { :primitive => 'TEXT' },
313
312
  }.freeze
314
313
  end
315
314
  end # module ClassMethods
@@ -320,41 +319,35 @@ module DataMapper
320
319
  DEFAULT_CHARACTER_SET = 'utf8'.freeze
321
320
  DEFAULT_COLLATION = 'utf8_unicode_ci'.freeze
322
321
 
323
- # TODO: document
324
322
  # @api private
325
323
  def self.included(base)
326
324
  base.extend ClassMethods
327
325
  end
328
326
 
329
- # TODO: document
330
327
  # @api semipublic
331
328
  def storage_exists?(storage_name)
332
- query('SHOW TABLES LIKE ?', storage_name).first == storage_name
329
+ select('SHOW TABLES LIKE ?', storage_name).first == storage_name
333
330
  end
334
331
 
335
- # TODO: document
336
332
  # @api semipublic
337
333
  def field_exists?(storage_name, field)
338
- result = query("SHOW COLUMNS FROM #{quote_name(storage_name)} LIKE ?", field).first
334
+ result = select("SHOW COLUMNS FROM #{quote_name(storage_name)} LIKE ?", field).first
339
335
  result ? result.field == field : false
340
336
  end
341
337
 
342
338
  module SQL #:nodoc:
343
339
  # private ## This cannot be private for current migrations
344
340
 
345
- # TODO: document
346
341
  # @api private
347
342
  def supports_serial?
348
343
  true
349
344
  end
350
345
 
351
- # TODO: document
352
346
  # @api private
353
347
  def supports_drop_table_if_exists?
354
348
  true
355
349
  end
356
350
 
357
- # TODO: document
358
351
  # @api private
359
352
  def schema_name
360
353
  # TODO: is there a cleaner way to find out the current DB we are connected to?
@@ -364,13 +357,11 @@ module DataMapper
364
357
  # TODO: update dkubb/dm-more/dm-migrations to use schema_name and remove this
365
358
  alias db_name schema_name
366
359
 
367
- # TODO: document
368
360
  # @api private
369
361
  def create_table_statement(connection, model, properties)
370
362
  "#{super} ENGINE = #{DEFAULT_ENGINE} CHARACTER SET #{character_set} COLLATE #{collation}"
371
363
  end
372
364
 
373
- # TODO: document
374
365
  # @api private
375
366
  def property_schema_hash(property)
376
367
  schema = super
@@ -380,14 +371,16 @@ module DataMapper
380
371
  schema.delete(:default)
381
372
  end
382
373
 
383
- if property.primitive == Integer && property.min && property.max
384
- schema[:primitive] = integer_column_statement(property.min..property.max)
374
+ min = property.min
375
+ max = property.max
376
+
377
+ if property.primitive == Integer && min && max
378
+ schema[:primitive] = integer_column_statement(min..max)
385
379
  end
386
380
 
387
381
  schema
388
382
  end
389
383
 
390
- # TODO: document
391
384
  # @api private
392
385
  def property_schema_statement(connection, schema)
393
386
  statement = super
@@ -399,22 +392,19 @@ module DataMapper
399
392
  statement
400
393
  end
401
394
 
402
- # TODO: document
403
395
  # @api private
404
396
  def character_set
405
397
  @character_set ||= show_variable('character_set_connection') || DEFAULT_CHARACTER_SET
406
398
  end
407
399
 
408
- # TODO: document
409
400
  # @api private
410
401
  def collation
411
402
  @collation ||= show_variable('collation_connection') || DEFAULT_COLLATION
412
403
  end
413
404
 
414
- # TODO: document
415
405
  # @api private
416
406
  def show_variable(name)
417
- result = query('SHOW VARIABLES LIKE ?', name).first
407
+ result = select('SHOW VARIABLES LIKE ?', name).first
418
408
  result ? result.value.freeze : nil
419
409
  end
420
410
 
@@ -493,11 +483,17 @@ module DataMapper
493
483
  min = range.first
494
484
  max = range.last
495
485
 
496
- if min >= -2**7 && max < 2**7 then 'TINYINT'
497
- elsif min >= -2**15 && max < 2**15 then 'SMALLINT'
498
- elsif min >= -2**23 && max < 2**23 then 'MEDIUMINT'
499
- elsif min >= -2**31 && max < 2**31 then 'INT'
500
- elsif min >= -2**63 && max < 2**63 then 'BIGINT'
486
+ tinyint = 2**7
487
+ smallint = 2**15
488
+ integer = 2**31
489
+ mediumint = 2**23
490
+ bigint = 2**63
491
+
492
+ if min >= -tinyint && max < tinyint then 'TINYINT'
493
+ elsif min >= -smallint && max < smallint then 'SMALLINT'
494
+ elsif min >= -mediumint && max < mediumint then 'MEDIUMINT'
495
+ elsif min >= -integer && max < integer then 'INT'
496
+ elsif min >= -bigint && max < bigint then 'BIGINT'
501
497
  else
502
498
  raise ArgumentError, "min #{min} and max #{max} exceeds supported range"
503
499
  end
@@ -574,25 +570,21 @@ module DataMapper
574
570
  end # module MysqlAdapter
575
571
 
576
572
  module PostgresAdapter
577
- # TODO: document
578
573
  # @api private
579
574
  def self.included(base)
580
575
  base.extend ClassMethods
581
576
  end
582
577
 
583
- # TODO: document
584
578
  # @api semipublic
585
579
  def upgrade_model_storage(model)
586
580
  without_notices { super }
587
581
  end
588
582
 
589
- # TODO: document
590
583
  # @api semipublic
591
584
  def create_model_storage(model)
592
585
  without_notices { super }
593
586
  end
594
587
 
595
- # TODO: document
596
588
  # @api semipublic
597
589
  def destroy_model_storage(model)
598
590
  if supports_drop_table_if_exists?
@@ -605,25 +597,21 @@ module DataMapper
605
597
  module SQL #:nodoc:
606
598
  # private ## This cannot be private for current migrations
607
599
 
608
- # TODO: document
609
600
  # @api private
610
601
  def supports_drop_table_if_exists?
611
602
  @supports_drop_table_if_exists ||= postgres_version >= '8.2'
612
603
  end
613
604
 
614
- # TODO: document
615
605
  # @api private
616
606
  def schema_name
617
- @schema_name ||= query('SELECT current_schema()').first.freeze
607
+ @schema_name ||= select('SELECT current_schema()').first.freeze
618
608
  end
619
609
 
620
- # TODO: document
621
610
  # @api private
622
611
  def postgres_version
623
- @postgres_version ||= query('SELECT version()').first.split[1].freeze
612
+ @postgres_version ||= select('SELECT version()').first.split[1].freeze
624
613
  end
625
614
 
626
- # TODO: document
627
615
  # @api private
628
616
  def without_notices
629
617
  # execute the block with NOTICE messages disabled
@@ -635,23 +623,27 @@ module DataMapper
635
623
  end
636
624
  end
637
625
 
638
- # TODO: document
639
626
  # @api private
640
627
  def property_schema_hash(property)
641
628
  schema = super
642
629
 
630
+ primitive = property.primitive
631
+
643
632
  # Postgres does not support precision and scale for Float
644
- if property.primitive == Float
633
+ if primitive == Float
645
634
  schema.delete(:precision)
646
635
  schema.delete(:scale)
647
636
  end
648
637
 
649
- if property.primitive == Integer && property.min && property.max
650
- schema[:primitive] = integer_column_statement(property.min..property.max)
638
+ min = property.min
639
+ max = property.max
640
+
641
+ if primitive == Integer && min && max
642
+ schema[:primitive] = integer_column_statement(min..max)
651
643
  end
652
644
 
653
645
  if schema[:serial]
654
- schema[:primitive] = serial_column_statement(property.min..property.max)
646
+ schema[:primitive] = serial_column_statement(min..max)
655
647
  end
656
648
 
657
649
  schema
@@ -672,9 +664,13 @@ module DataMapper
672
664
  min = range.first
673
665
  max = range.last
674
666
 
675
- if min >= -2**15 && max < 2**15 then 'SMALLINT'
676
- elsif min >= -2**31 && max < 2**31 then 'INTEGER'
677
- elsif min >= -2**63 && max < 2**63 then 'BIGINT'
667
+ smallint = 2**15
668
+ integer = 2**31
669
+ bigint = 2**63
670
+
671
+ if min >= -smallint && max < smallint then 'SMALLINT'
672
+ elsif min >= -integer && max < integer then 'INTEGER'
673
+ elsif min >= -bigint && max < bigint then 'BIGINT'
678
674
  else
679
675
  raise ArgumentError, "min #{min} and max #{max} exceeds supported range"
680
676
  end
@@ -713,30 +709,27 @@ module DataMapper
713
709
  scale = Property::DEFAULT_SCALE_BIGDECIMAL
714
710
 
715
711
  @type_map ||= super.merge(
716
- BigDecimal => { :primitive => 'NUMERIC', :precision => precision, :scale => scale },
717
- Float => { :primitive => 'DOUBLE PRECISION' }
712
+ BigDecimal => { :primitive => 'NUMERIC', :precision => precision, :scale => scale },
713
+ Float => { :primitive => 'DOUBLE PRECISION' }
718
714
  ).freeze
719
715
  end
720
716
  end # module ClassMethods
721
717
  end # module PostgresAdapter
722
718
 
723
719
  module Sqlite3Adapter
724
- # TODO: document
725
720
  # @api private
726
721
  def self.included(base)
727
722
  base.extend ClassMethods
728
723
  end
729
724
 
730
- # TODO: document
731
725
  # @api semipublic
732
726
  def storage_exists?(storage_name)
733
- query_table(storage_name).size > 0
727
+ table_info(storage_name).any?
734
728
  end
735
729
 
736
- # TODO: document
737
730
  # @api semipublic
738
731
  def field_exists?(storage_name, column_name)
739
- query_table(storage_name).any? do |row|
732
+ table_info(storage_name).any? do |row|
740
733
  row.name == column_name
741
734
  end
742
735
  end
@@ -744,25 +737,21 @@ module DataMapper
744
737
  module SQL #:nodoc:
745
738
  # private ## This cannot be private for current migrations
746
739
 
747
- # TODO: document
748
740
  # @api private
749
741
  def supports_serial?
750
742
  @supports_serial ||= sqlite_version >= '3.1.0'
751
743
  end
752
744
 
753
- # TODO: document
754
745
  # @api private
755
746
  def supports_drop_table_if_exists?
756
747
  @supports_drop_table_if_exists ||= sqlite_version >= '3.3.0'
757
748
  end
758
749
 
759
- # TODO: document
760
750
  # @api private
761
- def query_table(table_name)
762
- query("PRAGMA table_info(#{quote_name(table_name)})")
751
+ def table_info(table_name)
752
+ select("PRAGMA table_info(#{quote_name(table_name)})")
763
753
  end
764
754
 
765
- # TODO: document
766
755
  # @api private
767
756
  def create_table_statement(connection, model, properties)
768
757
  statement = <<-SQL.compress_lines
@@ -781,7 +770,6 @@ module DataMapper
781
770
  statement
782
771
  end
783
772
 
784
- # TODO: document
785
773
  # @api private
786
774
  def property_schema_statement(connection, schema)
787
775
  statement = super
@@ -793,10 +781,9 @@ module DataMapper
793
781
  statement
794
782
  end
795
783
 
796
- # TODO: document
797
784
  # @api private
798
785
  def sqlite_version
799
- @sqlite_version ||= query('SELECT sqlite_version(*)').first.freeze
786
+ @sqlite_version ||= select('SELECT sqlite_version(*)').first.freeze
800
787
  end
801
788
  end # module SQL
802
789
 
@@ -815,13 +802,11 @@ module DataMapper
815
802
  end # module Sqlite3Adapter
816
803
 
817
804
  module OracleAdapter
818
- # TODO: document
819
805
  # @api private
820
806
  def self.included(base)
821
807
  base.extend ClassMethods
822
808
  end
823
809
 
824
- # TODO: document
825
810
  # @api semipublic
826
811
  def storage_exists?(storage_name)
827
812
  statement = <<-SQL.compress_lines
@@ -831,10 +816,9 @@ module DataMapper
831
816
  AND table_name = ?
832
817
  SQL
833
818
 
834
- query(statement, schema_name, oracle_upcase(storage_name)).first > 0
819
+ select(statement, schema_name, oracle_upcase(storage_name)).first > 0
835
820
  end
836
821
 
837
- # TODO: document
838
822
  # @api semipublic
839
823
  def sequence_exists?(sequence_name)
840
824
  return false unless sequence_name
@@ -845,10 +829,9 @@ module DataMapper
845
829
  AND sequence_name = ?
846
830
  SQL
847
831
 
848
- query(statement, schema_name, oracle_upcase(sequence_name)).first > 0
832
+ select(statement, schema_name, oracle_upcase(sequence_name)).first > 0
849
833
  end
850
834
 
851
- # TODO: document
852
835
  # @api semipublic
853
836
  def field_exists?(storage_name, field_name)
854
837
  statement = <<-SQL.compress_lines
@@ -859,10 +842,9 @@ module DataMapper
859
842
  AND column_name = ?
860
843
  SQL
861
844
 
862
- query(statement, schema_name, oracle_upcase(storage_name), oracle_upcase(field_name)).first > 0
845
+ select(statement, schema_name, oracle_upcase(storage_name), oracle_upcase(field_name)).first > 0
863
846
  end
864
847
 
865
- # TODO: document
866
848
  # @api semipublic
867
849
  def storage_fields(storage_name)
868
850
  statement = <<-SQL.compress_lines
@@ -872,12 +854,12 @@ module DataMapper
872
854
  AND table_name = ?
873
855
  SQL
874
856
 
875
- query(statement, schema_name, oracle_upcase(storage_name))
857
+ select(statement, schema_name, oracle_upcase(storage_name))
876
858
  end
877
859
 
878
- # TODO: document
879
860
  # @api semipublic
880
861
  def create_model_storage(model)
862
+ name = self.name
881
863
  properties = model.properties_with_subclasses(name)
882
864
  table_name = model.storage_name(name)
883
865
  truncate_or_delete = self.class.auto_migrate_with
@@ -897,17 +879,12 @@ module DataMapper
897
879
  destroy_model_storage(model, true)
898
880
  end
899
881
 
900
- statement = create_table_statement(connection, model, properties)
901
- command = connection.create_command(statement)
902
- command.execute_non_query
882
+ statements = [ create_table_statement(connection, model, properties) ]
883
+ statements.concat(create_index_statements(model))
884
+ statements.concat(create_unique_index_statements(model))
885
+ statements.concat(create_sequence_statements(model))
903
886
 
904
- (create_index_statements(model) + create_unique_index_statements(model)).each do |statement|
905
- command = connection.create_command(statement)
906
- command.execute_non_query
907
- end
908
-
909
- # added creation of sequence
910
- create_sequence_statements(model).each do |statement|
887
+ statements.each do |statement|
911
888
  command = connection.create_command(statement)
912
889
  command.execute_non_query
913
890
  end
@@ -918,22 +895,21 @@ module DataMapper
918
895
  true
919
896
  end
920
897
 
921
- # TODO: document
922
898
  # @api semipublic
923
899
  def destroy_model_storage(model, forced = false)
924
900
  table_name = model.storage_name(name)
925
- truncate_or_delete = self.class.auto_migrate_with
901
+ klass = self.class
902
+ truncate_or_delete = klass.auto_migrate_with
926
903
  if storage_exists?(table_name)
927
904
  if truncate_or_delete && !forced
928
- statement = case truncate_or_delete
905
+ case truncate_or_delete
929
906
  when :truncate
930
- truncate_table_statement(model)
907
+ execute(truncate_table_statement(model))
931
908
  when :delete
932
- delete_table_statement(model)
909
+ execute(delete_table_statement(model))
933
910
  else
934
911
  raise ArgumentError, "Unsupported auto_migrate_with option"
935
912
  end
936
- execute(statement)
937
913
  @truncated_tables ||= {}
938
914
  @truncated_tables[table_name] = true
939
915
  else
@@ -942,14 +918,14 @@ module DataMapper
942
918
  end
943
919
  end
944
920
  # added destroy of sequences
945
- reset_sequences = self.class.auto_migrate_reset_sequences
921
+ reset_sequences = klass.auto_migrate_reset_sequences
946
922
  table_is_truncated = @truncated_tables && @truncated_tables[table_name]
947
923
  unless truncate_or_delete && !reset_sequences && !forced
948
924
  if sequence_exists?(model_sequence_name(model))
949
- if table_is_truncated && !forced
950
- statement = reset_sequence_statement(model)
925
+ statement = if table_is_truncated && !forced
926
+ reset_sequence_statement(model)
951
927
  else
952
- statement = drop_sequence_statement(model)
928
+ drop_sequence_statement(model)
953
929
  end
954
930
  execute(statement) if statement
955
931
  end
@@ -960,7 +936,7 @@ module DataMapper
960
936
  private
961
937
 
962
938
  def storage_has_all_fields?(table_name, properties)
963
- properties.map{|p| oracle_upcase(p.field)}.sort == storage_fields(table_name).sort
939
+ properties.map { |property| oracle_upcase(property.field) }.sort == storage_fields(table_name).sort
964
940
  end
965
941
 
966
942
  # If table or column name contains just lowercase characters then do uppercase
@@ -972,22 +948,24 @@ module DataMapper
972
948
  module SQL #:nodoc:
973
949
  # private ## This cannot be private for current migrations
974
950
 
975
- # TODO: document
976
951
  # @api private
977
952
  def schema_name
978
- @schema_name ||= query("SELECT SYS_CONTEXT('userenv','current_schema') FROM dual").first.freeze
953
+ @schema_name ||= select("SELECT SYS_CONTEXT('userenv','current_schema') FROM dual").first.freeze
979
954
  end
980
955
 
981
- # TODO: document
982
956
  # @api private
983
957
  def create_sequence_statements(model)
958
+ name = self.name
984
959
  table_name = model.storage_name(name)
985
- serial = model.serial(name)
960
+ serial = model.serial(name)
986
961
 
987
962
  statements = []
988
963
  if sequence_name = model_sequence_name(model)
964
+ sequence_name = quote_name(sequence_name)
965
+ column_name = quote_name(serial.field)
966
+
989
967
  statements << <<-SQL.compress_lines
990
- CREATE SEQUENCE #{quote_name(sequence_name)} NOCACHE
968
+ CREATE SEQUENCE #{sequence_name} NOCACHE
991
969
  SQL
992
970
 
993
971
  # create trigger only if custom sequence name was not specified
@@ -997,8 +975,8 @@ module DataMapper
997
975
  BEFORE INSERT ON #{quote_name(table_name)} FOR EACH ROW
998
976
  BEGIN
999
977
  IF inserting THEN
1000
- IF :new.#{quote_name(serial.field)} IS NULL THEN
1001
- SELECT #{quote_name(sequence_name)}.NEXTVAL INTO :new.#{quote_name(serial.field)} FROM dual;
978
+ IF :new.#{column_name} IS NULL THEN
979
+ SELECT #{sequence_name}.NEXTVAL INTO :new.#{column_name} FROM dual;
1002
980
  END IF;
1003
981
  END IF;
1004
982
  END;
@@ -1009,7 +987,6 @@ module DataMapper
1009
987
  statements
1010
988
  end
1011
989
 
1012
- # TODO: document
1013
990
  # @api private
1014
991
  def drop_sequence_statement(model)
1015
992
  if sequence_name = model_sequence_name(model)
@@ -1019,32 +996,31 @@ module DataMapper
1019
996
  end
1020
997
  end
1021
998
 
1022
- # TODO: document
1023
999
  # @api private
1024
1000
  def reset_sequence_statement(model)
1025
1001
  if sequence_name = model_sequence_name(model)
1002
+ sequence_name = quote_name(sequence_name)
1026
1003
  <<-SQL.compress_lines
1027
1004
  DECLARE
1028
1005
  cval INTEGER;
1029
1006
  BEGIN
1030
- SELECT #{quote_name(sequence_name)}.NEXTVAL INTO cval FROM dual;
1031
- EXECUTE IMMEDIATE 'ALTER SEQUENCE #{quote_name(sequence_name)} INCREMENT BY -' || cval || ' MINVALUE 0';
1032
- SELECT #{quote_name(sequence_name)}.NEXTVAL INTO cval FROM dual;
1033
- EXECUTE IMMEDIATE 'ALTER SEQUENCE #{quote_name(sequence_name)} INCREMENT BY 1';
1007
+ SELECT #{sequence_name}.NEXTVAL INTO cval FROM dual;
1008
+ EXECUTE IMMEDIATE 'ALTER SEQUENCE #{sequence_name} INCREMENT BY -' || cval || ' MINVALUE 0';
1009
+ SELECT #{sequence_name}.NEXTVAL INTO cval FROM dual;
1010
+ EXECUTE IMMEDIATE 'ALTER SEQUENCE #{sequence_name} INCREMENT BY 1';
1034
1011
  END;
1035
1012
  SQL
1036
1013
  else
1037
1014
  nil
1038
1015
  end
1016
+
1039
1017
  end
1040
1018
 
1041
- # TODO: document
1042
1019
  # @api private
1043
1020
  def truncate_table_statement(model)
1044
1021
  "TRUNCATE TABLE #{quote_name(model.storage_name(name))}"
1045
1022
  end
1046
1023
 
1047
- # TODO: document
1048
1024
  # @api private
1049
1025
  def delete_table_statement(model)
1050
1026
  "DELETE FROM #{quote_name(model.storage_name(name))}"
@@ -1053,8 +1029,9 @@ module DataMapper
1053
1029
  private
1054
1030
 
1055
1031
  def model_sequence_name(model)
1032
+ name = self.name
1056
1033
  table_name = model.storage_name(name)
1057
- serial = model.serial(name)
1034
+ serial = model.serial(name)
1058
1035
 
1059
1036
  if serial
1060
1037
  serial.options[:sequence] || default_sequence_name(table_name)
@@ -1089,17 +1066,16 @@ module DataMapper
1089
1066
  scale = Property::DEFAULT_SCALE_BIGDECIMAL
1090
1067
 
1091
1068
  @type_map ||= {
1092
- Integer => { :primitive => 'NUMBER', :precision => precision, :scale => 0 },
1093
- String => { :primitive => 'VARCHAR2', :length => length },
1094
- Class => { :primitive => 'VARCHAR2', :length => length },
1095
- BigDecimal => { :primitive => 'NUMBER', :precision => precision, :scale => nil },
1096
- Float => { :primitive => 'BINARY_FLOAT', },
1097
- DateTime => { :primitive => 'DATE' },
1098
- Date => { :primitive => 'DATE' },
1099
- Time => { :primitive => 'DATE' },
1100
- TrueClass => { :primitive => 'NUMBER', :precision => 1, :scale => 0 },
1101
- Types::Object => { :primitive => 'CLOB' },
1102
- Types::Text => { :primitive => 'CLOB' },
1069
+ Integer => { :primitive => 'NUMBER', :precision => precision, :scale => 0 },
1070
+ String => { :primitive => 'VARCHAR2', :length => length },
1071
+ Class => { :primitive => 'VARCHAR2', :length => length },
1072
+ BigDecimal => { :primitive => 'NUMBER', :precision => precision, :scale => nil },
1073
+ Float => { :primitive => 'BINARY_FLOAT', },
1074
+ DateTime => { :primitive => 'DATE' },
1075
+ Date => { :primitive => 'DATE' },
1076
+ Time => { :primitive => 'DATE' },
1077
+ TrueClass => { :primitive => 'NUMBER', :precision => 1, :scale => 0 },
1078
+ Types::Text => { :primitive => 'CLOB' },
1103
1079
  }.freeze
1104
1080
  end
1105
1081
 
@@ -1135,6 +1111,173 @@ module DataMapper
1135
1111
  end # module ClassMethods
1136
1112
  end # module PostgresAdapter
1137
1113
 
1114
+ module SqlserverAdapter
1115
+ DEFAULT_CHARACTER_SET = 'utf8'.freeze
1116
+
1117
+ # @api private
1118
+ def self.included(base)
1119
+ base.extend ClassMethods
1120
+ end
1121
+
1122
+ # @api semipublic
1123
+ def storage_exists?(storage_name)
1124
+ select("SELECT name FROM sysobjects WHERE name LIKE ?", storage_name).first == storage_name
1125
+ end
1126
+
1127
+ # @api semipublic
1128
+ def field_exists?(storage_name, field_name)
1129
+ result = select("SELECT c.name FROM sysobjects as o JOIN syscolumns AS c ON o.id = c.id WHERE o.name = #{quote_name(storage_name)} AND c.name LIKE ?", field_name).first
1130
+ result ? result.field == field_name : false
1131
+ end
1132
+
1133
+ module SQL #:nodoc:
1134
+ # private ## This cannot be private for current migrations
1135
+
1136
+ # @api private
1137
+ def supports_serial?
1138
+ true
1139
+ end
1140
+
1141
+ # @api private
1142
+ def supports_drop_table_if_exists?
1143
+ false
1144
+ end
1145
+
1146
+ # @api private
1147
+ def schema_name
1148
+ # TODO: is there a cleaner way to find out the current DB we are connected to?
1149
+ @options[:path].split('/').last
1150
+ end
1151
+
1152
+ # TODO: update dkubb/dm-more/dm-migrations to use schema_name and remove this
1153
+
1154
+ alias db_name schema_name
1155
+
1156
+ # @api private
1157
+ def create_table_statement(connection, model, properties)
1158
+ statement = <<-SQL.compress_lines
1159
+ CREATE TABLE #{quote_name(model.storage_name(name))}
1160
+ (#{properties.map { |property| property_schema_statement(connection, property_schema_hash(property)) }.join(', ')}
1161
+ SQL
1162
+
1163
+ unless properties.any? { |property| property.serial? }
1164
+ statement << ", PRIMARY KEY(#{properties.key.map { |property| quote_name(property.field) }.join(', ')})"
1165
+ end
1166
+
1167
+ statement << ')'
1168
+ statement
1169
+ end
1170
+
1171
+ # @api private
1172
+ def property_schema_hash(property)
1173
+ schema = super
1174
+
1175
+ min = property.min
1176
+ max = property.max
1177
+
1178
+ if property.primitive == Integer && min && max
1179
+ schema[:primitive] = integer_column_statement(min..max)
1180
+ end
1181
+
1182
+ if schema[:primitive] == 'TEXT'
1183
+ schema.delete(:default)
1184
+ end
1185
+
1186
+ schema
1187
+ end
1188
+
1189
+ # @api private
1190
+ def property_schema_statement(connection, schema)
1191
+ if supports_serial? && schema[:serial]
1192
+ statement = quote_name(schema[:name])
1193
+ statement << " #{schema[:primitive]}"
1194
+
1195
+ length = schema[:length]
1196
+
1197
+ if schema[:precision] && schema[:scale]
1198
+ statement << "(#{[ :precision, :scale ].map { |key| connection.quote_value(schema[key]) }.join(', ')})"
1199
+ elsif length
1200
+ statement << "(#{connection.quote_value(length)})"
1201
+ end
1202
+
1203
+ statement << ' IDENTITY'
1204
+ else
1205
+ statement = super
1206
+ end
1207
+
1208
+ statement
1209
+ end
1210
+
1211
+ # @api private
1212
+ def character_set
1213
+ @character_set ||= show_variable('character_set_connection') || DEFAULT_CHARACTER_SET
1214
+ end
1215
+
1216
+ # @api private
1217
+ def collation
1218
+ @collation ||= show_variable('collation_connection') || DEFAULT_COLLATION
1219
+ end
1220
+
1221
+ # @api private
1222
+ def show_variable(name)
1223
+ raise "SqlserverAdapter#show_variable: Not implemented"
1224
+ end
1225
+
1226
+ private
1227
+
1228
+ # Return SQL statement for the integer column
1229
+ #
1230
+ # @param [Range] range
1231
+ # the min/max allowed integers
1232
+ #
1233
+ # @return [String]
1234
+ # the statement to create the integer column
1235
+ #
1236
+ # @api private
1237
+ def integer_column_statement(range)
1238
+ min = range.first
1239
+ max = range.last
1240
+
1241
+ smallint = 2**15
1242
+ integer = 2**31
1243
+ bigint = 2**63
1244
+
1245
+ if min >= 0 && max < 2**8 then 'TINYINT'
1246
+ elsif min >= -smallint && max < smallint then 'SMALLINT'
1247
+ elsif min >= -integer && max < integer then 'INT'
1248
+ elsif min >= -bigint && max < bigint then 'BIGINT'
1249
+ else
1250
+ raise ArgumentError, "min #{min} and max #{max} exceeds supported range"
1251
+ end
1252
+ end
1253
+
1254
+ end # module SQL
1255
+
1256
+ include SQL
1257
+
1258
+ module ClassMethods
1259
+ # Types for Sqlserver databases.
1260
+ #
1261
+ # @return [Hash] types for Sqlserver databases.
1262
+ #
1263
+ # @api private
1264
+ def type_map
1265
+ length = Property::DEFAULT_LENGTH
1266
+ precision = Property::DEFAULT_PRECISION
1267
+ scale = Property::DEFAULT_SCALE_BIGDECIMAL
1268
+
1269
+ @type_map ||= super.merge(
1270
+ DateTime => { :primitive => 'DATETIME' },
1271
+ Date => { :primitive => 'SMALLDATETIME' },
1272
+ Time => { :primitive => 'SMALLDATETIME' },
1273
+ TrueClass => { :primitive => 'BIT', },
1274
+ Types::Text => { :primitive => 'NVARCHAR', :length => 'max' }
1275
+ ).freeze
1276
+ end
1277
+ end # module ClassMethods
1278
+ end # module SqlserverAdapter
1279
+
1280
+
1138
1281
  module Repository
1139
1282
  # Determine whether a particular named storage exists in this repository
1140
1283
  #
@@ -1146,30 +1289,31 @@ module DataMapper
1146
1289
  #
1147
1290
  # @api semipublic
1148
1291
  def storage_exists?(storage_name)
1292
+ adapter = self.adapter
1149
1293
  if adapter.respond_to?(:storage_exists?)
1150
1294
  adapter.storage_exists?(storage_name)
1151
1295
  end
1152
1296
  end
1153
1297
 
1154
- # TODO: document
1155
1298
  # @api semipublic
1156
1299
  def upgrade_model_storage(model)
1300
+ adapter = self.adapter
1157
1301
  if adapter.respond_to?(:upgrade_model_storage)
1158
1302
  adapter.upgrade_model_storage(model)
1159
1303
  end
1160
1304
  end
1161
1305
 
1162
- # TODO: document
1163
1306
  # @api semipublic
1164
1307
  def create_model_storage(model)
1308
+ adapter = self.adapter
1165
1309
  if adapter.respond_to?(:create_model_storage)
1166
1310
  adapter.create_model_storage(model)
1167
1311
  end
1168
1312
  end
1169
1313
 
1170
- # TODO: document
1171
1314
  # @api semipublic
1172
1315
  def destroy_model_storage(model)
1316
+ adapter = self.adapter
1173
1317
  if adapter.respond_to?(:destroy_model_storage)
1174
1318
  adapter.destroy_model_storage(model)
1175
1319
  end
@@ -1194,13 +1338,11 @@ module DataMapper
1194
1338
  end # module Repository
1195
1339
 
1196
1340
  module Model
1197
- # TODO: document
1198
1341
  # @api private
1199
1342
  def self.included(mod)
1200
1343
  mod.descendants.each { |model| model.extend self }
1201
1344
  end
1202
1345
 
1203
- # TODO: document
1204
1346
  # @api semipublic
1205
1347
  def storage_exists?(repository_name = default_repository_name)
1206
1348
  repository(repository_name).storage_exists?(storage_name(repository_name))
@@ -1226,6 +1368,7 @@ module DataMapper
1226
1368
  # @api public
1227
1369
  def auto_upgrade!(repository_name = self.repository_name)
1228
1370
  assert_valid
1371
+ base_model = self.base_model
1229
1372
  if base_model == self
1230
1373
  repository(repository_name).upgrade_model_storage(self)
1231
1374
  else
@@ -1242,6 +1385,7 @@ module DataMapper
1242
1385
  # @api private
1243
1386
  def auto_migrate_down!(repository_name = self.repository_name)
1244
1387
  assert_valid
1388
+ base_model = self.base_model
1245
1389
  if base_model == self
1246
1390
  repository(repository_name).destroy_model_storage(self)
1247
1391
  else
@@ -1256,6 +1400,7 @@ module DataMapper
1256
1400
  # @api private
1257
1401
  def auto_migrate_up!(repository_name = self.repository_name)
1258
1402
  assert_valid
1403
+ base_model = self.base_model
1259
1404
  if base_model == self
1260
1405
  repository(repository_name).create_model_storage(self)
1261
1406
  else
@@ -1268,7 +1413,6 @@ module DataMapper
1268
1413
  module Adapters
1269
1414
  extendable do
1270
1415
 
1271
- # TODO: document
1272
1416
  # @api private
1273
1417
  def const_added(const_name)
1274
1418
  if DataMapper::Migrations.const_defined?(const_name)