declare_schema 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/declare_schema_build.yml +21 -5
  3. data/Appraisals +21 -4
  4. data/CHANGELOG.md +40 -0
  5. data/Gemfile +1 -2
  6. data/Gemfile.lock +7 -9
  7. data/README.md +3 -3
  8. data/Rakefile +17 -4
  9. data/bin/declare_schema +1 -1
  10. data/declare_schema.gemspec +1 -1
  11. data/gemfiles/rails_4_mysql.gemfile +22 -0
  12. data/gemfiles/{rails_4.gemfile → rails_4_sqlite.gemfile} +1 -2
  13. data/gemfiles/rails_5_mysql.gemfile +22 -0
  14. data/gemfiles/{rails_5.gemfile → rails_5_sqlite.gemfile} +1 -2
  15. data/gemfiles/rails_6_mysql.gemfile +22 -0
  16. data/gemfiles/{rails_6.gemfile → rails_6_sqlite.gemfile} +2 -3
  17. data/lib/declare_schema/command.rb +10 -3
  18. data/lib/declare_schema/model/column.rb +168 -0
  19. data/lib/declare_schema/model/field_spec.rb +59 -143
  20. data/lib/declare_schema/model/foreign_key_definition.rb +36 -25
  21. data/lib/declare_schema/model/table_options_definition.rb +8 -6
  22. data/lib/declare_schema/version.rb +1 -1
  23. data/lib/generators/declare_schema/migration/migration_generator.rb +1 -1
  24. data/lib/generators/declare_schema/migration/migrator.rb +142 -116
  25. data/spec/lib/declare_schema/field_declaration_dsl_spec.rb +1 -1
  26. data/spec/lib/declare_schema/field_spec_spec.rb +135 -38
  27. data/spec/lib/declare_schema/generator_spec.rb +4 -2
  28. data/spec/lib/declare_schema/interactive_primary_key_spec.rb +8 -2
  29. data/spec/lib/declare_schema/migration_generator_spec.rb +277 -171
  30. data/spec/lib/declare_schema/model/column_spec.rb +141 -0
  31. data/spec/lib/declare_schema/model/foreign_key_definition_spec.rb +93 -0
  32. data/spec/lib/declare_schema/model/index_definition_spec.rb +4 -5
  33. data/spec/lib/declare_schema/model/table_options_definition_spec.rb +19 -29
  34. data/spec/lib/generators/declare_schema/migration/migrator_spec.rb +12 -26
  35. data/spec/support/acceptance_spec_helpers.rb +3 -3
  36. metadata +15 -9
@@ -26,12 +26,14 @@ module DeclareSchema
26
26
 
27
27
  def mysql_table_options(connection, table_name)
28
28
  database = connection.current_database
29
- defaults = connection.select_one(<<~EOS)
29
+ defaults = connection.select_one(<<~EOS) or raise "no defaults found for table #{table_name}"
30
30
  SELECT CCSA.character_set_name, CCSA.collation_name
31
- FROM information_schema.`TABLES` T, information_schema.`COLLATION_CHARACTER_SET_APPLICABILITY` CCSA
32
- WHERE CCSA.collation_name = T.table_collation AND
33
- T.table_schema = '#{connection.quote_string(database)}' AND
34
- T.table_name = '#{connection.quote_string(table_name)}';
31
+ FROM information_schema.TABLES as T
32
+ JOIN information_schema.COLLATION_CHARACTER_SET_APPLICABILITY as CCSA
33
+ ON CCSA.collation_name = T.table_collation
34
+ WHERE
35
+ T.table_schema = '#{connection.quote_string(database)}' AND
36
+ T.table_name = '#{connection.quote_string(table_name)}'
35
37
  EOS
36
38
 
37
39
  defaults["character_set_name"] or raise "character_set_name missing from #{defaults.inspect}"
@@ -75,7 +77,7 @@ module DeclareSchema
75
77
  alias to_s settings
76
78
 
77
79
  def alter_table_statement
78
- statement = "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(table_name)} #{to_s};"
80
+ statement = "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(table_name)} #{to_s}"
79
81
  "execute #{statement.inspect}"
80
82
  end
81
83
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeclareSchema
4
- VERSION = "0.6.0"
4
+ VERSION = "0.7.0"
5
5
  end
@@ -96,7 +96,7 @@ module DeclareSchema
96
96
  end
97
97
  end
98
98
  end
99
- rescue ::DeclareSchema::Model::FieldSpec::UnknownSqlTypeError => ex
99
+ rescue ::DeclareSchema::UnknownSqlTypeError => ex
100
100
  say "Invalid field type: #{ex}"
101
101
  end
102
102
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'active_record'
4
+ require 'active_record/connection_adapters/abstract_adapter'
4
5
 
5
6
  module Generators
6
7
  module DeclareSchema
@@ -71,8 +72,8 @@ module Generators
71
72
  class Migrator
72
73
  class Error < RuntimeError; end
73
74
 
74
- DEFAULT_CHARSET = :utf8mb4
75
- DEFAULT_COLLATION = :utf8mb4_general
75
+ DEFAULT_CHARSET = "utf8mb4"
76
+ DEFAULT_COLLATION = "utf8mb4_bin"
76
77
 
77
78
  @ignore_models = []
78
79
  @ignore_tables = []
@@ -82,9 +83,18 @@ module Generators
82
83
  @default_collation = DEFAULT_COLLATION
83
84
 
84
85
  class << self
85
- attr_accessor :ignore_models, :ignore_tables, :disable_indexing, :disable_constraints,
86
- :active_record_class, :default_charset, :default_collation
87
- attr_reader :before_generating_migration_callback
86
+ attr_accessor :ignore_models, :ignore_tables, :disable_indexing, :disable_constraints
87
+ attr_reader :active_record_class, :default_charset, :default_collation, :before_generating_migration_callback
88
+
89
+ def default_charset=(charset)
90
+ charset.is_a?(String) or raise ArgumentError, "charset must be a string (got #{charset.inspect})"
91
+ @default_charset = charset
92
+ end
93
+
94
+ def default_collation=(collation)
95
+ collation.is_a?(String) or raise ArgumentError, "collation must be a string (got #{collation.inspect})"
96
+ @default_collation = collation
97
+ end
88
98
 
89
99
  def active_record_class
90
100
  @active_record_class.is_a?(Class) or @active_record_class = @active_record_class.to_s.constantize
@@ -107,20 +117,6 @@ module Generators
107
117
  ActiveRecord::Base.connection
108
118
  end
109
119
 
110
- def fix_native_types(types)
111
- case connection.class.name
112
- when /mysql/i
113
- types[:integer][:limit] ||= 11
114
- types[:text][:limit] ||= 0xffff
115
- types[:binary][:limit] ||= 0xffff
116
- end
117
- types
118
- end
119
-
120
- def native_types
121
- @native_types ||= fix_native_types(connection.native_database_types)
122
- end
123
-
124
120
  def before_generating_migration(&block)
125
121
  block or raise ArgumentError, 'A block is required when setting the before_generating_migration callback'
126
122
  @before_generating_migration_callback = block
@@ -290,7 +286,7 @@ module Generators
290
286
  "drop_table :#{t}"
291
287
  end * "\n"
292
288
  undo_drops = to_drop.map do |t|
293
- revert_table(t)
289
+ add_table_back(t)
294
290
  end * "\n\n"
295
291
 
296
292
  creates = to_create.map do |t|
@@ -336,7 +332,7 @@ module Generators
336
332
  disable_auto_increment = model.respond_to?(:disable_auto_increment) && model.disable_auto_increment
337
333
  table_options_definition = ::DeclareSchema::Model::TableOptionsDefinition.new(model.table_name, table_options_for_model(model))
338
334
  field_definitions = [
339
- disable_auto_increment ? "t.integer :id, limit: 8, auto_increment: false, primary_key: true" : nil,
335
+ ("t.integer :id, limit: 8, auto_increment: false, primary_key: true" if disable_auto_increment),
340
336
  *(model.field_specs.values.sort_by(&:position).map { |f| create_field(f, longest_field_name) })
341
337
  ].compact
342
338
 
@@ -377,12 +373,12 @@ module Generators
377
373
  end
378
374
 
379
375
  def create_constraints(model)
380
- model.constraint_specs.map { |fk| fk.to_add_statement(model.table_name) }
376
+ model.constraint_specs.map { |fk| fk.to_add_statement }
381
377
  end
382
378
 
383
379
  def create_field(field_spec, field_name_width)
384
- options = fk_field_options(field_spec.model, field_spec.name).merge(field_spec.sql_options)
385
- args = [field_spec.name.inspect] + format_options(options, field_spec.sql_type)
380
+ options = field_spec.sql_options.merge(fk_field_options(field_spec.model, field_spec.name))
381
+ args = [field_spec.name.inspect] + format_options(options.compact)
386
382
  format("t.%-*s %s", field_name_width, field_spec.sql_type, args.join(', '))
387
383
  end
388
384
 
@@ -420,12 +416,12 @@ module Generators
420
416
  adds = to_add.map do |c|
421
417
  args =
422
418
  if (spec = model.field_specs[c])
423
- options = fk_field_options(model, c).merge(spec.sql_options)
424
- [":#{spec.sql_type}", *format_options(options, spec.sql_type)]
419
+ options = spec.sql_options.merge(fk_field_options(model, c))
420
+ [":#{spec.sql_type}", *format_options(options.compact)]
425
421
  else
426
422
  [":integer"]
427
423
  end
428
- "add_column :#{new_table_name}, :#{c}, #{args.join(', ')}"
424
+ ["add_column :#{new_table_name}, :#{c}", *args].join(', ')
429
425
  end
430
426
  undo_adds = to_add.map do |c|
431
427
  "remove_column :#{new_table_name}, :#{c}"
@@ -435,36 +431,30 @@ module Generators
435
431
  "remove_column :#{new_table_name}, :#{c}"
436
432
  end
437
433
  undo_removes = to_remove.map do |c|
438
- revert_column(current_table_name, c)
434
+ add_column_back(model, current_table_name, c)
439
435
  end
440
436
 
441
437
  old_names = to_rename.invert
442
438
  changes = []
443
439
  undo_changes = []
444
- to_change.each do |c|
445
- col_name = old_names[c] || c
446
- col = db_columns[col_name]
447
- spec = model.field_specs[c]
448
- if spec.different_to?(current_table_name, col) # TODO: TECH-4814 DRY this up to a diff function that returns the differences. It's different if it has differences. -Colin
449
- change_spec = fk_field_options(model, c)
450
- change_spec[:limit] ||= spec.limit if (spec.sql_type != :text ||
451
- ::DeclareSchema::Model::FieldSpec.mysql_text_limits?) &&
452
- (spec.limit || col.limit)
453
- change_spec[:precision] = spec.precision unless spec.precision.nil?
454
- change_spec[:scale] = spec.scale unless spec.scale.nil?
455
- change_spec[:null] = spec.null unless spec.null && col.null
456
- change_spec[:default] = spec.default unless spec.default.nil? && col.default.nil?
457
- change_spec[:collation] = spec.collation unless spec.collation.nil?
458
- change_spec[:charset] = spec.charset unless spec.charset.nil?
459
-
460
- changes << "change_column :#{new_table_name}, :#{c}, " +
461
- ([":#{spec.sql_type}"] + format_options(change_spec, spec.sql_type, changing: true)).join(", ")
462
- back = change_column_back(current_table_name, col_name)
463
- undo_changes << back unless back.blank?
440
+ to_change.each do |col_name_to_change|
441
+ orig_col_name = old_names[col_name_to_change] || col_name_to_change
442
+ column = db_columns[orig_col_name] or raise "failed to find column info for #{orig_col_name.inspect}"
443
+ spec = model.field_specs[col_name_to_change] or raise "failed to find field spec for #{col_name_to_change.inspect}"
444
+ spec_attrs = spec.schema_attributes(column)
445
+ column_declaration = ::DeclareSchema::Model::Column.new(model, current_table_name, column)
446
+ col_attrs = column_declaration.schema_attributes
447
+ if !::DeclareSchema::Model::Column.equivalent_schema_attributes?(spec_attrs, col_attrs)
448
+ normalized_schema_attributes = spec_attrs.merge(fk_field_options(model, col_name_to_change))
449
+
450
+ type = normalized_schema_attributes.delete(:type) or raise "no :type found in #{normalized_schema_attributes.inspect}"
451
+ changes << ["change_column #{new_table_name.to_sym.inspect}", col_name_to_change.to_sym.inspect,
452
+ type.to_sym.inspect, *format_options(normalized_schema_attributes)].join(", ")
453
+ undo_changes << change_column_back(model, current_table_name, orig_col_name)
464
454
  end
465
- end.compact
455
+ end
466
456
 
467
- index_changes, undo_index_changes = change_indexes(model, current_table_name)
457
+ index_changes, undo_index_changes = change_indexes(model, current_table_name, to_remove)
468
458
  fk_changes, undo_fk_changes = if ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/)
469
459
  [[], []]
470
460
  else
@@ -476,41 +466,42 @@ module Generators
476
466
  [[], []]
477
467
  end
478
468
 
479
- [(renames + adds + removes + changes) * "\n",
469
+ [(renames + adds + removes + changes) * "\n",
480
470
  (undo_renames + undo_adds + undo_removes + undo_changes) * "\n",
481
- index_changes * "\n",
482
- undo_index_changes * "\n",
483
- fk_changes * "\n",
484
- undo_fk_changes * "\n",
485
- table_options_changes * "\n",
486
- undo_table_options_changes * "\n"]
471
+ index_changes * "\n",
472
+ undo_index_changes * "\n",
473
+ fk_changes * "\n",
474
+ undo_fk_changes * "\n",
475
+ table_options_changes * "\n",
476
+ undo_table_options_changes * "\n"]
487
477
  end
488
478
 
489
- def change_indexes(model, old_table_name)
490
- return [[], []] if Migrator.disable_constraints
479
+ def change_indexes(model, old_table_name, to_remove)
480
+ Migrator.disable_constraints and return [[], []]
491
481
 
492
482
  new_table_name = model.table_name
493
483
  existing_indexes = ::DeclareSchema::Model::IndexDefinition.for_model(model, old_table_name)
494
484
  model_indexes_with_equivalents = model.index_definitions_with_primary_key
495
485
  model_indexes = model_indexes_with_equivalents.map do |i|
496
486
  if i.explicit_name.nil?
497
- if ex = existing_indexes.find { |e| i != e && e.equivalent?(i) }
498
- i.with_name(ex.name)
487
+ if (existing = existing_indexes.find { |e| i != e && e.equivalent?(i) })
488
+ i.with_name(existing.name)
499
489
  end
500
490
  end || i
501
491
  end
502
- existing_has_primary_key = existing_indexes.any? { |i| i.name == ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME }
492
+ existing_has_primary_key = existing_indexes.any? do |i|
493
+ i.name == ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME &&
494
+ !i.fields.all? { |f| to_remove.include?(f) } # if we're removing the primary key column(s), the primary key index will be removed too
495
+ end
503
496
  model_has_primary_key = model_indexes.any? { |i| i.name == ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME }
504
497
 
505
- add_indexes_init = model_indexes - existing_indexes
506
- drop_indexes_init = existing_indexes - model_indexes
507
498
  undo_add_indexes = []
508
- undo_drop_indexes = []
509
- add_indexes = add_indexes_init.map do |i|
499
+ add_indexes = (model_indexes - existing_indexes).map do |i|
510
500
  undo_add_indexes << drop_index(old_table_name, i.name) unless i.name == ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME
511
501
  i.to_add_statement(new_table_name, existing_has_primary_key)
512
502
  end
513
- drop_indexes = drop_indexes_init.map do |i|
503
+ undo_drop_indexes = []
504
+ drop_indexes = (existing_indexes - model_indexes).map do |i|
514
505
  undo_drop_indexes << i.to_add_statement(old_table_name, model_has_primary_key)
515
506
  drop_index(new_table_name, i.name) unless i.name == ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME
516
507
  end.compact
@@ -526,59 +517,50 @@ module Generators
526
517
  end
527
518
 
528
519
  def change_foreign_key_constraints(model, old_table_name)
529
- ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/) and raise 'SQLite does not support foreign keys'
530
- return [[], []] if Migrator.disable_indexing
520
+ ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/) and raise ArgumentError, 'SQLite does not support foreign keys'
521
+ Migrator.disable_indexing and return [[], []]
531
522
 
532
523
  new_table_name = model.table_name
533
524
  existing_fks = ::DeclareSchema::Model::ForeignKeyDefinition.for_model(model, old_table_name)
534
525
  model_fks = model.constraint_specs
535
- add_fks = model_fks - existing_fks
536
- drop_fks = existing_fks - model_fks
537
- undo_add_fks = []
538
- undo_drop_fks = []
539
526
 
540
- add_fks.map! do |fk|
527
+ undo_add_fks = []
528
+ add_fks = (model_fks - existing_fks).map do |fk|
541
529
  # next if fk.parent.constantize.abstract_class || fk.parent == fk.model.class_name
542
- undo_add_fks << remove_foreign_key(old_table_name, fk.options[:constraint_name])
530
+ undo_add_fks << remove_foreign_key(old_table_name, fk.constraint_name)
543
531
  fk.to_add_statement
544
- end.compact
532
+ end
545
533
 
546
- drop_fks.map! do |fk|
534
+ undo_drop_fks = []
535
+ drop_fks = (existing_fks - model_fks).map do |fk|
547
536
  undo_drop_fks << fk.to_add_statement
548
- remove_foreign_key(new_table_name, fk.options[:constraint_name])
537
+ remove_foreign_key(new_table_name, fk.constraint_name)
549
538
  end
550
539
 
551
540
  [drop_fks + add_fks, undo_add_fks + undo_drop_fks]
552
541
  end
553
542
 
554
543
  def remove_foreign_key(old_table_name, fk_name)
555
- "remove_foreign_key('#{old_table_name}', name: '#{fk_name}')"
544
+ "remove_foreign_key(#{old_table_name.inspect}, name: #{fk_name.to_s.inspect})"
556
545
  end
557
546
 
558
- def format_options(options, type, changing: false)
547
+ def format_options(options)
559
548
  options.map do |k, v|
560
- unless changing
561
- next if k == :limit && (type == :decimal || v == native_types[type][:limit])
562
- next if k == :null && v == true
563
- end
564
-
565
- next if k == :limit && type == :text && !::DeclareSchema::Model::FieldSpec.mysql_text_limits?
566
-
567
549
  if k.is_a?(Symbol)
568
550
  "#{k}: #{v.inspect}"
569
551
  else
570
552
  "#{k.inspect} => #{v.inspect}"
571
553
  end
572
- end.compact
554
+ end
573
555
  end
574
556
 
575
557
  def fk_field_options(model, field_name)
576
558
  foreign_key = model.constraint_specs.find { |fk| field_name == fk.foreign_key.to_s }
577
559
  if foreign_key && (parent_table = foreign_key.parent_table_name)
578
560
  parent_columns = connection.columns(parent_table) rescue []
579
- pk_limit =
561
+ pk_limit =
580
562
  if (pk_column = parent_columns.find { |column| column.name.to_s == "id" }) # right now foreign keys assume id is the target
581
- if Rails::VERSION::MAJOR <= 4
563
+ if Rails::VERSION::MAJOR < 5
582
564
  pk_column.cast_type.limit
583
565
  else
584
566
  pk_column.limit
@@ -607,39 +589,83 @@ module Generators
607
589
  end
608
590
  end
609
591
 
610
- def revert_table(table)
611
- res = StringIO.new
612
- schema_dumper_klass = case Rails::VERSION::MAJOR
613
- when 4
614
- ActiveRecord::SchemaDumper
615
- else
616
- ActiveRecord::ConnectionAdapters::SchemaDumper
617
- end
618
- schema_dumper_klass.send(:new, ActiveRecord::Base.connection).send(:table, table, res)
619
- res.string.strip.gsub("\n ", "\n")
592
+ def with_previous_model_table_name(model, table_name)
593
+ model_table_name, model.table_name = model.table_name, table_name
594
+ yield
595
+ ensure
596
+ model.table_name = model_table_name
597
+ end
598
+
599
+ def add_column_back(model, current_table_name, col_name)
600
+ with_previous_model_table_name(model, current_table_name) do
601
+ column = model.columns_hash[col_name] or raise "no columns_hash entry found for #{col_name} in #{model.inspect}"
602
+ col_spec = ::DeclareSchema::Model::Column.new(model, current_table_name, column)
603
+ schema_attributes = col_spec.schema_attributes
604
+ type = schema_attributes.delete(:type) or raise "no :type found in #{schema_attributes.inspect}"
605
+ ["add_column :#{current_table_name}, :#{col_name}, #{type.inspect}", *format_options(schema_attributes)].join(', ')
606
+ end
607
+ end
608
+
609
+ def change_column_back(model, current_table_name, col_name)
610
+ with_previous_model_table_name(model, current_table_name) do
611
+ column = model.columns_hash[col_name] or raise "no columns_hash entry found for #{col_name} in #{model.inspect}"
612
+ col_spec = ::DeclareSchema::Model::Column.new(model, current_table_name, column)
613
+ schema_attributes = col_spec.schema_attributes
614
+ type = schema_attributes.delete(:type) or raise "no :type found in #{schema_attributes.inspect}"
615
+ ["change_column #{current_table_name.to_sym.inspect}", col_name.to_sym.inspect, type.to_sym.inspect, *format_options(schema_attributes)].join(', ')
616
+ end
620
617
  end
621
618
 
622
- def column_options_from_reverted_table(table, col_name)
623
- revert = revert_table(table)
624
- if (md = revert.match(/\s*t\.column\s+"#{col_name}",\s+(:[a-zA-Z0-9_]+)(?:,\s+(.*?)$)?/m))
625
- # Ugly migration
626
- _, type, options = *md
627
- elsif (md = revert.match(/\s*t\.([a-z_]+)\s+"#{col_name}"(?:,\s+(.*?)$)?/m))
628
- # Sexy migration
629
- _, type, options = *md
630
- type = ":#{type}"
619
+ def default_collation_from_charset(charset)
620
+ case charset
621
+ when "utf8"
622
+ "utf8_general_ci"
623
+ when "utf8mb4"
624
+ "utf8mb4_general_ci"
631
625
  end
632
- [type, options]
633
626
  end
634
627
 
635
- def change_column_back(table, col_name)
636
- type, options = column_options_from_reverted_table(table, col_name)
637
- "change_column :#{table}, :#{col_name}, #{type}#{', ' + options.strip if options}"
628
+ SchemaDumper = case Rails::VERSION::MAJOR
629
+ when 4
630
+ ActiveRecord::SchemaDumper
631
+ else
632
+ ActiveRecord::ConnectionAdapters::SchemaDumper
633
+ end
634
+
635
+ def add_table_back(table)
636
+ dumped_schema_stream = StringIO.new
637
+ SchemaDumper.send(:new, ActiveRecord::Base.connection).send(:table, table, dumped_schema_stream)
638
+
639
+ dumped_schema = dumped_schema_stream.string.strip.gsub!("\n ", "\n")
640
+ if connection.class.name.match?(/mysql/i)
641
+ fix_mysql_charset_and_collation(dumped_schema)
642
+ else
643
+ dumped_schema
644
+ end
638
645
  end
639
646
 
640
- def revert_column(table, column)
641
- type, options = column_options_from_reverted_table(table, column)
642
- "add_column :#{table}, :#{column}, #{type}#{', ' + options.strip if options}"
647
+ # TODO: rewrite this method to use charset and collation variables rather than manipulating strings. -Colin
648
+ def fix_mysql_charset_and_collation(dumped_schema)
649
+ if !dumped_schema['options: ']
650
+ dumped_schema.sub!('",', "\", options: \"DEFAULT CHARSET=#{Generators::DeclareSchema::Migration::Migrator.default_charset} "+
651
+ "COLLATE=#{Generators::DeclareSchema::Migration::Migrator.default_collation}\",")
652
+ end
653
+ default_charset = dumped_schema[/CHARSET=(\w+)/, 1] or raise "unable to find charset in #{dumped_schema.inspect}"
654
+ default_collation = dumped_schema[/COLLATE=(\w+)/, 1] || default_collation_from_charset(default_charset) or
655
+ raise "unable to find collation in #{dumped_schema.inspect} or charset #{default_charset.inspect}"
656
+ dumped_schema.split("\n").map do |line|
657
+ if line['t.text'] || line['t.string']
658
+ if !line['charset: ']
659
+ if line['collation: ']
660
+ line.sub!('collation: ', "charset: #{default_charset.inspect}, collation: ")
661
+ else
662
+ line << ", charset: #{default_charset.inspect}"
663
+ end
664
+ end
665
+ line['collation: '] or line << ", collation: #{default_collation.inspect}"
666
+ end
667
+ line
668
+ end.join("\n")
643
669
  end
644
670
  end
645
671
  end