sbf-dm-migrations 1.3.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +38 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +468 -0
  5. data/.travis.yml +52 -0
  6. data/Gemfile +61 -0
  7. data/LICENSE +20 -0
  8. data/README.rdoc +39 -0
  9. data/Rakefile +4 -0
  10. data/db/migrations/1_create_people_table.rb +12 -0
  11. data/db/migrations/2_add_dob_to_people.rb +13 -0
  12. data/db/migrations/config.rb +4 -0
  13. data/dm-migrations.gemspec +20 -0
  14. data/examples/Rakefile +149 -0
  15. data/examples/sample_migration.rb +58 -0
  16. data/examples/sample_migration_spec.rb +46 -0
  17. data/lib/dm-migrations/adapters/dm-do-adapter.rb +304 -0
  18. data/lib/dm-migrations/adapters/dm-mysql-adapter.rb +306 -0
  19. data/lib/dm-migrations/adapters/dm-oracle-adapter.rb +339 -0
  20. data/lib/dm-migrations/adapters/dm-postgres-adapter.rb +152 -0
  21. data/lib/dm-migrations/adapters/dm-sqlite-adapter.rb +88 -0
  22. data/lib/dm-migrations/adapters/dm-sqlserver-adapter.rb +184 -0
  23. data/lib/dm-migrations/adapters/dm-yaml-adapter.rb +21 -0
  24. data/lib/dm-migrations/auto_migration.rb +227 -0
  25. data/lib/dm-migrations/exceptions/duplicate_migration.rb +6 -0
  26. data/lib/dm-migrations/migration.rb +323 -0
  27. data/lib/dm-migrations/migration_runner.rb +76 -0
  28. data/lib/dm-migrations/sql/column.rb +5 -0
  29. data/lib/dm-migrations/sql/mysql.rb +84 -0
  30. data/lib/dm-migrations/sql/oracle.rb +9 -0
  31. data/lib/dm-migrations/sql/postgres.rb +89 -0
  32. data/lib/dm-migrations/sql/sqlite.rb +59 -0
  33. data/lib/dm-migrations/sql/sqlserver.rb +9 -0
  34. data/lib/dm-migrations/sql/table.rb +15 -0
  35. data/lib/dm-migrations/sql/table_creator.rb +105 -0
  36. data/lib/dm-migrations/sql/table_modifier.rb +57 -0
  37. data/lib/dm-migrations/sql.rb +7 -0
  38. data/lib/dm-migrations/version.rb +5 -0
  39. data/lib/dm-migrations.rb +3 -0
  40. data/lib/spec/example/migration_example_group.rb +69 -0
  41. data/lib/spec/matchers/migration_matchers.rb +96 -0
  42. data/spec/integration/auto_migration_spec.rb +590 -0
  43. data/spec/integration/auto_upgrade_spec.rb +41 -0
  44. data/spec/integration/migration_runner_spec.rb +84 -0
  45. data/spec/integration/migration_spec.rb +156 -0
  46. data/spec/integration/sql_spec.rb +290 -0
  47. data/spec/isolated/require_after_setup_spec.rb +24 -0
  48. data/spec/isolated/require_before_setup_spec.rb +24 -0
  49. data/spec/isolated/require_spec.rb +23 -0
  50. data/spec/spec_helper.rb +16 -0
  51. data/spec/unit/migration_spec.rb +501 -0
  52. data/spec/unit/sql/column_spec.rb +14 -0
  53. data/spec/unit/sql/postgres_spec.rb +90 -0
  54. data/spec/unit/sql/sqlite_extensions_spec.rb +103 -0
  55. data/spec/unit/sql/table_creator_spec.rb +91 -0
  56. data/spec/unit/sql/table_modifier_spec.rb +47 -0
  57. data/spec/unit/sql/table_spec.rb +26 -0
  58. data/spec/unit/sql_spec.rb +7 -0
  59. data/tasks/spec.rake +21 -0
  60. data/tasks/yard.rake +9 -0
  61. data/tasks/yardstick.rake +19 -0
  62. metadata +120 -0
@@ -0,0 +1,306 @@
1
+ require 'dm-migrations/auto_migration'
2
+ require 'dm-migrations/adapters/dm-do-adapter'
3
+
4
+ module DataMapper
5
+ module Migrations
6
+ module MysqlAdapter
7
+ DEFAULT_ENGINE = 'InnoDB'.freeze
8
+ DEFAULT_CHARACTER_SET = 'utf8'.freeze
9
+ DEFAULT_COLLATION = 'utf8_unicode_ci'.freeze
10
+ MAXIMUM_CHAR_LENGTH = ((2**16) - 1) / 4 # allow room for utf8mb4
11
+
12
+ include SQL, DataObjectsAdapter
13
+
14
+ # @api private
15
+ def self.included(base)
16
+ base.extend DataObjectsAdapter::ClassMethods
17
+ base.extend ClassMethods
18
+ end
19
+
20
+ # @api semipublic
21
+ def storage_exists?(storage_name)
22
+ select('SHOW TABLES LIKE ?', storage_name).first == storage_name
23
+ end
24
+
25
+ # @api semipublic
26
+ def field_exists?(storage_name, field)
27
+ result = select("SHOW COLUMNS FROM #{quote_name(storage_name)} LIKE ?", field).first
28
+ result ? result.field == field : false
29
+ end
30
+
31
+ module SQL # :nodoc:
32
+ # private ## This cannot be private for current migrations
33
+
34
+ # Allows for specification of the default storage engine to use when creating tables via
35
+ # migrations. Defaults to DEFAULT_ENGINE.
36
+ #
37
+ # adapter = DataMapper.setup(:default, 'mysql://localhost/foo')
38
+ # adapter.storage_engine = 'MyISAM'
39
+ #
40
+ # @api public
41
+ attr_accessor :storage_engine
42
+
43
+ # @api private
44
+ def supports_serial?
45
+ true
46
+ end
47
+
48
+ # @api private
49
+ def supports_drop_table_if_exists?
50
+ true
51
+ end
52
+
53
+ # @api private
54
+ def schema_name
55
+ # TODO: is there a cleaner way to find out the current DB we are connected to?
56
+ normalized_uri.path.split('/').last
57
+ end
58
+
59
+ # @api private
60
+ def create_table_statement(connection, model, properties)
61
+ "#{super} ENGINE = #{storage_engine} CHARACTER SET #{character_set} COLLATE #{collation}"
62
+ end
63
+
64
+ # @api private
65
+ def property_schema_hash(property)
66
+ schema = super
67
+
68
+ case property.dump_as
69
+ when Integer.singleton_class
70
+ if property.respond_to?(:min) && property.respond_to?(:max)
71
+ min = property.min
72
+ max = property.max
73
+ schema[:primitive] = integer_column_statement(min..max) if min && max
74
+ end
75
+ when String.singleton_class
76
+ if property.is_a?(Property::Text)
77
+ schema[:primitive] = text_column_statement(property.length)
78
+ schema.delete(:default)
79
+ else
80
+ schema[:length] ||= MAXIMUM_CHAR_LENGTH
81
+ end
82
+ end
83
+
84
+ schema
85
+ end
86
+
87
+ # @api private
88
+ def property_schema_statement(connection, schema)
89
+ statement = super
90
+
91
+ statement << ' AUTO_INCREMENT' if supports_serial? && schema[:serial]
92
+
93
+ statement
94
+ end
95
+
96
+ # @api private
97
+ def storage_engine
98
+ # Don't pull the default engine via show_variable for backwards compat where it was hard
99
+ # coded to InnoDB
100
+ @storage_engine ||= DEFAULT_ENGINE
101
+ end
102
+
103
+ # @api private
104
+ def character_set
105
+ @character_set ||= show_variable('character_set_connection') || DEFAULT_CHARACTER_SET
106
+ end
107
+
108
+ # @api private
109
+ def collation
110
+ @collation ||= show_variable('collation_connection') || DEFAULT_COLLATION
111
+ end
112
+
113
+ # @api private
114
+ def show_variable(name)
115
+ result = select('SHOW VARIABLES LIKE ?', name).first
116
+ result ? result.value.freeze : nil
117
+ end
118
+
119
+ # Return SQL statement for the text column
120
+ #
121
+ # @param [Integer] length
122
+ # the max allowed length
123
+ #
124
+ # @return [String]
125
+ # the statement to create the text column
126
+ #
127
+ # @api private
128
+ private def text_column_statement(length)
129
+ if length < 2**8
130
+ 'TINYTEXT'
131
+ elsif length < 2**16
132
+ 'TEXT'
133
+ elsif length < 2**24
134
+ 'MEDIUMTEXT'
135
+ elsif length < 2**32
136
+ 'LONGTEXT'
137
+
138
+ # http://www.postgresql.org/files/documentation/books/aw_pgsql/node90.html
139
+ # Implies that PostgreSQL doesn't have a size limit on text
140
+ # fields, so this param validation happens here instead of
141
+ # DM::Property#initialize.
142
+ else
143
+ raise ArgumentError, "length of #{length} exceeds maximum size supported"
144
+ end
145
+ end
146
+
147
+ # Return SQL statement for the integer column
148
+ #
149
+ # @param [Range] range
150
+ # the min/max allowed integers
151
+ #
152
+ # @return [String]
153
+ # the statement to create the integer column
154
+ #
155
+ # @api private
156
+ private def integer_column_statement(range)
157
+ format('%s(%d)%s', integer_column_type(range), integer_display_size(range), integer_statement_sign(range))
158
+ end
159
+
160
+ # Return the integer column type
161
+ #
162
+ # Use the smallest available column type that will satisfy the
163
+ # allowable range of numbers
164
+ #
165
+ # @param [Range] range
166
+ # the min/max allowed integers
167
+ #
168
+ # @return [String]
169
+ # the column type
170
+ #
171
+ # @api private
172
+ private def integer_column_type(range)
173
+ if range.first < 0
174
+ signed_integer_column_type(range)
175
+ else
176
+ unsigned_integer_column_type(range)
177
+ end
178
+ end
179
+
180
+ # Return the signed integer column type
181
+ #
182
+ # @param [Range] range
183
+ # the min/max allowed integers
184
+ #
185
+ # @return [String]
186
+ #
187
+ # @api private
188
+ private def signed_integer_column_type(range)
189
+ min = range.first
190
+ max = range.last
191
+
192
+ tinyint = 2**7
193
+ smallint = 2**15
194
+ integer = 2**31
195
+ mediumint = 2**23
196
+ bigint = 2**63
197
+
198
+ if min >= -tinyint && max < tinyint
199
+ 'TINYINT'
200
+ elsif min >= -smallint && max < smallint
201
+ 'SMALLINT'
202
+ elsif min >= -mediumint && max < mediumint
203
+ 'MEDIUMINT'
204
+ elsif min >= -integer && max < integer
205
+ 'INT'
206
+ elsif min >= -bigint && max < bigint
207
+ 'BIGINT'
208
+ else
209
+ raise ArgumentError, "min #{min} and max #{max} exceeds supported range"
210
+ end
211
+ end
212
+
213
+ # Return the unsigned integer column type
214
+ #
215
+ # @param [Range] range
216
+ # the min/max allowed integers
217
+ #
218
+ # @return [String]
219
+ #
220
+ # @api private
221
+ private def unsigned_integer_column_type(range)
222
+ max = range.last
223
+
224
+ if max < 2**8
225
+ 'TINYINT'
226
+ elsif max < 2**16
227
+ 'SMALLINT'
228
+ elsif max < 2**24
229
+ 'MEDIUMINT'
230
+ elsif max < 2**32
231
+ 'INT'
232
+ elsif max < 2**64
233
+ 'BIGINT'
234
+ else
235
+ raise ArgumentError, "min #{range.first} and max #{max} exceeds supported range"
236
+ end
237
+ end
238
+
239
+ # Return the integer column display size
240
+ #
241
+ # Adjust the display size to match the maximum number of
242
+ # expected digits. This is more for documentation purposes
243
+ # and does not affect what can actually be stored in a
244
+ # specific column
245
+ #
246
+ # @param [Range] range
247
+ # the min/max allowed integers
248
+ #
249
+ # @return [Integer]
250
+ # the display size for the integer
251
+ #
252
+ # @api private
253
+ private def integer_display_size(range)
254
+ [range.first.to_s.length, range.last.to_s.length].max
255
+ end
256
+
257
+ # Return the integer sign statement
258
+ #
259
+ # @param [Range] range
260
+ # the min/max allowed integers
261
+ #
262
+ # @return [String, nil]
263
+ # statement if unsigned, nil if signed
264
+ #
265
+ # @api private
266
+ private def integer_statement_sign(range)
267
+ ' UNSIGNED' unless range.first < 0
268
+ end
269
+
270
+ # @api private
271
+ private def indexes(model)
272
+ filter_indexes(model, super)
273
+ end
274
+
275
+ # @api private
276
+ private def unique_indexes(model)
277
+ filter_indexes(model, super)
278
+ end
279
+
280
+ # Filter out any indexes with a non index-able column in MySQL
281
+ #
282
+ # @api private
283
+ private def filter_indexes(model, indexes)
284
+ field_map = model.properties(name).field_map
285
+ indexes.select do |_index_name, fields|
286
+ fields.all? { |field| !field_map[field].is_a?(Property::Text) }
287
+ end
288
+ end
289
+ end
290
+
291
+ module ClassMethods
292
+ # Types for MySQL databases.
293
+ #
294
+ # @return [Hash] types for MySQL databases.
295
+ #
296
+ # @api private
297
+ def type_map
298
+ super.merge(
299
+ DateTime => {primitive: 'DATETIME'},
300
+ Time => {primitive: 'DATETIME'}
301
+ ).freeze
302
+ end
303
+ end
304
+ end
305
+ end
306
+ end
@@ -0,0 +1,339 @@
1
+ require 'dm-migrations/auto_migration'
2
+ require 'dm-migrations/adapters/dm-do-adapter'
3
+
4
+ module DataMapper
5
+ module Migrations
6
+ module OracleAdapter
7
+ include SQL, DataObjectsAdapter
8
+
9
+ # @api private
10
+ def self.included(base)
11
+ base.extend DataObjectsAdapter::ClassMethods
12
+ base.extend ClassMethods
13
+ end
14
+
15
+ # @api semipublic
16
+ def storage_exists?(storage_name)
17
+ statement = DataMapper::Ext::String.compress_lines(<<-SQL)
18
+ SELECT COUNT(*)
19
+ FROM all_tables
20
+ WHERE owner = ?
21
+ AND table_name = ?
22
+ SQL
23
+
24
+ select(statement, schema_name, oracle_upcase(storage_name)).first > 0
25
+ end
26
+
27
+ # @api semipublic
28
+ def sequence_exists?(sequence_name)
29
+ return false unless sequence_name
30
+
31
+ statement = DataMapper::Ext::String.compress_lines(<<-SQL)
32
+ SELECT COUNT(*)
33
+ FROM all_sequences
34
+ WHERE sequence_owner = ?
35
+ AND sequence_name = ?
36
+ SQL
37
+
38
+ select(statement, schema_name, oracle_upcase(sequence_name)).first > 0
39
+ end
40
+
41
+ # @api semipublic
42
+ def field_exists?(storage_name, field_name)
43
+ statement = DataMapper::Ext::String.compress_lines(<<-SQL)
44
+ SELECT COUNT(*)
45
+ FROM all_tab_columns
46
+ WHERE owner = ?
47
+ AND table_name = ?
48
+ AND column_name = ?
49
+ SQL
50
+
51
+ select(statement, schema_name, oracle_upcase(storage_name), oracle_upcase(field_name)).first > 0
52
+ end
53
+
54
+ # @api semipublic
55
+ def storage_fields(storage_name)
56
+ statement = DataMapper::Ext::String.compress_lines(<<-SQL)
57
+ SELECT column_name
58
+ FROM all_tab_columns
59
+ WHERE owner = ?
60
+ AND table_name = ?
61
+ SQL
62
+
63
+ select(statement, schema_name, oracle_upcase(storage_name))
64
+ end
65
+
66
+ def drop_table_statement(model)
67
+ table_name = quote_name(model.storage_name(name))
68
+ "DROP TABLE #{table_name} CASCADE CONSTRAINTS"
69
+ end
70
+
71
+ # @api semipublic
72
+ def create_model_storage(model)
73
+ name = self.name
74
+ properties = model.properties_with_subclasses(name)
75
+ table_name = model.storage_name(name)
76
+ truncate_or_delete = self.class.auto_migrate_with
77
+ table_is_truncated = truncate_or_delete && @truncated_tables && @truncated_tables[table_name]
78
+
79
+ return false if storage_exists?(table_name) && !table_is_truncated
80
+ return false if properties.empty?
81
+
82
+ with_connection do |connection|
83
+ # if table was truncated then check if all columns for properties are present
84
+ # TODO: check all other column definition options
85
+ if table_is_truncated && storage_has_all_fields?(table_name, properties)
86
+ @truncated_tables[table_name] = nil
87
+ else
88
+ # forced drop of table if properties are different
89
+ destroy_model_storage(model, true) if truncate_or_delete
90
+
91
+ statements = [create_table_statement(connection, model, properties)]
92
+ statements.concat(create_index_statements(model))
93
+ statements.concat(create_unique_index_statements(model))
94
+ statements.concat(create_sequence_statements(model))
95
+
96
+ statements.each do |statement|
97
+ command = connection.create_command(statement)
98
+ command.execute_non_query
99
+ end
100
+ end
101
+ end
102
+
103
+ true
104
+ end
105
+
106
+ # @api semipublic
107
+ def destroy_model_storage(model, forced = false)
108
+ table_name = model.storage_name(name)
109
+ klass = self.class
110
+ truncate_or_delete = klass.auto_migrate_with
111
+ if storage_exists?(table_name)
112
+ if truncate_or_delete && !forced
113
+ case truncate_or_delete
114
+ when :truncate
115
+ execute(truncate_table_statement(model))
116
+ when :delete
117
+ execute(delete_table_statement(model))
118
+ else
119
+ raise ArgumentError, 'Unsupported auto_migrate_with option'
120
+ end
121
+ @truncated_tables ||= {}
122
+ @truncated_tables[table_name] = true
123
+ else
124
+ execute(drop_table_statement(model))
125
+ @truncated_tables[table_name] = nil if @truncated_tables
126
+ end
127
+ end
128
+ # added destroy of sequences
129
+ reset_sequences = klass.auto_migrate_reset_sequences
130
+ table_is_truncated = @truncated_tables && @truncated_tables[table_name]
131
+ unless truncate_or_delete && !reset_sequences && !forced
132
+ if sequence_exists?(model_sequence_name(model))
133
+ statement = if table_is_truncated && !forced
134
+ reset_sequence_statement(model)
135
+ else
136
+ drop_sequence_statement(model)
137
+ end
138
+ execute(statement) if statement
139
+ end
140
+ end
141
+ true
142
+ end
143
+
144
+ private def storage_has_all_fields?(table_name, properties)
145
+ properties.map { |property| oracle_upcase(property.field) }.sort == storage_fields(table_name).sort
146
+ end
147
+
148
+ # If table or column name contains just lowercase characters then do uppercase
149
+ # as uppercase version will be used in Oracle data dictionary tables
150
+ private def oracle_upcase(name)
151
+ (name =~ /[A-Z]/) ? name : name.upcase
152
+ end
153
+
154
+ module SQL # :nodoc:
155
+ # private ## This cannot be private for current migrations
156
+
157
+ # @api private
158
+ def schema_name
159
+ @schema_name ||= select("SELECT SYS_CONTEXT('userenv','current_schema') FROM dual").first.freeze
160
+ end
161
+
162
+ # @api private
163
+ def property_schema_hash(property)
164
+ schema = super
165
+
166
+ if property.is_a?(Property::Binary)
167
+ # BLOB does not support length
168
+ schema.delete(:length)
169
+ end
170
+
171
+ if property.primitive == String && property.length > 4000
172
+ # Oracle doesn't support string over 4000 bytes
173
+ schema[:primitive] = 'NCLOB'
174
+ # CLOB and NCLOB do not support length
175
+ schema.delete(:length)
176
+ end
177
+
178
+ schema
179
+ end
180
+
181
+ # @api private
182
+ def create_sequence_statements(model)
183
+ name = self.name
184
+ table_name = model.storage_name(name)
185
+ serial = model.serial(name)
186
+
187
+ statements = []
188
+ if (sequence_name = model_sequence_name(model))
189
+ sequence_name = quote_name(sequence_name)
190
+ column_name = quote_name(serial.field)
191
+
192
+ statements << DataMapper::Ext::String.compress_lines(<<-SQL)
193
+ CREATE SEQUENCE #{sequence_name} NOCACHE
194
+ SQL
195
+
196
+ # create trigger only if custom sequence name was not specified
197
+ unless serial.options[:sequence]
198
+ statements << DataMapper::Ext::String.compress_lines(<<-SQL)
199
+ CREATE OR REPLACE TRIGGER #{quote_name(default_trigger_name(table_name))}
200
+ BEFORE INSERT ON #{quote_name(table_name)} FOR EACH ROW
201
+ BEGIN
202
+ IF inserting THEN
203
+ IF :new.#{column_name} IS NULL THEN
204
+ SELECT #{sequence_name}.NEXTVAL INTO :new.#{column_name} FROM dual;
205
+ END IF;
206
+ END IF;
207
+ END;
208
+ SQL
209
+ end
210
+ end
211
+
212
+ statements
213
+ end
214
+
215
+ # @api private
216
+ def drop_sequence_statement(model)
217
+ return unless (sequence_name = model_sequence_name(model))
218
+
219
+ "DROP SEQUENCE #{quote_name(sequence_name)}"
220
+ end
221
+
222
+ # @api private
223
+ def reset_sequence_statement(model)
224
+ return unless (sequence_name = model_sequence_name(model))
225
+
226
+ sequence_name = quote_name(sequence_name)
227
+ DataMapper::Ext::String.compress_lines(<<-SQL)
228
+ DECLARE
229
+ cval INTEGER;
230
+ BEGIN
231
+ SELECT #{sequence_name}.NEXTVAL INTO cval FROM dual;
232
+ EXECUTE IMMEDIATE 'ALTER SEQUENCE #{sequence_name} INCREMENT BY -' || cval || ' MINVALUE 0';
233
+ SELECT #{sequence_name}.NEXTVAL INTO cval FROM dual;
234
+ EXECUTE IMMEDIATE 'ALTER SEQUENCE #{sequence_name} INCREMENT BY 1';
235
+ END;
236
+ SQL
237
+ end
238
+
239
+ # @api private
240
+ def truncate_table_statement(model)
241
+ "TRUNCATE TABLE #{quote_name(model.storage_name(name))}"
242
+ end
243
+
244
+ # @api private
245
+ def delete_table_statement(model)
246
+ "DELETE FROM #{quote_name(model.storage_name(name))}"
247
+ end
248
+
249
+ private def model_sequence_name(model)
250
+ name = self.name
251
+ table_name = model.storage_name(name)
252
+ serial = model.serial(name)
253
+
254
+ return unless serial
255
+
256
+ serial.options[:sequence] || default_sequence_name(table_name)
257
+ end
258
+
259
+ private def default_sequence_name(table_name)
260
+ # truncate table name if necessary to fit in max length of identifier
261
+ "#{table_name[0, self.class::IDENTIFIER_MAX_LENGTH - 4]}_seq"
262
+ end
263
+
264
+ private def default_trigger_name(table_name)
265
+ # truncate table name if necessary to fit in max length of identifier
266
+ "#{table_name[0, self.class::IDENTIFIER_MAX_LENGTH - 4]}_pkt"
267
+ end
268
+
269
+ # @api private
270
+ private def add_column_statement
271
+ 'ADD'
272
+ end
273
+ end
274
+
275
+ module ClassMethods
276
+ # Types for Oracle databases.
277
+ #
278
+ # @return [Hash] types for Oracle databases.
279
+ #
280
+ # @api private
281
+ def type_map
282
+ length = Property::String.length
283
+ precision = Property::Numeric.precision
284
+ _scale = Property::Decimal.scale
285
+
286
+ super.merge(
287
+ Integer => {primitive: 'NUMBER', precision: precision, scale: 0},
288
+ String => {primitive: 'VARCHAR2', length: length},
289
+ Class => {primitive: 'VARCHAR2', length: length},
290
+ BigDecimal => {primitive: 'NUMBER', precision: precision, scale: nil},
291
+ Float => {primitive: 'BINARY_FLOAT'},
292
+ DateTime => {primitive: 'DATE'},
293
+ Date => {primitive: 'DATE'},
294
+ Time => {primitive: 'DATE'},
295
+ TrueClass => {primitive: 'NUMBER', precision: 1, scale: 0},
296
+ Property::Text => {primitive: 'CLOB'}
297
+ ).freeze
298
+ end
299
+
300
+ # Use table truncate or delete for auto_migrate! to speed up test execution
301
+ #
302
+ # @param [Symbol] :truncate, :delete or :drop_and_create (or nil)
303
+ # do not specify parameter to return current value
304
+ #
305
+ # @return [Mixed]
306
+ # [Symbol] current value of auto_migrate_with option
307
+ # (nil returned for :drop_and_create)
308
+ #
309
+ # @api semipublic
310
+ def auto_migrate_with(value = :not_specified)
311
+ return @auto_migrate_with if value == :not_specified
312
+
313
+ value = nil if value == :drop_and_create
314
+ raise ArgumentError unless [nil, :truncate, :delete].include?(value)
315
+
316
+ @auto_migrate_with = value
317
+ end
318
+
319
+ # Set if sequences will or will not be reset during auto_migrate!
320
+ #
321
+ # @param [Mixed] value
322
+ # reset sequences? TrueClass, FalseClass
323
+ # do not specify parameter to return current value
324
+ #
325
+ # @return [Mixed]
326
+ # [Symbol] current value of auto_migrate_reset_sequences option
327
+ # (default value is true)
328
+ #
329
+ # @api semipublic
330
+ def auto_migrate_reset_sequences(value = :not_specified)
331
+ return @auto_migrate_reset_sequences.nil? ? true : @auto_migrate_reset_sequences if value == :not_specified
332
+ raise ArgumentError unless [true, false].include?(value)
333
+
334
+ @auto_migrate_reset_sequences = value
335
+ end
336
+ end
337
+ end
338
+ end
339
+ end