declare_schema 1.4.0.colin.7 → 1.4.0.colin.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,6 +3,7 @@
3
3
  require 'active_support'
4
4
  require 'active_support/all'
5
5
  require_relative 'declare_schema/version'
6
+ require 'rubygems/version'
6
7
 
7
8
  ActiveSupport::Dependencies.autoload_paths |= [__dir__]
8
9
 
@@ -21,6 +22,8 @@ module DeclareSchema
21
22
  text: String
22
23
  }.freeze
23
24
 
25
+ SEMVER_8 = Gem::Version.new('8.0.0').freeze
26
+
24
27
  @default_charset = "utf8mb4"
25
28
  @default_collation = "utf8mb4_bin"
26
29
  @default_text_limit = 0xffff_ffff
@@ -32,6 +35,7 @@ module DeclareSchema
32
35
  @max_index_and_constraint_name_length = 64 # limit for MySQL
33
36
 
34
37
  class << self
38
+ attr_writer :mysql_version
35
39
  attr_reader :default_charset, :default_collation, :default_text_limit, :default_string_limit, :default_null,
36
40
  :default_generate_foreign_keys, :default_generate_indexing, :db_migrate_command,
37
41
  :max_index_and_constraint_name_length
@@ -47,14 +51,42 @@ module DeclareSchema
47
51
  end
48
52
  end
49
53
 
54
+ def mysql_version
55
+ if defined?(@mysql_version)
56
+ @mysql_version
57
+ else
58
+ @mysql_version =
59
+ if ActiveRecord::Base.connection.class.name.match?(/mysql/i)
60
+ version_string = ActiveRecord::Base.connection.select_value('SELECT VERSION()')
61
+ Gem::Version.new(version_string)
62
+ end
63
+ end
64
+ end
65
+
66
+ def normalize_charset(charset)
67
+ if mysql_version && mysql_version >= SEMVER_8 && charset == 'utf8'
68
+ 'utf8mb3'
69
+ else
70
+ charset
71
+ end
72
+ end
73
+
74
+ def normalize_collation(collation)
75
+ if mysql_version && mysql_version >= SEMVER_8
76
+ collation.sub(/\Autf8_/, 'utf8mb3_')
77
+ else
78
+ collation
79
+ end
80
+ end
81
+
50
82
  def default_charset=(charset)
51
83
  charset.is_a?(String) or raise ArgumentError, "charset must be a string (got #{charset.inspect})"
52
- @default_charset = charset
84
+ @default_charset = normalize_charset(charset)
53
85
  end
54
86
 
55
87
  def default_collation=(collation)
56
88
  collation.is_a?(String) or raise ArgumentError, "collation must be a string (got #{collation.inspect})"
57
- @default_collation = collation
89
+ @default_collation = normalize_collation(collation)
58
90
  end
59
91
 
60
92
  def default_text_limit=(text_limit)
@@ -3,6 +3,7 @@
3
3
  require 'active_record'
4
4
  require 'active_record/connection_adapters/abstract_adapter'
5
5
  require 'declare_schema/schema_change/all'
6
+ require_relative '../../../declare_schema/model/habtm_model_shim'
6
7
 
7
8
  module Generators
8
9
  module DeclareSchema
@@ -335,9 +336,9 @@ module Generators
335
336
  end
336
337
 
337
338
  def create_constraints(model)
338
- model.constraint_specs.map do |fk|
339
+ model.constraint_definitions.map do |fk|
339
340
  ::DeclareSchema::SchemaChange::ForeignKeyAdd.new(fk.child_table_name, fk.parent_table_name,
340
- column_name: fk.foreign_key_name, name: fk.constraint_name)
341
+ column_name: fk.foreign_key_column, name: fk.constraint_name)
341
342
  end
342
343
  end
343
344
 
@@ -425,7 +426,7 @@ module Generators
425
426
  ::DeclareSchema.default_generate_indexing or return []
426
427
 
427
428
  new_table_name = model.table_name
428
- existing_indexes = ::DeclareSchema::Model::IndexDefinition.for_model(model, old_table_name)
429
+ existing_indexes = ::DeclareSchema::Model::IndexDefinition.for_table(old_table_name || new_table_name, model.ignore_indexes, model.connection)
429
430
  model_indexes_with_equivalents = model.index_definitions_with_primary_key.to_a
430
431
  model_indexes = model_indexes_with_equivalents.map do |i|
431
432
  if i.explicit_name.nil?
@@ -485,8 +486,12 @@ module Generators
485
486
  ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/) and raise ArgumentError, 'SQLite does not support foreign keys'
486
487
  ::DeclareSchema.default_generate_foreign_keys or return []
487
488
 
488
- existing_fks = ::DeclareSchema::Model::ForeignKeyDefinition.for_model(model, old_table_name)
489
- model_fks = model.constraint_specs.to_a
489
+ if model.is_a?(::DeclareSchema::Model::HabtmModelShim)
490
+ force_dependent_delete = :delete
491
+ end
492
+
493
+ existing_fks = ::DeclareSchema::Model::ForeignKeyDefinition.for_table(old_table_name || model.table_name, model.connection, dependent: force_dependent_delete)
494
+ model_fks = model.constraint_definitions.to_a
490
495
 
491
496
  fks_to_drop = existing_fks - model_fks
492
497
  fks_to_add = model_fks - existing_fks
@@ -495,13 +500,13 @@ module Generators
495
500
 
496
501
  drop_fks = (fks_to_drop - renamed_fks_to_drop).map do |fk|
497
502
  ::DeclareSchema::SchemaChange::ForeignKeyRemove.new(fk.child_table_name, fk.parent_table_name,
498
- column_name: fk.foreign_key_name, name: fk.constraint_name)
503
+ column_name: fk.foreign_key_column, name: fk.constraint_name)
499
504
  end
500
505
 
501
506
  add_fks = (fks_to_add - renamed_fks_to_add).map do |fk|
502
507
  # next if fk.parent.constantize.abstract_class || fk.parent == fk.model.class_name
503
508
  ::DeclareSchema::SchemaChange::ForeignKeyAdd.new(fk.child_table_name, fk.parent_table_name,
504
- column_name: fk.foreign_key_name, name: fk.constraint_name)
509
+ column_name: fk.foreign_key_column, name: fk.constraint_name)
505
510
  end
506
511
 
507
512
  [drop_fks + add_fks]
@@ -523,9 +528,8 @@ module Generators
523
528
  end
524
529
 
525
530
  def fk_field_options(model, field_name)
526
- foreign_key = model.constraint_specs.find { |fk| field_name == fk.foreign_key.to_s }
527
- if foreign_key && (parent_table = foreign_key.parent_table_name)
528
- parent_columns = connection.columns(parent_table) rescue []
531
+ if (foreign_key = model.constraint_definitions.find { |fk| field_name == fk.foreign_key_column })
532
+ parent_columns = connection.columns(foreign_key.parent_table_name) rescue []
529
533
  pk_limit =
530
534
  if (pk_column = parent_columns.find { |column| column.name.to_s == "id" }) # right now foreign keys assume id is the target
531
535
  pk_column.limit
@@ -585,6 +589,8 @@ module Generators
585
589
  case charset
586
590
  when "utf8"
587
591
  "utf8_general_ci"
592
+ when "utf8mb3"
593
+ "utf8mb3_general_ci"
588
594
  when "utf8mb4"
589
595
  "utf8mb4_general_ci"
590
596
  end
@@ -5,6 +5,11 @@ begin
5
5
  rescue LoadError
6
6
  end
7
7
 
8
+ begin
9
+ require 'sqlite3'
10
+ rescue LoadError
11
+ end
12
+
8
13
  RSpec.describe DeclareSchema::Model::FieldSpec do
9
14
  let(:model) { double('model', _table_options: {}, _declared_primary_key: 'id') }
10
15
  let(:col_spec) { double('col_spec', type: :string) }
@@ -58,8 +63,23 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
58
63
  end
59
64
  end
60
65
 
61
- it 'raises error when default_string_limit option is nil when not explicitly set in field spec' do
62
- if defined?(Mysql2)
66
+ if defined?(Mysql2)
67
+ context 'when running on MySQL 8.0' do
68
+ around do |spec|
69
+ DeclareSchema.mysql_version = Gem::Version.new('8.0.21')
70
+ spec.run
71
+ ensure
72
+ DeclareSchema.remove_instance_variable('@mysql_version') rescue nil
73
+ end
74
+
75
+ it 'normalizes charset and collation' do
76
+ subject = described_class.new(model, :title, :string, limit: 100, null: true, charset: 'utf8', collation: 'utf8_general', position: 0)
77
+
78
+ expect(subject.schema_attributes(col_spec)).to eq(type: :string, limit: 100, null: true, charset: 'utf8mb3', collation: 'utf8mb3_general')
79
+ end
80
+ end
81
+
82
+ it 'raises error when default_string_limit option is nil when not explicitly set in field spec' do
63
83
  expect(::DeclareSchema).to receive(:default_string_limit) { nil }
64
84
  expect do
65
85
  described_class.new(model, :title, :string, null: true, charset: 'utf8mb4', position: 0)
@@ -391,6 +391,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
391
391
 
392
392
  Advert.field_specs.delete(:c_id)
393
393
  Advert.index_definitions.delete_if { |spec| spec.fields == ["c_id"] }
394
+ Advert.constraint_definitions.delete_if { |spec| spec.foreign_key_column == "c_id" }
394
395
 
395
396
  # You can avoid generating the index by specifying `index: false`
396
397
 
@@ -403,13 +404,13 @@ RSpec.describe 'DeclareSchema Migration Generator' do
403
404
  expect(Generators::DeclareSchema::Migration::Migrator.run).to(
404
405
  migrate_up(<<~EOS.strip)
405
406
  add_column :adverts, :category_id, :integer, limit: 8, null: false
406
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
407
- "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
407
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id" if defined?(Mysql2)}
408
408
  EOS
409
409
  )
410
410
 
411
411
  Advert.field_specs.delete(:category_id)
412
412
  Advert.index_definitions.delete_if { |spec| spec.fields == ["category_id"] }
413
+ Advert.constraint_definitions.delete_if { |spec| spec.foreign_key_column == "category_id" }
413
414
 
414
415
  # You can specify the index name with index: 'name' [deprecated]
415
416
 
@@ -425,13 +426,13 @@ RSpec.describe 'DeclareSchema Migration Generator' do
425
426
  migrate_up(<<~EOS.strip)
426
427
  add_column :adverts, :category_id, :integer, limit: 8, null: false
427
428
  add_index :adverts, [:category_id], name: :my_index
428
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
429
- "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
429
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :my_index" if defined?(Mysql2)}
430
430
  EOS
431
431
  )
432
432
 
433
433
  Advert.field_specs.delete(:category_id)
434
434
  Advert.index_definitions.delete_if { |spec| spec.fields == ["category_id"] }
435
+ Advert.constraint_definitions.delete_if { |spec| spec.foreign_key_column == "category_id" }
435
436
 
436
437
  # You can specify the index name with index: { name: }'name', unique: true|false }
437
438
 
@@ -443,15 +444,15 @@ RSpec.describe 'DeclareSchema Migration Generator' do
443
444
 
444
445
  expect(Generators::DeclareSchema::Migration::Migrator.run).to(
445
446
  migrate_up(<<~EOS.strip)
446
- add_column :adverts, :category_id, :integer, limit: 8, null: false
447
- add_index :adverts, [:category_id], name: :my_index
448
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
449
- "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
450
- EOS
447
+ add_column :adverts, :category_id, :integer, limit: 8, null: false
448
+ add_index :adverts, [:category_id], name: :my_index
449
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :my_index" if defined?(Mysql2)}
450
+ EOS
451
451
  )
452
452
 
453
453
  Advert.field_specs.delete(:category_id)
454
454
  Advert.index_definitions.delete_if { |spec| spec.fields == ["category_id"] }
455
+ Advert.constraint_definitions.delete_if { |spec| spec.foreign_key_column == "category_id" }
455
456
 
456
457
  ### Timestamps and Optimimistic Locking
457
458
 
@@ -470,12 +471,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
470
471
  add_column :adverts, :created_at, :datetime, null: true
471
472
  add_column :adverts, :updated_at, :datetime, null: true
472
473
  add_column :adverts, :lock_version, :integer#{lock_version_limit}, null: false, default: 1
473
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
474
- "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
475
474
  EOS
476
475
  .and migrate_down(<<~EOS.strip)
477
- #{"remove_foreign_key :adverts, name: :index_adverts_on_c_id\n" +
478
- "remove_foreign_key :adverts, name: :index_adverts_on_category_id" if defined?(Mysql2)}
479
476
  remove_column :adverts, :lock_version
480
477
  remove_column :adverts, :updated_at
481
478
  remove_column :adverts, :created_at
@@ -506,15 +503,15 @@ RSpec.describe 'DeclareSchema Migration Generator' do
506
503
  add_column :adverts, :category_id, :integer, limit: 8, null: false
507
504
  add_index :adverts, [:title], name: :index_adverts_on_title
508
505
  add_index :adverts, [:category_id], name: :my_index
509
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
510
- "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
506
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :my_index" if defined?(Mysql2)}
511
507
  EOS
512
508
  )
513
509
 
514
510
  Advert.field_specs.delete(:category_id)
515
511
  Advert.index_definitions.delete_if { |spec| spec.fields == ["title"] || spec.fields == ["category_id"] }
512
+ Advert.constraint_definitions.delete_if { |spec| spec.foreign_key_column == "category_id" }
516
513
 
517
- # You can ask for a unique index
514
+ # You can ask for a unique index (deprecated syntax; use index: { unique: true } instead).
518
515
 
519
516
  class Advert < ActiveRecord::Base
520
517
  declare_schema do
@@ -526,8 +523,6 @@ RSpec.describe 'DeclareSchema Migration Generator' do
526
523
  migrate_up(<<~EOS.strip)
527
524
  add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
528
525
  add_index :adverts, [:title], name: :index_adverts_on_title, unique: true
529
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
530
- "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
531
526
  EOS
532
527
  )
533
528
 
@@ -545,8 +540,6 @@ RSpec.describe 'DeclareSchema Migration Generator' do
545
540
  migrate_up(<<~EOS.strip)
546
541
  add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
547
542
  add_index :adverts, [:title], name: :my_index
548
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
549
- "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
550
543
  EOS
551
544
  )
552
545
 
@@ -555,6 +548,9 @@ RSpec.describe 'DeclareSchema Migration Generator' do
555
548
  # You can ask for an index outside of the fields block
556
549
 
557
550
  class Advert < ActiveRecord::Base
551
+ declare_schema do
552
+ string :title, limit: 250, null: true
553
+ end
558
554
  index :title
559
555
  end
560
556
 
@@ -562,8 +558,6 @@ RSpec.describe 'DeclareSchema Migration Generator' do
562
558
  migrate_up(<<~EOS.strip)
563
559
  add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
564
560
  add_index :adverts, [:title], name: :index_adverts_on_title
565
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
566
- "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
567
561
  EOS
568
562
  )
569
563
 
@@ -579,8 +573,6 @@ RSpec.describe 'DeclareSchema Migration Generator' do
579
573
  migrate_up(<<~EOS.strip)
580
574
  add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
581
575
  add_index :adverts, [:title], name: :my_index, length: 10
582
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
583
- "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
584
576
  EOS
585
577
  )
586
578
 
@@ -596,8 +588,6 @@ RSpec.describe 'DeclareSchema Migration Generator' do
596
588
  migrate_up(<<~EOS.strip)
597
589
  add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
598
590
  add_index :adverts, [:title, :category_id], name: :index_adverts_on_title_and_category_id
599
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
600
- "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
601
591
  EOS
602
592
  )
603
593
 
@@ -627,16 +617,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
627
617
  rename_table :adverts, :ads
628
618
  add_column :ads, :title, :string, limit: 250, null: true#{charset_and_collation}
629
619
  add_column :ads, :body, :text#{', limit: 4294967295' if defined?(Mysql2)}, null: true#{charset_and_collation}
630
- #{if defined?(Mysql2)
631
- "add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
632
- "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id"
633
- end}
634
- EOS
620
+ EOS
635
621
  .and migrate_down(<<~EOS.strip)
636
- #{if defined?(Mysql2)
637
- "remove_foreign_key :adverts, name: :index_adverts_on_c_id\n" +
638
- "remove_foreign_key :adverts, name: :index_adverts_on_category_id"
639
- end}
640
622
  remove_column :ads, :body
641
623
  remove_column :ads, :title
642
624
  rename_table :ads, :adverts
@@ -21,9 +21,9 @@ RSpec.describe DeclareSchema::Model::ForeignKeyDefinition do
21
21
  describe 'instance methods' do
22
22
  let(:connection) { instance_double(ActiveRecord::Base.connection.class) }
23
23
  let(:model) { instance_double('Model', table_name: 'models', connection: connection) }
24
- let(:foreign_key) { :network_id }
25
- let(:options) { {} }
26
- subject { described_class.new(model, foreign_key, **options)}
24
+ let(:foreign_key_column) { :network_id }
25
+ let(:options) { { child_table_name: 'advertisers' } }
26
+ subject { described_class.new(foreign_key_column, **options)}
27
27
 
28
28
  before do
29
29
  allow(model.connection).to receive(:index_name).with(any_args) { 'index_on_network_id' }
@@ -31,63 +31,64 @@ RSpec.describe DeclareSchema::Model::ForeignKeyDefinition do
31
31
 
32
32
  describe '#initialize' do
33
33
  it 'normalizes symbols to strings' do
34
- expect(subject.foreign_key).to eq('network_id')
34
+ expect(subject.foreign_key_column).to eq('network_id')
35
35
  expect(subject.parent_table_name).to eq('networks')
36
36
  end
37
37
 
38
38
  context 'when most options passed' do
39
- let(:options) { { parent_table: :networks, foreign_key: :the_network_id } }
39
+ let(:options) { { child_table_name: 'advertisers', parent_class_name: 'Network' } }
40
40
 
41
41
  it 'normalizes symbols to strings' do
42
- expect(subject.foreign_key).to eq('network_id')
43
- expect(subject.foreign_key_name).to eq('the_network_id')
42
+ expect(subject.foreign_key_column).to eq('network_id')
44
43
  expect(subject.parent_table_name).to eq('networks')
45
- expect(subject.foreign_key).to eq('network_id')
46
- expect(subject.constraint_name).to eq('index_on_network_id')
47
- expect(subject.on_delete_cascade).to be_falsey
44
+ expect(subject.foreign_key_column).to eq('network_id')
45
+ expect(subject.constraint_name).to eq('index_advertisers_on_network_id')
46
+ expect(subject.dependent).to be_nil
48
47
  end
49
48
  end
50
49
 
51
50
  context 'when all options passed' do
52
- let(:options) { { parent_table: :networks, foreign_key: :the_network_id, constraint_name: :constraint_1, dependent: :delete } }
51
+ let(:options) { { child_table_name: 'advertisers', parent_class_name: 'Network', constraint_name: :constraint_1, dependent: :delete } }
53
52
 
54
53
  it 'normalizes symbols to strings' do
55
- expect(subject.foreign_key).to eq('network_id')
56
- expect(subject.foreign_key_name).to eq('the_network_id')
54
+ expect(subject.foreign_key_column).to eq('network_id')
57
55
  expect(subject.parent_table_name).to eq('networks')
58
56
  expect(subject.constraint_name).to eq('constraint_1')
59
- expect(subject.on_delete_cascade).to be_truthy
57
+ expect(subject.dependent).to eq(:delete)
60
58
  end
61
59
  end
62
60
 
63
61
  describe `#<=>` do
62
+ let(:foreign_key_column) { :the_network_id }
63
+
64
64
  context 'when class name not passed' do
65
- let(:options) { { foreign_key: :the_network_id, constraint_name: :constraint_1, dependent: :delete } }
65
+ let(:options) { { child_table_name: 'advertisers', constraint_name: :constraint_1, dependent: :delete } }
66
66
 
67
- it 'compares equal without requring the parent class' do
67
+ it 'compares equal without requiring the parent class' do
68
68
  expect(subject <=> subject).to eq(0)
69
69
  end
70
70
  end
71
71
 
72
72
  context 'when class name passed' do
73
- let(:options) { { foreign_key: :the_network_id, class_name: 'TheNetwork', constraint_name: :constraint_1 } }
73
+ let(:options) { { child_table_name: 'advertisers', parent_class_name: 'TheNetwork', constraint_name: :constraint_1 } }
74
74
 
75
- it 'compares equal without requring the parent class' do
75
+ it 'compares equal without requiring the parent class' do
76
76
  expect(subject <=> subject).to eq(0)
77
77
  end
78
78
  end
79
79
  end
80
80
 
81
81
  context 'when constraint name passed as empty string' do
82
- let(:options) { { constraint_name: "" } }
82
+ let(:options) { { child_table_name: 'advertisers', constraint_name: "" } }
83
+
83
84
  it 'defaults to rails constraint name' do
84
- expect(subject.constraint_name).to eq("index_on_network_id")
85
+ expect(subject.constraint_name).to eq("index_advertisers_on_network_id")
85
86
  end
86
87
  end
87
88
 
88
89
  context 'when no constraint name passed' do
89
90
  it 'defaults to rails constraint name' do
90
- expect(subject.constraint_name).to eq("index_on_network_id")
91
+ expect(subject.constraint_name).to eq("index_advertisers_on_network_id")
91
92
  end
92
93
  end
93
94
  end
@@ -103,13 +104,13 @@ RSpec.describe DeclareSchema::Model::ForeignKeyDefinition do
103
104
  allow(connection).to receive(:index_name).with('models', column: 'network_id') { }
104
105
  end
105
106
 
106
- describe '.for_model' do
107
- subject { described_class.for_model(model, old_table_name) }
107
+ describe '.for_table' do
108
+ subject { described_class.for_table(old_table_name, model.connection) }
108
109
 
109
- it 'returns new object' do
110
- expect(subject.size).to eq(1), subject.inspect
111
- expect(subject.first).to be_kind_of(described_class)
112
- expect(subject.first.foreign_key).to eq('network_id')
110
+ it 'returns definitions' do
111
+ expect(subject.map(&:key)).to eq([
112
+ ["networks", "network_id", nil]
113
+ ])
113
114
  end
114
115
  end
115
116
  end
@@ -8,19 +8,19 @@ end
8
8
  require_relative '../../../../lib/declare_schema/model/habtm_model_shim'
9
9
 
10
10
  RSpec.describe DeclareSchema::Model::HabtmModelShim do
11
- let(:join_table) { "parent_1_parent_2" }
12
- let(:foreign_keys) { ["parent_1_id", "parent_2_id"] }
13
- let(:foreign_key_classes) { [Parent1, Parent2] }
11
+ let(:join_table) { "customers_users" }
12
+ let(:foreign_keys) { ["user_id", "customer_id"] }
13
+ let(:parent_table_names) { ["users", "customers"] }
14
14
 
15
15
  before do
16
16
  load File.expand_path('../prepare_testapp.rb', __dir__)
17
17
 
18
- class Parent1 < ActiveRecord::Base
19
- self.table_name = "parent_1s"
18
+ class User < ActiveRecord::Base
19
+ self.table_name = "users"
20
20
  end
21
21
 
22
- class Parent2 < ActiveRecord::Base
23
- self.table_name = "parent_2s"
22
+ class Customer < ActiveRecord::Base
23
+ self.table_name = "customers"
24
24
  end
25
25
  end
26
26
 
@@ -29,12 +29,14 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
29
29
  let(:reflection) { double("reflection", join_table: join_table,
30
30
  foreign_key: foreign_keys.first,
31
31
  association_foreign_key: foreign_keys.last,
32
- active_record: foreign_key_classes.first,
33
- class_name: 'Parent1') }
32
+ active_record: User,
33
+ class_name: 'Customer') }
34
34
  it 'returns a new object' do
35
35
  result = described_class.from_reflection(reflection)
36
36
 
37
37
  expect(result).to be_a(described_class)
38
+ expect(result.foreign_keys).to eq(foreign_keys.reverse)
39
+ expect(result.parent_table_names).to eq(parent_table_names.reverse)
38
40
  end
39
41
  end
40
42
  end
@@ -42,14 +44,12 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
42
44
  describe 'instance methods' do
43
45
  let(:connection) { instance_double(ActiveRecord::Base.connection.class, "connection") }
44
46
 
45
- subject { described_class.new(join_table, foreign_keys, foreign_key_classes, connection) }
47
+ subject { described_class.new(join_table, foreign_keys, parent_table_names) }
46
48
 
47
49
  describe '#initialize' do
48
50
  it 'stores initialization attributes' do
49
51
  expect(subject.join_table).to eq(join_table)
50
- expect(subject.foreign_keys).to eq(foreign_keys)
51
- expect(subject.foreign_key_classes).to be(foreign_key_classes)
52
- expect(subject.connection).to be(connection)
52
+ expect(subject.foreign_keys).to eq(foreign_keys.reverse)
53
53
  end
54
54
  end
55
55
 
@@ -67,20 +67,20 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
67
67
 
68
68
  describe '#field_specs' do
69
69
  it 'returns 2 field specs' do
70
- result = subject.field_specs
71
- expect(result.size).to eq(2), result.inspect
72
-
73
- expect(result[foreign_keys.first]).to be_a(::DeclareSchema::Model::FieldSpec)
74
- expect(result[foreign_keys.first].model).to eq(subject)
75
- expect(result[foreign_keys.first].name.to_s).to eq(foreign_keys.first)
76
- expect(result[foreign_keys.first].type).to eq(:integer)
77
- expect(result[foreign_keys.first].position).to eq(0)
78
-
79
- expect(result[foreign_keys.last]).to be_a(::DeclareSchema::Model::FieldSpec)
80
- expect(result[foreign_keys.last].model).to eq(subject)
81
- expect(result[foreign_keys.last].name.to_s).to eq(foreign_keys.last)
82
- expect(result[foreign_keys.last].type).to eq(:integer)
83
- expect(result[foreign_keys.last].position).to eq(1)
70
+ field_specs = subject.field_specs
71
+ expect(field_specs.size).to eq(2), field_specs.inspect
72
+
73
+ expect(field_specs[foreign_keys.first]).to be_a(::DeclareSchema::Model::FieldSpec)
74
+ expect(field_specs[foreign_keys.first].model).to eq(subject)
75
+ expect(field_specs[foreign_keys.first].name.to_s).to eq(foreign_keys.first)
76
+ expect(field_specs[foreign_keys.first].type).to eq(:integer)
77
+ expect(field_specs[foreign_keys.first].position).to eq(1)
78
+
79
+ expect(field_specs[foreign_keys.last]).to be_a(::DeclareSchema::Model::FieldSpec)
80
+ expect(field_specs[foreign_keys.last].model).to eq(subject)
81
+ expect(field_specs[foreign_keys.last].name.to_s).to eq(foreign_keys.last)
82
+ expect(field_specs[foreign_keys.last].type).to eq(:integer)
83
+ expect(field_specs[foreign_keys.last].position).to eq(0)
84
84
  end
85
85
  end
86
86
 
@@ -98,20 +98,21 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
98
98
 
99
99
  describe '#index_definitions_with_primary_key' do
100
100
  it 'returns 2 index definitions' do
101
- result = subject.index_definitions_with_primary_key
102
- expect(result.size).to eq(2), result.inspect
101
+ index_definitions = subject.index_definitions_with_primary_key
102
+ expect(index_definitions.size).to eq(2), index_definitions.inspect
103
103
 
104
- expect(result.first).to be_a(::DeclareSchema::Model::IndexDefinition)
105
- expect(result.first.name).to eq('PRIMARY')
106
- expect(result.first.fields).to eq(['parent_1_id', 'parent_2_id'])
107
- expect(result.first.unique).to be_truthy
104
+ expect(index_definitions.first).to be_a(::DeclareSchema::Model::IndexDefinition)
105
+ expect(index_definitions.first.name).to eq('PRIMARY')
106
+ expect(index_definitions.first.fields).to eq(foreign_keys.reverse)
107
+ expect(index_definitions.first.unique).to be_truthy
108
108
  end
109
109
  end
110
110
 
111
111
  context 'when table and foreign key names are long' do
112
112
  let(:join_table) { "advertiser_campaigns_tracking_pixels" }
113
- let(:foreign_keys) { ['advertiser_campaign', 'tracking_pixel'] }
114
- let(:foreign_key_classes) { [Table1, Table2] }
113
+ let(:foreign_keys_and_table_names) { [["advertiser_id", "advertisers"], ["campaign_id", "campaigns"]] }
114
+ let(:foreign_keys) { foreign_keys_and_table_names.map(&:first) }
115
+ let(:parent_table_names) { foreign_keys_and_table_names.map(&:last) }
115
116
 
116
117
  before do
117
118
  class Table1 < ActiveRecord::Base
@@ -124,19 +125,23 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
124
125
  end
125
126
 
126
127
  it 'returns two index definitions and does not raise a IndexNameTooLongError' do
127
- result = subject.index_definitions_with_primary_key
128
- expect(result.size).to eq(2), result.inspect
129
- expect(result.first).to be_a(::DeclareSchema::Model::IndexDefinition)
130
- expect(result.first.name).to eq('PRIMARY')
131
- expect(result.first.fields).to eq(['advertiser_campaign', 'tracking_pixel'])
132
- expect(result.first.unique).to be_truthy
128
+ indexes = subject.index_definitions_with_primary_key.to_a
129
+ expect(indexes.size).to eq(2), indexes.inspect
130
+ expect(indexes.first).to be_a(::DeclareSchema::Model::IndexDefinition)
131
+ expect(indexes.first.name).to eq('PRIMARY')
132
+ expect(indexes.first.fields).to eq(foreign_keys)
133
+ expect(indexes.first.unique).to be_truthy
134
+ expect(indexes.last).to be_a(::DeclareSchema::Model::IndexDefinition)
135
+ expect(indexes.last.name).to eq('index_advertiser_campaigns_tracking_pixels_on_campaign_id')
136
+ expect(indexes.last.fields).to eq([foreign_keys.last])
137
+ expect(indexes.last.unique).to be_falsey
133
138
  end
134
139
  end
135
140
 
136
141
  describe '#index_definitions' do
137
142
  it 'returns index_definitions_with_primary_key' do
138
- result = subject.index_definitions
139
- expect(result.size).to eq(2), result.inspect
143
+ indexes = subject.index_definitions
144
+ expect(indexes.size).to eq(2), indexes.inspect
140
145
  end
141
146
  end
142
147
 
@@ -146,21 +151,13 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
146
151
  end
147
152
  end
148
153
 
149
- describe '#constraint_specs' do
154
+ describe '#constraint_definitions' do
150
155
  it 'returns 2 foreign keys' do
151
- result = subject.constraint_specs
152
- expect(result.size).to eq(2), result.inspect
153
-
154
- expect(result.first).to be_a(::DeclareSchema::Model::ForeignKeyDefinition)
155
- expect(result.first.foreign_key).to eq(foreign_keys.first)
156
- expect(result.first.parent_table_name).to be(Parent1.table_name)
157
- expect(result.first.on_delete_cascade).to be_truthy
158
-
159
- sample = result.to_a.last
160
- expect(sample).to be_a(::DeclareSchema::Model::ForeignKeyDefinition)
161
- expect(sample.foreign_key).to eq(foreign_keys.last)
162
- expect(sample.parent_table_name).to be(Parent2.table_name)
163
- expect(sample.on_delete_cascade).to be_truthy
156
+ constraints = subject.constraint_definitions
157
+ expect(constraints.map(&:key)).to eq([
158
+ ["customers_users", "customer_id", :delete],
159
+ ["customers_users", "user_id", :delete]
160
+ ])
164
161
  end
165
162
  end
166
163
  end