declare_schema 1.4.0.colin.7 → 1.4.0.colin.9
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/Gemfile.lock +1 -1
- data/README.md +4 -1
- data/lib/declare_schema/model/field_spec.rb +2 -0
- data/lib/declare_schema/model/foreign_key_definition.rb +39 -43
- data/lib/declare_schema/model/habtm_model_shim.rb +30 -30
- data/lib/declare_schema/model/index_definition.rb +51 -30
- data/lib/declare_schema/model/table_options_definition.rb +11 -1
- data/lib/declare_schema/model.rb +55 -42
- data/lib/declare_schema/version.rb +1 -1
- data/lib/declare_schema.rb +34 -2
- data/lib/generators/declare_schema/migration/migrator.rb +32 -21
- data/spec/lib/declare_schema/field_spec_spec.rb +22 -2
- data/spec/lib/declare_schema/migration_generator_spec.rb +63 -41
- data/spec/lib/declare_schema/model/foreign_key_definition_spec.rb +28 -27
- data/spec/lib/declare_schema/model/habtm_model_shim_spec.rb +84 -62
- data/spec/lib/declare_schema/model/index_definition_spec.rb +126 -55
- data/spec/lib/declare_schema/model/table_options_definition_spec.rb +26 -6
- data/spec/lib/declare_schema_spec.rb +62 -8
- data/spec/lib/generators/declare_schema/migration/migrator_spec.rb +3 -1
- data/spec/spec_helper.rb +4 -3
- metadata +2 -2
data/lib/declare_schema.rb
CHANGED
@@ -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
|
@@ -203,8 +204,8 @@ module Generators
|
|
203
204
|
end
|
204
205
|
end
|
205
206
|
# generate shims for HABTM models
|
206
|
-
habtm_tables.each do |name,
|
207
|
-
models_by_table_name[name] = ::DeclareSchema::Model::HabtmModelShim.from_reflection(
|
207
|
+
habtm_tables.each do |name, reflections|
|
208
|
+
models_by_table_name[name] = ::DeclareSchema::Model::HabtmModelShim.from_reflection(reflections.first)
|
208
209
|
end
|
209
210
|
model_table_names = models_by_table_name.keys
|
210
211
|
|
@@ -221,8 +222,8 @@ module Generators
|
|
221
222
|
::DeclareSchema::SchemaChange::TableRemove.new(t, add_table_back(t))
|
222
223
|
end
|
223
224
|
|
224
|
-
creates = to_create.map do |
|
225
|
-
model = models_by_table_name[
|
225
|
+
creates = to_create.map do |table_name|
|
226
|
+
model = models_by_table_name[table_name]
|
226
227
|
disable_auto_increment = model.try(:disable_auto_increment)
|
227
228
|
|
228
229
|
primary_key_definition =
|
@@ -239,10 +240,13 @@ module Generators
|
|
239
240
|
table_options_definition = ::DeclareSchema::Model::TableOptionsDefinition.new(model.table_name, **table_options_for_model(model))
|
240
241
|
table_options = create_table_options(model, disable_auto_increment)
|
241
242
|
|
242
|
-
table_add = ::DeclareSchema::SchemaChange::TableAdd.new(
|
243
|
-
|
244
|
-
|
245
|
-
|
243
|
+
table_add = ::DeclareSchema::SchemaChange::TableAdd.new(
|
244
|
+
table_name,
|
245
|
+
primary_key_definition + field_definitions,
|
246
|
+
table_options,
|
247
|
+
sql_options: table_options_definition.settings
|
248
|
+
)
|
249
|
+
|
246
250
|
[
|
247
251
|
table_add,
|
248
252
|
*Array((create_indexes(model) if ::DeclareSchema.default_generate_indexing)),
|
@@ -255,9 +259,9 @@ module Generators
|
|
255
259
|
fk_changes = []
|
256
260
|
table_options_changes = []
|
257
261
|
|
258
|
-
to_change.each do |
|
259
|
-
model = models_by_table_name[
|
260
|
-
table = to_rename.key(
|
262
|
+
to_change.each do |table_name|
|
263
|
+
model = models_by_table_name[table_name]
|
264
|
+
table = to_rename.key(table_name) || model.table_name
|
261
265
|
if table.in?(db_tables)
|
262
266
|
change, index_change, fk_change, table_options_change = change_table(model, table)
|
263
267
|
changes << change
|
@@ -311,6 +315,8 @@ module Generators
|
|
311
315
|
{ id: false }
|
312
316
|
elsif primary_key == "id"
|
313
317
|
{ id: :bigint }
|
318
|
+
elsif primary_key.is_a?(Array)
|
319
|
+
{ primary_key: primary_key.map(&:to_sym) }
|
314
320
|
else
|
315
321
|
{ primary_key: primary_key.to_sym }
|
316
322
|
end.merge(model._table_options)
|
@@ -335,9 +341,9 @@ module Generators
|
|
335
341
|
end
|
336
342
|
|
337
343
|
def create_constraints(model)
|
338
|
-
model.
|
344
|
+
model.constraint_definitions.map do |fk|
|
339
345
|
::DeclareSchema::SchemaChange::ForeignKeyAdd.new(fk.child_table_name, fk.parent_table_name,
|
340
|
-
column_name: fk.
|
346
|
+
column_name: fk.foreign_key_column, name: fk.constraint_name)
|
341
347
|
end
|
342
348
|
end
|
343
349
|
|
@@ -425,7 +431,7 @@ module Generators
|
|
425
431
|
::DeclareSchema.default_generate_indexing or return []
|
426
432
|
|
427
433
|
new_table_name = model.table_name
|
428
|
-
existing_indexes = ::DeclareSchema::Model::IndexDefinition.
|
434
|
+
existing_indexes = ::DeclareSchema::Model::IndexDefinition.for_table(old_table_name || new_table_name, model.ignore_indexes, model.connection)
|
429
435
|
model_indexes_with_equivalents = model.index_definitions_with_primary_key.to_a
|
430
436
|
model_indexes = model_indexes_with_equivalents.map do |i|
|
431
437
|
if i.explicit_name.nil?
|
@@ -485,8 +491,12 @@ module Generators
|
|
485
491
|
ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/) and raise ArgumentError, 'SQLite does not support foreign keys'
|
486
492
|
::DeclareSchema.default_generate_foreign_keys or return []
|
487
493
|
|
488
|
-
|
489
|
-
|
494
|
+
if model.is_a?(::DeclareSchema::Model::HabtmModelShim)
|
495
|
+
force_dependent_delete = :delete
|
496
|
+
end
|
497
|
+
|
498
|
+
existing_fks = ::DeclareSchema::Model::ForeignKeyDefinition.for_table(old_table_name || model.table_name, model.connection, dependent: force_dependent_delete)
|
499
|
+
model_fks = model.constraint_definitions.to_a
|
490
500
|
|
491
501
|
fks_to_drop = existing_fks - model_fks
|
492
502
|
fks_to_add = model_fks - existing_fks
|
@@ -495,13 +505,13 @@ module Generators
|
|
495
505
|
|
496
506
|
drop_fks = (fks_to_drop - renamed_fks_to_drop).map do |fk|
|
497
507
|
::DeclareSchema::SchemaChange::ForeignKeyRemove.new(fk.child_table_name, fk.parent_table_name,
|
498
|
-
column_name: fk.
|
508
|
+
column_name: fk.foreign_key_column, name: fk.constraint_name)
|
499
509
|
end
|
500
510
|
|
501
511
|
add_fks = (fks_to_add - renamed_fks_to_add).map do |fk|
|
502
512
|
# next if fk.parent.constantize.abstract_class || fk.parent == fk.model.class_name
|
503
513
|
::DeclareSchema::SchemaChange::ForeignKeyAdd.new(fk.child_table_name, fk.parent_table_name,
|
504
|
-
column_name: fk.
|
514
|
+
column_name: fk.foreign_key_column, name: fk.constraint_name)
|
505
515
|
end
|
506
516
|
|
507
517
|
[drop_fks + add_fks]
|
@@ -523,9 +533,8 @@ module Generators
|
|
523
533
|
end
|
524
534
|
|
525
535
|
def fk_field_options(model, field_name)
|
526
|
-
foreign_key = model.
|
527
|
-
|
528
|
-
parent_columns = connection.columns(parent_table) rescue []
|
536
|
+
if (foreign_key = model.constraint_definitions.find { |fk| field_name == fk.foreign_key_column })
|
537
|
+
parent_columns = connection.columns(foreign_key.parent_table_name) rescue []
|
529
538
|
pk_limit =
|
530
539
|
if (pk_column = parent_columns.find { |column| column.name.to_s == "id" }) # right now foreign keys assume id is the target
|
531
540
|
pk_column.limit
|
@@ -585,6 +594,8 @@ module Generators
|
|
585
594
|
case charset
|
586
595
|
when "utf8"
|
587
596
|
"utf8_general_ci"
|
597
|
+
when "utf8mb3"
|
598
|
+
"utf8mb3_general_ci"
|
588
599
|
when "utf8mb4"
|
589
600
|
"utf8mb4_general_ci"
|
590
601
|
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
|
-
|
62
|
-
|
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,17 +404,17 @@ 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
|
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
|
|
416
|
-
expect(
|
417
|
+
expect(ActiveSupport::Deprecation).to receive(:warn).with(/belongs_to :category, index: 'name' is deprecated; use index: \{ name: 'name' \} instead/i)
|
417
418
|
|
418
419
|
class Category < ActiveRecord::Base; end
|
419
420
|
class Advert < ActiveRecord::Base
|
@@ -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: :
|
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
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
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
|
@@ -490,8 +487,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
490
487
|
|
491
488
|
# You can add an index to a field definition
|
492
489
|
|
493
|
-
expect(
|
494
|
-
expect(
|
490
|
+
expect(ActiveSupport::Deprecation).to receive(:warn).with(/belongs_to :category, index: 'name' is deprecated; use index: \{ name: 'name' \} instead/i)
|
491
|
+
expect(ActiveSupport::Deprecation).to receive(:warn).with(/belongs_to :category, unique: true\|false is deprecated; use index: \{ unique: true\|false \} instead/i)
|
495
492
|
|
496
493
|
class Advert < ActiveRecord::Base
|
497
494
|
declare_schema do
|
@@ -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: :
|
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
|
|
@@ -578,9 +572,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
578
572
|
expect(Generators::DeclareSchema::Migration::Migrator.run).to(
|
579
573
|
migrate_up(<<~EOS.strip)
|
580
574
|
add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
|
581
|
-
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)}
|
575
|
+
add_index :adverts, [:title], name: :my_index, length: { title: 10 }
|
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
|
-
|
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
|
@@ -838,6 +820,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
838
820
|
expect(User.field_specs.keys).to eq(['company'])
|
839
821
|
expect(User.field_specs['company'].options[:ruby_default]&.call).to eq("BigCorp")
|
840
822
|
|
823
|
+
nuke_model_class(User)
|
824
|
+
|
841
825
|
## validates
|
842
826
|
|
843
827
|
# DeclareSchema can accept a validates hash in the field options.
|
@@ -858,6 +842,44 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
858
842
|
up, _down = Generators::DeclareSchema::Migration::Migrator.run
|
859
843
|
ActiveRecord::Migration.class_eval(up)
|
860
844
|
expect(Ad.field_specs['company'].options[:validates].inspect).to eq("{:presence=>true, :uniqueness=>{:case_sensitive=>false}}")
|
845
|
+
|
846
|
+
# DeclareSchema supports has_and_belongs_to_many relationships and generates the intersection ("join") table
|
847
|
+
# with appropriate primary key, indexes, and foreign keys.
|
848
|
+
|
849
|
+
class Advertiser < ActiveRecord::Base
|
850
|
+
declare_schema do
|
851
|
+
string :name, limit: 250
|
852
|
+
end
|
853
|
+
has_and_belongs_to_many :creatives
|
854
|
+
end
|
855
|
+
class Creative < ActiveRecord::Base
|
856
|
+
declare_schema do
|
857
|
+
string :url, limit: 500
|
858
|
+
end
|
859
|
+
has_and_belongs_to_many :advertisers
|
860
|
+
end
|
861
|
+
|
862
|
+
expect(Generators::DeclareSchema::Migration::Migrator.run).to(
|
863
|
+
migrate_up(<<~EOS.strip)
|
864
|
+
create_table :advertisers, id: :bigint#{create_table_charset_and_collation} do |t|
|
865
|
+
t.string :name, limit: 250, null: false#{charset_and_collation}
|
866
|
+
end
|
867
|
+
create_table :advertisers_creatives, primary_key: [:advertiser_id, :creative_id]#{create_table_charset_and_collation} do |t|
|
868
|
+
t.integer :advertiser_id, limit: 8, null: false
|
869
|
+
t.integer :creative_id, limit: 8, null: false
|
870
|
+
end
|
871
|
+
create_table :creatives, id: :bigint#{create_table_charset_and_collation} do |t|
|
872
|
+
t.string :url, limit: 500, null: false#{charset_and_collation}
|
873
|
+
end
|
874
|
+
add_index :advertisers_creatives, [:creative_id], name: :index_advertisers_creatives_on_creative_id
|
875
|
+
add_foreign_key :advertisers_creatives, :advertisers, column: :advertiser_id, name: :advertisers_creatives_FK1
|
876
|
+
add_foreign_key :advertisers_creatives, :creatives, column: :creative_id, name: :advertisers_creatives_FK2
|
877
|
+
EOS
|
878
|
+
)
|
879
|
+
|
880
|
+
nuke_model_class(Ad)
|
881
|
+
nuke_model_class(Advertiser)
|
882
|
+
nuke_model_class(Creative)
|
861
883
|
end
|
862
884
|
|
863
885
|
context 'models with the same parent foreign key relation' do
|
@@ -881,7 +903,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
881
903
|
end
|
882
904
|
end
|
883
905
|
|
884
|
-
it 'will
|
906
|
+
it 'will generate unique constraint names' do
|
885
907
|
expect(Generators::DeclareSchema::Migration::Migrator.run).to(
|
886
908
|
migrate_up(<<~EOS.strip)
|
887
909
|
create_table :categories, id: :bigint, options: "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin" do |t|
|
@@ -1178,7 +1200,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
1178
1200
|
end
|
1179
1201
|
|
1180
1202
|
it 'deprecates limit:' do
|
1181
|
-
expect(ActiveSupport::Deprecation).to receive(:warn).with("belongs_to limit: is deprecated since it is now inferred")
|
1203
|
+
expect(ActiveSupport::Deprecation).to receive(:warn).with("belongs_to :ad_category, limit: is deprecated since it is now inferred")
|
1182
1204
|
eval <<~EOS
|
1183
1205
|
class UsingLimit < ActiveRecord::Base
|
1184
1206
|
declare_schema { }
|
@@ -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(:
|
25
|
-
let(:options) { {} }
|
26
|
-
subject { described_class.new(
|
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.
|
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) { {
|
39
|
+
let(:options) { { child_table_name: 'advertisers', parent_class_name: 'Network' } }
|
40
40
|
|
41
41
|
it 'normalizes symbols to strings' do
|
42
|
-
expect(subject.
|
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.
|
46
|
-
expect(subject.constraint_name).to eq('
|
47
|
-
expect(subject.
|
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) { {
|
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.
|
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.
|
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) { {
|
65
|
+
let(:options) { { child_table_name: 'advertisers', constraint_name: :constraint_1, dependent: :delete } }
|
66
66
|
|
67
|
-
it 'compares equal without
|
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) { {
|
73
|
+
let(:options) { { child_table_name: 'advertisers', parent_class_name: 'TheNetwork', constraint_name: :constraint_1 } }
|
74
74
|
|
75
|
-
it 'compares equal without
|
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("
|
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("
|
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 '.
|
107
|
-
subject { described_class.
|
107
|
+
describe '.for_table' do
|
108
|
+
subject { described_class.for_table(old_table_name, model.connection) }
|
108
109
|
|
109
|
-
it 'returns
|
110
|
-
expect(subject.
|
111
|
-
|
112
|
-
|
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
|