declare_schema 1.4.0.colin.6 → 1.4.0.colin.8

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.
@@ -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,8 +426,8 @@ 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
- model_indexes_with_equivalents = model.index_definitions_with_primary_key
429
+ existing_indexes = ::DeclareSchema::Model::IndexDefinition.for_table(old_table_name || new_table_name, model.ignore_indexes, model.connection)
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?
432
433
  if (existing = existing_indexes.find { |e| i != e && e.equivalent?(i) })
@@ -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
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