declare_schema 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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