declare_schema 0.10.1 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +16 -6
  5. data/lib/declare_schema.rb +12 -1
  6. data/lib/declare_schema/extensions/active_record/fields_declaration.rb +4 -2
  7. data/lib/declare_schema/model.rb +59 -15
  8. data/lib/declare_schema/model/column.rb +2 -2
  9. data/lib/declare_schema/model/field_spec.rb +4 -4
  10. data/lib/declare_schema/model/foreign_key_definition.rb +2 -3
  11. data/lib/declare_schema/model/habtm_model_shim.rb +2 -2
  12. data/lib/declare_schema/model/index_definition.rb +8 -6
  13. data/lib/declare_schema/schema_change/column_add.rb +3 -3
  14. data/lib/declare_schema/version.rb +1 -1
  15. data/lib/generators/declare_schema/migration/USAGE +14 -24
  16. data/lib/generators/declare_schema/migration/migration_generator.rb +40 -38
  17. data/lib/generators/declare_schema/migration/migrator.rb +18 -14
  18. data/lib/generators/declare_schema/migration/templates/migration.rb.erb +3 -3
  19. data/spec/lib/declare_schema/api_spec.rb +2 -4
  20. data/spec/lib/declare_schema/field_spec_spec.rb +3 -3
  21. data/spec/lib/declare_schema/generator_spec.rb +2 -2
  22. data/spec/lib/declare_schema/interactive_primary_key_spec.rb +8 -11
  23. data/spec/lib/declare_schema/migration_generator_spec.rb +352 -130
  24. data/spec/lib/declare_schema/model/column_spec.rb +2 -6
  25. data/spec/lib/declare_schema/model/foreign_key_definition_spec.rb +32 -8
  26. data/spec/lib/declare_schema/model/habtm_model_shim_spec.rb +4 -6
  27. data/spec/lib/declare_schema/model/index_definition_spec.rb +4 -4
  28. data/spec/spec_helper.rb +1 -1
  29. metadata +2 -2
@@ -1,47 +1,37 @@
1
1
  Description:
2
2
 
3
3
  This generator compares your existing schema against the
4
- schema declared inside your fields declarations in your
5
- models.
4
+ schema declared inside your declare_schema do ... end
5
+ declarations in your models.
6
6
 
7
7
  If the generator finds differences, it will display the
8
- migration it has created, and ask you if you wish to
9
- [g]enerate migration, generate and [m]igrate now or [c]ancel?
10
- Enter "g" to just generate the migration but do not run it.
11
- Enter "m" to generate the migration and run it, or press "c"
12
- to do nothing.
13
-
14
- The generator will then prompt you for the migration name,
8
+ migration it has created, and prompt you for the migration name,
15
9
  supplying a numbered default name.
16
10
 
17
11
  The generator is conservative and will prompt you to resolve
18
12
  any ambiguities.
19
13
 
20
- Examples:
14
+ Example:
21
15
 
22
- $ rails generate declare_schema:migration
16
+ $ bundle exec rails generate declare_schema:migration
23
17
 
24
18
  ---------- Up Migration ----------
25
- create_table :foos do |t|
19
+ create_table :users do |t|
20
+ t.string :first_name, limit: 50
21
+ t.string :last_name, limit: 50
26
22
  t.datetime :created_at
27
23
  t.datetime :updated_at
28
24
  end
29
25
  ----------------------------------
30
26
 
31
27
  ---------- Down Migration --------
32
- drop_table :foos
28
+ drop_table :users
33
29
  ----------------------------------
34
- What now: [g]enerate migration, generate and [m]igrate now or [c]ancel? m
35
-
36
- Migration filename:
37
- (you can type spaces instead of '_' -- every little helps)
38
- Filename [declare_schema_migration_2]: create_foo
30
+ Migration filename: (spaces will be converted to _) [declare_schema_migration_2]: create users
39
31
  exists db/migrate
40
- create db/migrate/20091023183838_create_foo.rb
41
- (in /work/foo)
42
- == CreateFoo: migrating ======================================================
43
- -- create_table(:yos)
44
- -> 0.0856s
45
- == CreateFoo: migrated (0.0858s) =============================================
32
+ create db/migrate/20091023183838_create_users.rb
33
+
34
+ Not running migration since --migrate not given. When you are ready, run:
46
35
 
36
+ bundle exec rails db:migrate
47
37
 
@@ -38,20 +38,18 @@ module DeclareSchema
38
38
  type: :boolean,
39
39
  desc: "Don't prompt for a migration name - just pick one"
40
40
 
41
- class_option :generate,
42
- aliases: '-g',
43
- type: :boolean,
44
- desc: "Don't prompt for action - generate the migration"
45
-
46
41
  class_option :migrate,
47
42
  aliases: '-m',
48
43
  type: :boolean,
49
- desc: "Don't prompt for action - generate and migrate"
44
+ desc: "After generating migration, run it"
50
45
 
51
46
  def migrate
52
47
  return if migrations_pending?
53
48
 
54
- generator = Generators::DeclareSchema::Migration::Migrator.new(->(c, d, k, p) { extract_renames!(c, d, k, p) })
49
+ generator = Generators::DeclareSchema::Migration::Migrator.new do |to_create, to_drop, kind_str, name_prefix|
50
+ extract_renames!(to_create, to_drop, kind_str, name_prefix)
51
+ end
52
+
55
53
  up, down = generator.generate
56
54
 
57
55
  if up.blank?
@@ -67,34 +65,31 @@ module DeclareSchema
67
65
  say down
68
66
  say "----------------------------------"
69
67
 
70
- action = options[:generate] && 'g' ||
71
- options[:migrate] && 'm' ||
72
- choose("\nWhat now: [g]enerate migration, generate and [m]igrate now or [c]ancel?", /^(g|m|c)$/)
73
-
74
- if action != 'c'
75
- if name.blank? && !options[:default_name]
76
- final_migration_name = choose("\nMigration filename: [<enter>=#{migration_name}|<custom_name>]:", /^[a-z0-9_ ]*$/, migration_name).strip.gsub(' ', '_')
77
- end
78
- final_migration_name = migration_name if final_migration_name.blank?
79
-
80
- up.gsub!("\n", "\n ")
81
- up.gsub!(/ +\n/, "\n")
82
- down.gsub!("\n", "\n ")
83
- down.gsub!(/ +\n/, "\n")
84
-
85
- @up = up
86
- @down = down
87
- @migration_class_name = final_migration_name.camelize
88
-
89
- migration_template('migration.rb.erb', "db/migrate/#{final_migration_name.underscore}.rb")
90
- if action == 'm'
91
- case Rails::VERSION::MAJOR
92
- when 4
93
- rake('db:migrate')
94
- else
95
- rails_command('db:migrate')
96
- end
68
+ final_migration_name =
69
+ name.presence ||
70
+ if !options[:default_name]
71
+ choose("\nMigration filename (spaces will be converted to _) [#{default_migration_name}]:", /^[a-z0-9_ ]*$/,
72
+ default_migration_name).strip.gsub(' ', '_').presence
73
+ end ||
74
+ default_migration_name
75
+
76
+ @up = indent(up.strip, 4)
77
+ @down = indent(down.strip, 4)
78
+ @migration_class_name = final_migration_name.camelize
79
+
80
+ migration_template('migration.rb.erb', "db/migrate/#{final_migration_name.underscore}.rb")
81
+
82
+ db_migrate_command = ::DeclareSchema.db_migrate_command
83
+ if options[:migrate]
84
+ say db_migrate_command
85
+ bare_rails_command = db_migrate_command.sub(/\Abundle exec +/, '').sub(/\Arake +|rails +/, '')
86
+ if ActiveSupport::VERSION::MAJOR < 5
87
+ rake(bare_rails_command)
88
+ else
89
+ rails_command(bare_rails_command)
97
90
  end
91
+ else
92
+ say "\nNot running migration since --migrate not given. When you are ready, run:\n\n #{db_migrate_command}\n\n"
98
93
  end
99
94
  rescue ::DeclareSchema::UnknownTypeError => ex
100
95
  say "Invalid field type: #{ex}"
@@ -102,8 +97,15 @@ module DeclareSchema
102
97
 
103
98
  private
104
99
 
100
+ if ActiveSupport::VERSION::MAJOR < 5
101
+ def indent(string, columns)
102
+ whitespace = ' ' * columns
103
+ string.gsub("\n", "\n#{whitespace}").gsub(/ +\n/, "\n")
104
+ end
105
+ end
106
+
105
107
  def migrations_pending?
106
- migrations = case Rails::VERSION::MAJOR
108
+ migrations = case ActiveSupport::VERSION::MAJOR
107
109
  when 4
108
110
  ActiveRecord::Migrator.migrations('db/migrate')
109
111
  when 5
@@ -111,7 +113,7 @@ module DeclareSchema
111
113
  else
112
114
  ActiveRecord::MigrationContext.new(ActiveRecord::Migrator.migrations_paths, ActiveRecord::SchemaMigration).migrations
113
115
  end
114
- pending_migrations = case Rails::VERSION::MAJOR
116
+ pending_migrations = case ActiveSupport::VERSION::MAJOR
115
117
  when 4, 5
116
118
  ActiveRecord::Migrator.new(:up, migrations).pending_migrations
117
119
  else
@@ -176,8 +178,8 @@ module DeclareSchema
176
178
  to_rename
177
179
  end
178
180
 
179
- def migration_name
180
- name || Generators::DeclareSchema::Migration::Migrator.default_migration_name
181
+ def default_migration_name
182
+ Generators::DeclareSchema::Migration::Migrator.default_migration_name
181
183
  end
182
184
  end
183
185
  end
@@ -47,8 +47,8 @@ module Generators
47
47
  deprecate :default_charset=, :default_collation=, :default_charset, :default_collation, deprecator: ActiveSupport::Deprecation.new('1.0', 'declare_schema')
48
48
  end
49
49
 
50
- def initialize(ambiguity_resolver = {}, renames: nil)
51
- @ambiguity_resolver = ambiguity_resolver
50
+ def initialize(renames: nil, &block)
51
+ @ambiguity_resolver = block
52
52
  @drops = []
53
53
  @renames = renames
54
54
  end
@@ -187,6 +187,12 @@ module Generators
187
187
  models, db_tables = models_and_tables
188
188
  models_by_table_name = {}
189
189
  models.each do |m|
190
+ m.try(:field_specs)&.each do |_name, field_spec|
191
+ if (pre_migration = field_spec.options.delete(:pre_migration))
192
+ pre_migration.call(field_spec)
193
+ end
194
+ end
195
+
190
196
  if !models_by_table_name.has_key?(m.table_name)
191
197
  models_by_table_name[m.table_name] = m
192
198
  elsif m.superclass == models_by_table_name[m.table_name].superclass.superclass
@@ -203,8 +209,8 @@ module Generators
203
209
 
204
210
  to_create = model_table_names - db_tables
205
211
  to_drop = db_tables - model_table_names - self.class.always_ignore_tables
206
- to_change = model_table_names
207
212
  to_rename = extract_table_renames!(to_create, to_drop)
213
+ to_change = model_table_names
208
214
 
209
215
  renames = to_rename.map do |old_name, new_name|
210
216
  ::DeclareSchema::SchemaChange::TableRename.new(old_name, new_name)
@@ -297,14 +303,14 @@ module Generators
297
303
  end
298
304
 
299
305
  def create_table_options(model, disable_auto_increment)
300
- primary_key = model._defined_primary_key
306
+ primary_key = model._declared_primary_key
301
307
  if primary_key.blank? || disable_auto_increment
302
308
  { id: false }
303
309
  elsif primary_key == "id"
304
310
  { id: :bigint }
305
311
  else
306
312
  { primary_key: primary_key.to_sym }
307
- end
313
+ end.merge(model._table_options)
308
314
  end
309
315
 
310
316
  def table_options_for_model(model)
@@ -312,8 +318,8 @@ module Generators
312
318
  {}
313
319
  else
314
320
  {
315
- charset: model.table_options[:charset] || ::DeclareSchema.default_charset,
316
- collation: model.table_options[:collation] || ::DeclareSchema.default_collation
321
+ charset: model._table_options&.[](:charset) || ::DeclareSchema.default_charset,
322
+ collation: model._table_options&.[](:collation) || ::DeclareSchema.default_collation
317
323
  }
318
324
  end
319
325
  end
@@ -336,18 +342,16 @@ module Generators
336
342
  new_table_name = model.table_name
337
343
 
338
344
  db_columns = model.connection.columns(current_table_name).index_by(&:name)
339
- key_missing = db_columns[model._defined_primary_key].nil? && model._defined_primary_key.present?
340
- if model._defined_primary_key.present?
341
- db_columns.delete(model._defined_primary_key)
345
+ if (pk = model._declared_primary_key.presence)
346
+ pk_was_in_db_columns = db_columns.delete(pk)
342
347
  end
343
348
 
344
349
  model_column_names = model.field_specs.keys.map(&:to_s)
345
350
  db_column_names = db_columns.keys.map(&:to_s)
346
351
 
347
352
  to_add = model_column_names - db_column_names
348
- to_add += [model._defined_primary_key] if key_missing && model._defined_primary_key.present?
353
+ to_add << pk if pk && !pk_was_in_db_columns
349
354
  to_remove = db_column_names - model_column_names
350
- to_remove -= [model._defined_primary_key.to_sym] if model._defined_primary_key.present?
351
355
 
352
356
  to_rename = extract_column_renames!(to_add, to_remove, new_table_name)
353
357
 
@@ -483,7 +487,7 @@ module Generators
483
487
  parent_columns = connection.columns(parent_table) rescue []
484
488
  pk_limit =
485
489
  if (pk_column = parent_columns.find { |column| column.name.to_s == "id" }) # right now foreign keys assume id is the target
486
- if Rails::VERSION::MAJOR < 5
490
+ if ActiveSupport::VERSION::MAJOR < 5
487
491
  pk_column.cast_type.limit
488
492
  else
489
493
  pk_column.limit
@@ -549,7 +553,7 @@ module Generators
549
553
  end
550
554
  end
551
555
 
552
- SchemaDumper = case Rails::VERSION::MAJOR
556
+ SchemaDumper = case ActiveSupport::VERSION::MAJOR
553
557
  when 4
554
558
  ActiveRecord::SchemaDumper
555
559
  else
@@ -1,9 +1,9 @@
1
- class <%= @migration_class_name %> < (Rails::VERSION::MAJOR >= 5 ? ActiveRecord::Migration[4.2] : ActiveRecord::Migration)
1
+ class <%= @migration_class_name %> < (ActiveSupport::VERSION::MAJOR >= 5 ? ActiveRecord::Migration[4.2] : ActiveRecord::Migration)
2
2
  def self.up
3
- <%= @up %>
3
+ <%= @up.presence or raise "no @up given!" %>
4
4
  end
5
5
 
6
6
  def self.down
7
- <%= @down %>
7
+ <%= @down.presence or raise "no @down given!" %>
8
8
  end
9
9
  end
@@ -39,17 +39,15 @@ RSpec.describe 'DeclareSchema API' do
39
39
 
40
40
  load_models
41
41
 
42
- if Rails::VERSION::MAJOR == 5
43
- # TODO: get this to work on Travis for Rails 6
42
+ if ActiveSupport::VERSION::MAJOR == 5
44
43
  generate_migrations '-n', '-m'
45
44
  end
46
45
 
47
46
  require 'advert'
47
+ Advert.reset_primary_key
48
48
 
49
49
  ## The Basics
50
50
 
51
- # The main feature of DeclareSchema, aside from the migration generator, is the ability to declare rich types for your fields. For example, you can declare that a field is an email address, and the field will be automatically validated for correct email address syntax.
52
-
53
51
  ### Field Types
54
52
 
55
53
  # Field values are returned as the type you specify.
@@ -6,13 +6,13 @@ rescue LoadError
6
6
  end
7
7
 
8
8
  RSpec.describe DeclareSchema::Model::FieldSpec do
9
- let(:model) { double('model', table_options: {}, _defined_primary_key: 'id') }
9
+ let(:model) { double('model', _table_options: {}, _declared_primary_key: 'id') }
10
10
  let(:col_spec) { double('col_spec', type: :string) }
11
11
 
12
12
  before do
13
13
  load File.expand_path('prepare_testapp.rb', __dir__)
14
14
 
15
- if Rails::VERSION::MAJOR < 5
15
+ if ActiveSupport::VERSION::MAJOR < 5
16
16
  allow(col_spec).to receive(:type_cast_from_database, &:itself)
17
17
  end
18
18
  end
@@ -184,7 +184,7 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
184
184
 
185
185
  describe '#schema_attributes' do
186
186
  let(:col_spec) do
187
- case Rails::VERSION::MAJOR
187
+ case ActiveSupport::VERSION::MAJOR
188
188
  when 4
189
189
  cast_type = ActiveRecord::Type::Integer.new(limit: 8)
190
190
  ActiveRecord::ConnectionAdapters::Column.new("price", nil, cast_type, "integer(8)", false)
@@ -27,7 +27,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
27
27
  end
28
28
  EOS
29
29
 
30
- case Rails::VERSION::MAJOR
30
+ case ActiveSupport::VERSION::MAJOR
31
31
  when 4, 5
32
32
  expect_test_definition_to_eq('alpha/beta', <<~EOS)
33
33
  require "test_helper"
@@ -50,7 +50,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
50
50
  EOS
51
51
  end
52
52
 
53
- case Rails::VERSION::MAJOR
53
+ case ActiveSupport::VERSION::MAJOR
54
54
  when 4
55
55
  expect_test_fixture_to_eq('alpha/beta', <<~EOS)
56
56
  # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rails'
4
3
  begin
5
4
  require 'mysql2'
6
5
  rescue LoadError
@@ -20,7 +19,7 @@ RSpec.describe 'DeclareSchema Migration Generator interactive primary key' do
20
19
  end
21
20
 
22
21
  generate_migrations '-n', '-m'
23
- expect(Foo._defined_primary_key).to eq('foo_id')
22
+ expect(Foo._declared_primary_key).to eq('foo_id')
24
23
 
25
24
  ### migrate from
26
25
  # rename from custom primary_key
@@ -32,7 +31,7 @@ RSpec.describe 'DeclareSchema Migration Generator interactive primary key' do
32
31
 
33
32
  allow_any_instance_of(DeclareSchema::Support::ThorShell).to receive(:ask).with(/one of the rename choices or press enter to keep/) { 'id' }
34
33
  generate_migrations '-n', '-m'
35
- expect(Foo._defined_primary_key).to eq('id')
34
+ expect(Foo._declared_primary_key).to eq('id')
36
35
 
37
36
  nuke_model_class(Foo)
38
37
 
@@ -63,7 +62,7 @@ RSpec.describe 'DeclareSchema Migration Generator interactive primary key' do
63
62
  ActiveRecord::Base.connection.execute("CREATE TABLE foos (id integer PRIMARY KEY AUTOINCREMENT NOT NULL)")
64
63
  end
65
64
 
66
- if Rails::VERSION::MAJOR >= 5 && !defined?(Mysql2) # TODO TECH-4814 Put this test back for Mysql2
65
+ if ActiveSupport::VERSION::MAJOR >= 5 && !defined?(Mysql2) # TODO TECH-4814 Put this test back for Mysql2
67
66
  # replace custom primary_key
68
67
  class Foo < ActiveRecord::Base
69
68
  fields do
@@ -73,7 +72,7 @@ RSpec.describe 'DeclareSchema Migration Generator interactive primary key' do
73
72
 
74
73
  allow_any_instance_of(DeclareSchema::Support::ThorShell).to receive(:ask).with(/one of the rename choices or press enter to keep/) { 'drop id' }
75
74
  generate_migrations '-n', '-m'
76
- expect(Foo._defined_primary_key).to eq('foo_id')
75
+ expect(Foo._declared_primary_key).to eq('foo_id')
77
76
  end
78
77
  end
79
78
  end
@@ -81,8 +80,7 @@ RSpec.describe 'DeclareSchema Migration Generator interactive primary key' do
81
80
  context 'Using declare_schema' do
82
81
  it "allows alternate primary keys" do
83
82
  class Foo < ActiveRecord::Base
84
- declare_schema do
85
- end
83
+ declare_schema { }
86
84
  self.primary_key = "foo_id"
87
85
  end
88
86
 
@@ -92,15 +90,14 @@ RSpec.describe 'DeclareSchema Migration Generator interactive primary key' do
92
90
  ### migrate from
93
91
  # rename from custom primary_key
94
92
  class Foo < ActiveRecord::Base
95
- declare_schema do
96
- end
93
+ declare_schema { }
97
94
  self.primary_key = "id"
98
95
  end
99
96
 
100
97
  puts "\n\e[45m Please enter 'id' (no quotes) at the next prompt \e[0m"
101
98
  allow_any_instance_of(DeclareSchema::Support::ThorShell).to receive(:ask).with(/one of the rename choices or press enter to keep/) { 'id' }
102
99
  generate_migrations '-n', '-m'
103
- expect(Foo.primary_key).to eq('id')
100
+ expect(Foo._declared_primary_key).to eq('id')
104
101
 
105
102
  nuke_model_class(Foo)
106
103
 
@@ -131,7 +128,7 @@ RSpec.describe 'DeclareSchema Migration Generator interactive primary key' do
131
128
  ActiveRecord::Base.connection.execute("CREATE TABLE foos (id integer PRIMARY KEY AUTOINCREMENT NOT NULL)")
132
129
  end
133
130
 
134
- if Rails::VERSION::MAJOR >= 5 && !defined?(Mysql2) # TODO TECH-4814 Put this test back for Mysql2
131
+ if ActiveSupport::VERSION::MAJOR >= 5 && !defined?(Mysql2) # TODO TECH-4814 Put this test back for Mysql2
135
132
  # replace custom primary_key
136
133
  class Foo < ActiveRecord::Base
137
134
  declare_schema do
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rails'
4
3
  begin
5
4
  require 'mysql2'
6
5
  rescue LoadError
7
6
  end
8
7
 
8
+ begin
9
+ require 'sqlite3'
10
+ rescue LoadError
11
+ end
12
+
9
13
  RSpec.describe 'DeclareSchema Migration Generator' do
10
14
  before do
11
15
  load File.expand_path('prepare_testapp.rb', __dir__)
@@ -26,20 +30,20 @@ RSpec.describe 'DeclareSchema Migration Generator' do
26
30
  end
27
31
  end
28
32
  let(:datetime_precision) do
29
- if defined?(Mysql2) && Rails::VERSION::MAJOR >= 5
33
+ if defined?(Mysql2) && ActiveSupport::VERSION::MAJOR >= 5
30
34
  ', precision: 0'
31
35
  end
32
36
  end
33
37
  let(:table_options) do
34
38
  if defined?(Mysql2)
35
- ", options: \"#{'ENGINE=InnoDB ' if Rails::VERSION::MAJOR == 5}DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\"" +
36
- if Rails::VERSION::MAJOR >= 6
39
+ ", options: \"#{'ENGINE=InnoDB ' if ActiveSupport::VERSION::MAJOR == 5}DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\"" +
40
+ if ActiveSupport::VERSION::MAJOR >= 6
37
41
  ', charset: "utf8mb4", collation: "utf8mb4_bin"'
38
42
  else
39
43
  ''
40
44
  end
41
45
  else
42
- ", id: :integer" unless Rails::VERSION::MAJOR < 5
46
+ ", id: :integer" unless ActiveSupport::VERSION::MAJOR < 5
43
47
  end
44
48
  end
45
49
  let(:lock_version_limit) do
@@ -87,7 +91,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
87
91
  ActiveRecord::Migration.class_eval(up)
88
92
  expect(Advert.columns.map(&:name)).to eq(["id", "name"])
89
93
 
90
- if Rails::VERSION::MAJOR < 5
94
+ if ActiveSupport::VERSION::MAJOR < 5
91
95
  # Rails 4 drivers don't always create PK properly. Fix that by dropping and recreating.
92
96
  ActiveRecord::Base.connection.execute("drop table adverts")
93
97
  if defined?(Mysql2)
@@ -366,10 +370,10 @@ RSpec.describe 'DeclareSchema Migration Generator' do
366
370
  migrate_up(<<~EOS.strip)
367
371
  add_column :adverts, :category_id, :integer, limit: 8, null: false
368
372
  add_index :adverts, [:category_id], name: :on_category_id
369
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id" if defined?(Mysql2)}
373
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id" if defined?(Mysql2)}
370
374
  EOS
371
375
  .and migrate_down(<<~EOS.strip)
372
- #{"remove_foreign_key :adverts, name: :on_category_id" if defined?(Mysql2)}
376
+ #{"remove_foreign_key :adverts, name: :index_adverts_on_category_id" if defined?(Mysql2)}
373
377
  remove_index :adverts, name: :on_category_id
374
378
  remove_column :adverts, :category_id
375
379
  EOS
@@ -390,8 +394,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
390
394
  migrate_up(<<~EOS.strip)
391
395
  add_column :adverts, :c_id, :integer, limit: 8, null: false
392
396
  add_index :adverts, [:c_id], name: :on_c_id
393
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
394
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
397
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
398
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
395
399
  EOS
396
400
  )
397
401
 
@@ -409,8 +413,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
409
413
  expect(Generators::DeclareSchema::Migration::Migrator.run).to(
410
414
  migrate_up(<<~EOS.strip)
411
415
  add_column :adverts, :category_id, :integer, limit: 8, null: false
412
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
413
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
416
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
417
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
414
418
  EOS
415
419
  )
416
420
 
@@ -429,18 +433,18 @@ RSpec.describe 'DeclareSchema Migration Generator' do
429
433
  migrate_up(<<~EOS.strip)
430
434
  add_column :adverts, :category_id, :integer, limit: 8, null: false
431
435
  add_index :adverts, [:category_id], name: :my_index
432
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
433
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
436
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
437
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
434
438
  EOS
435
439
  )
436
440
 
437
441
  Advert.field_specs.delete(:category_id)
438
442
  Advert.index_definitions.delete_if { |spec| spec.fields == ["category_id"] }
439
443
 
440
- ### Timestamps and Optimimistic Locking
444
+ ### Timestamps and Optimistic Locking
441
445
 
442
446
  # `updated_at` and `created_at` can be declared with the shorthand `timestamps`.
443
- # Similarly, `lock_version` can be declared with the "shorthand" `optimimistic_lock`.
447
+ # Similarly, `lock_version` can be declared with the "shorthand" `optimistic_lock`.
444
448
 
445
449
  class Advert < ActiveRecord::Base
446
450
  fields do
@@ -454,12 +458,12 @@ RSpec.describe 'DeclareSchema Migration Generator' do
454
458
  add_column :adverts, :created_at, :datetime, null: true
455
459
  add_column :adverts, :updated_at, :datetime, null: true
456
460
  add_column :adverts, :lock_version, :integer#{lock_version_limit}, null: false, default: 1
457
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
458
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
461
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
462
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
459
463
  EOS
460
464
  .and migrate_down(<<~EOS.strip)
461
- #{"remove_foreign_key :adverts, name: :on_c_id\n" +
462
- "remove_foreign_key :adverts, name: :on_category_id" if defined?(Mysql2)}
465
+ #{"remove_foreign_key :adverts, name: :index_adverts_on_c_id\n" +
466
+ "remove_foreign_key :adverts, name: :index_adverts_on_category_id" if defined?(Mysql2)}
463
467
  remove_column :adverts, :lock_version
464
468
  remove_column :adverts, :updated_at
465
469
  remove_column :adverts, :created_at
@@ -484,8 +488,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
484
488
  migrate_up(<<~EOS.strip)
485
489
  add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
486
490
  add_index :adverts, [:title], name: :on_title
487
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
488
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
491
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
492
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
489
493
  EOS
490
494
  )
491
495
 
@@ -503,8 +507,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
503
507
  migrate_up(<<~EOS.strip)
504
508
  add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
505
509
  add_index :adverts, [:title], name: :on_title, unique: true
506
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
507
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
510
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
511
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
508
512
  EOS
509
513
  )
510
514
 
@@ -522,8 +526,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
522
526
  migrate_up(<<~EOS.strip)
523
527
  add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
524
528
  add_index :adverts, [:title], name: :my_index
525
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
526
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
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)}
527
531
  EOS
528
532
  )
529
533
 
@@ -539,8 +543,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
539
543
  migrate_up(<<~EOS.strip)
540
544
  add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
541
545
  add_index :adverts, [:title], name: :on_title
542
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
543
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
546
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
547
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
544
548
  EOS
545
549
  )
546
550
 
@@ -556,8 +560,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
556
560
  migrate_up(<<~EOS.strip)
557
561
  add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
558
562
  add_index :adverts, [:title], name: :my_index, unique: true
559
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
560
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
563
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
564
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
561
565
  EOS
562
566
  )
563
567
 
@@ -573,8 +577,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
573
577
  migrate_up(<<~EOS.strip)
574
578
  add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
575
579
  add_index :adverts, [:title, :category_id], name: :on_title_and_category_id
576
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
577
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
580
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
581
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
578
582
  EOS
579
583
  )
580
584
 
@@ -606,14 +610,14 @@ RSpec.describe 'DeclareSchema Migration Generator' do
606
610
  add_column :ads, :title, :string, limit: 250, null: true#{charset_and_collation}
607
611
  add_column :ads, :body, :text#{', limit: 4294967295' if defined?(Mysql2)}, null: true#{charset_and_collation}
608
612
  #{if defined?(Mysql2)
609
- "add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
610
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id"
613
+ "add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
614
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id"
611
615
  end}
612
616
  EOS
613
617
  .and migrate_down(<<~EOS.strip)
614
618
  #{if defined?(Mysql2)
615
- "remove_foreign_key :adverts, name: :on_c_id\n" +
616
- "remove_foreign_key :adverts, name: :on_category_id"
619
+ "remove_foreign_key :adverts, name: :index_adverts_on_c_id\n" +
620
+ "remove_foreign_key :adverts, name: :index_adverts_on_category_id"
617
621
  end}
618
622
  remove_column :ads, :body
619
623
  remove_column :ads, :title
@@ -836,6 +840,54 @@ RSpec.describe 'DeclareSchema Migration Generator' do
836
840
  expect(Ad.field_specs['company'].options[:validates].inspect).to eq("{:presence=>true, :uniqueness=>{:case_sensitive=>false}}")
837
841
  end
838
842
 
843
+ context 'models with the same parent foreign key relation' do
844
+ before do
845
+ class Category < ActiveRecord::Base
846
+ fields do
847
+ name :string, limit: 250, null: true
848
+ end
849
+ end
850
+ class Advertiser < ActiveRecord::Base
851
+ fields do
852
+ name :string, limit: 250, null: true
853
+ end
854
+ belongs_to :category, limit: 8
855
+ end
856
+ class Affiliate < ActiveRecord::Base
857
+ fields do
858
+ name :string, limit: 250, null: true
859
+ end
860
+ belongs_to :category, limit: 8
861
+ end
862
+ end
863
+
864
+ it 'will genereate unique constraint names' do
865
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
866
+ migrate_up(<<~EOS.strip)
867
+ create_table :categories, id: :bigint, options: "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin" do |t|
868
+ t.string :name, limit: 250, null: true, charset: "utf8mb4", collation: "utf8mb4_bin"
869
+ end
870
+ create_table :advertisers, id: :bigint, options: "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin" do |t|
871
+ t.string :name, limit: 250, null: true, charset: "utf8mb4", collation: "utf8mb4_bin"
872
+ t.integer :category_id, limit: 8, null: false
873
+ end
874
+ create_table :affiliates, id: :bigint, options: "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin" do |t|
875
+ t.string :name, limit: 250, null: true, charset: "utf8mb4", collation: "utf8mb4_bin"
876
+ t.integer :category_id, limit: 8, null: false
877
+ end
878
+ add_index :advertisers, [:category_id], name: :on_category_id
879
+ add_index :affiliates, [:category_id], name: :on_category_id
880
+ add_foreign_key :advertisers, :categories, column: :category_id, name: :index_advertisers_on_category_id
881
+ add_foreign_key :affiliates, :categories, column: :category_id, name: :index_affiliates_on_category_id
882
+ EOS
883
+ )
884
+ migrate
885
+
886
+ nuke_model_class(Advertiser)
887
+ nuke_model_class(Affiliate)
888
+ end
889
+ end if !defined?(SQLite3) && ActiveRecord::VERSION::MAJOR >= 5
890
+
839
891
  describe 'serialize' do
840
892
  before do
841
893
  class Ad < ActiveRecord::Base
@@ -1020,8 +1072,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1020
1072
  end
1021
1073
  end
1022
1074
 
1023
- context "for Rails #{Rails::VERSION::MAJOR}" do
1024
- if Rails::VERSION::MAJOR >= 5
1075
+ context "for Rails #{ActiveSupport::VERSION::MAJOR}" do
1076
+ if ActiveSupport::VERSION::MAJOR >= 5
1025
1077
  let(:optional_true) { { optional: true } }
1026
1078
  let(:optional_false) { { optional: false } }
1027
1079
  else
@@ -1127,13 +1179,13 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1127
1179
  migration_content = File.read(migrations.first)
1128
1180
  first_line = migration_content.split("\n").first
1129
1181
  base_class = first_line.split(' < ').last
1130
- expect(base_class).to eq("(Rails::VERSION::MAJOR >= 5 ? ActiveRecord::Migration[4.2] : ActiveRecord::Migration)")
1182
+ expect(base_class).to eq("(ActiveSupport::VERSION::MAJOR >= 5 ? ActiveRecord::Migration[4.2] : ActiveRecord::Migration)")
1131
1183
  end
1132
1184
  end
1133
1185
 
1134
1186
  context 'Does not generate migrations' do
1135
1187
  it 'for aliased fields bigint -> integer limit 8' do
1136
- if Rails::VERSION::MAJOR >= 5 || !ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/)
1188
+ if ActiveSupport::VERSION::MAJOR >= 5 || !ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/)
1137
1189
  class Advert < active_record_base_class.constantize
1138
1190
  fields do
1139
1191
  price :bigint
@@ -1145,7 +1197,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1145
1197
  migrations = Dir.glob('db/migrate/*declare_schema_migration*.rb')
1146
1198
  expect(migrations.size).to eq(1), migrations.inspect
1147
1199
 
1148
- if defined?(Mysql2) && Rails::VERSION::MAJOR < 5
1200
+ if defined?(Mysql2) && ActiveSupport::VERSION::MAJOR < 5
1149
1201
  ActiveRecord::Base.connection.execute("ALTER TABLE adverts ADD PRIMARY KEY (id)")
1150
1202
  end
1151
1203
 
@@ -1198,7 +1250,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1198
1250
  ActiveRecord::Migration.class_eval(up)
1199
1251
  expect(Advert.columns.map(&:name)).to eq(["id", "name"])
1200
1252
 
1201
- if Rails::VERSION::MAJOR < 5
1253
+ if ActiveSupport::VERSION::MAJOR < 5
1202
1254
  # Rails 4 drivers don't always create PK properly. Fix that by dropping and recreating.
1203
1255
  ActiveRecord::Base.connection.execute("drop table adverts")
1204
1256
  if defined?(Mysql2)
@@ -1477,10 +1529,10 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1477
1529
  migrate_up(<<~EOS.strip)
1478
1530
  add_column :adverts, :category_id, :integer, limit: 8, null: false
1479
1531
  add_index :adverts, [:category_id], name: :on_category_id
1480
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" if defined?(Mysql2)}
1532
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" if defined?(Mysql2)}
1481
1533
  EOS
1482
1534
  .and migrate_down(<<~EOS.strip)
1483
- #{"remove_foreign_key :adverts, name: :on_category_id" if defined?(Mysql2)}
1535
+ #{"remove_foreign_key :adverts, name: :index_adverts_on_category_id" if defined?(Mysql2)}
1484
1536
  remove_index :adverts, name: :on_category_id
1485
1537
  remove_column :adverts, :category_id
1486
1538
  EOS
@@ -1501,8 +1553,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1501
1553
  migrate_up(<<~EOS.strip)
1502
1554
  add_column :adverts, :c_id, :integer, limit: 8, null: false
1503
1555
  add_index :adverts, [:c_id], name: :on_c_id
1504
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
1505
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
1556
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
1557
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
1506
1558
  EOS
1507
1559
  )
1508
1560
 
@@ -1520,8 +1572,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1520
1572
  expect(Generators::DeclareSchema::Migration::Migrator.run).to(
1521
1573
  migrate_up(<<~EOS.strip)
1522
1574
  add_column :adverts, :category_id, :integer, limit: 8, null: false
1523
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
1524
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
1575
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
1576
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
1525
1577
  EOS
1526
1578
  )
1527
1579
 
@@ -1540,8 +1592,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1540
1592
  migrate_up(<<~EOS.strip)
1541
1593
  add_column :adverts, :category_id, :integer, limit: 8, null: false
1542
1594
  add_index :adverts, [:category_id], name: :my_index
1543
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
1544
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
1595
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
1596
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
1545
1597
  EOS
1546
1598
  )
1547
1599
 
@@ -1565,12 +1617,12 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1565
1617
  add_column :adverts, :created_at, :datetime, null: true
1566
1618
  add_column :adverts, :updated_at, :datetime, null: true
1567
1619
  add_column :adverts, :lock_version, :integer#{lock_version_limit}, null: false, default: 1
1568
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
1569
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
1620
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
1621
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
1570
1622
  EOS
1571
1623
  .and migrate_down(<<~EOS.strip)
1572
- #{"remove_foreign_key :adverts, name: :on_c_id\n" +
1573
- "remove_foreign_key :adverts, name: :on_category_id" if defined?(Mysql2)}
1624
+ #{"remove_foreign_key :adverts, name: :index_adverts_on_c_id\n" +
1625
+ "remove_foreign_key :adverts, name: :index_adverts_on_category_id" if defined?(Mysql2)}
1574
1626
  remove_column :adverts, :lock_version
1575
1627
  remove_column :adverts, :updated_at
1576
1628
  remove_column :adverts, :created_at
@@ -1595,8 +1647,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1595
1647
  migrate_up(<<~EOS.strip)
1596
1648
  add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
1597
1649
  add_index :adverts, [:title], name: :on_title
1598
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
1599
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
1650
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
1651
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
1600
1652
  EOS
1601
1653
  )
1602
1654
 
@@ -1614,8 +1666,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1614
1666
  migrate_up(<<~EOS.strip)
1615
1667
  add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
1616
1668
  add_index :adverts, [:title], name: :on_title, unique: true
1617
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
1618
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
1669
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
1670
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
1619
1671
  EOS
1620
1672
  )
1621
1673
 
@@ -1633,8 +1685,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1633
1685
  migrate_up(<<~EOS.strip)
1634
1686
  add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
1635
1687
  add_index :adverts, [:title], name: :my_index
1636
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
1637
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
1688
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
1689
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
1638
1690
  EOS
1639
1691
  )
1640
1692
 
@@ -1650,8 +1702,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1650
1702
  migrate_up(<<~EOS.strip)
1651
1703
  add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
1652
1704
  add_index :adverts, [:title], name: :on_title
1653
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
1654
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
1705
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
1706
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
1655
1707
  EOS
1656
1708
  )
1657
1709
 
@@ -1667,8 +1719,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1667
1719
  migrate_up(<<~EOS.strip)
1668
1720
  add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
1669
1721
  add_index :adverts, [:title], name: :my_index, unique: true
1670
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
1671
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
1722
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
1723
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
1672
1724
  EOS
1673
1725
  )
1674
1726
 
@@ -1684,8 +1736,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1684
1736
  migrate_up(<<~EOS.strip)
1685
1737
  add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
1686
1738
  add_index :adverts, [:title, :category_id], name: :on_title_and_category_id
1687
- #{"add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
1688
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id" if defined?(Mysql2)}
1739
+ #{"add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
1740
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id" if defined?(Mysql2)}
1689
1741
  EOS
1690
1742
  )
1691
1743
 
@@ -1716,14 +1768,14 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1716
1768
  add_column :ads, :title, :string, limit: 250, null: true#{charset_and_collation}
1717
1769
  add_column :ads, :body, :text#{', limit: 4294967295' if defined?(Mysql2)}, null: true#{charset_and_collation}
1718
1770
  #{if defined?(Mysql2)
1719
- "add_foreign_key :adverts, :categories, column: :category_id, name: :on_category_id\n" +
1720
- "add_foreign_key :adverts, :categories, column: :c_id, name: :on_c_id"
1771
+ "add_foreign_key :adverts, :categories, column: :category_id, name: :index_adverts_on_category_id\n" +
1772
+ "add_foreign_key :adverts, :categories, column: :c_id, name: :index_adverts_on_c_id"
1721
1773
  end}
1722
1774
  EOS
1723
1775
  .and migrate_down(<<~EOS.strip)
1724
1776
  #{if defined?(Mysql2)
1725
- "remove_foreign_key :adverts, name: :on_c_id\n" +
1726
- "remove_foreign_key :adverts, name: :on_category_id"
1777
+ "remove_foreign_key :adverts, name: :index_adverts_on_c_id\n" +
1778
+ "remove_foreign_key :adverts, name: :index_adverts_on_category_id"
1727
1779
  end}
1728
1780
  remove_column :ads, :body
1729
1781
  remove_column :ads, :title
@@ -1946,6 +1998,54 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1946
1998
  expect(Ad.field_specs['company'].options[:validates].inspect).to eq("{:presence=>true, :uniqueness=>{:case_sensitive=>false}}")
1947
1999
  end
1948
2000
 
2001
+ context 'models with the same parent foreign key relation' do
2002
+ before do
2003
+ class Category < ActiveRecord::Base
2004
+ fields do
2005
+ name :string, limit: 250, null: true
2006
+ end
2007
+ end
2008
+ class Advertiser < ActiveRecord::Base
2009
+ fields do
2010
+ name :string, limit: 250, null: true
2011
+ end
2012
+ belongs_to :category, limit: 8
2013
+ end
2014
+ class Affiliate < ActiveRecord::Base
2015
+ fields do
2016
+ name :string, limit: 250, null: true
2017
+ end
2018
+ belongs_to :category, limit: 8
2019
+ end
2020
+ end
2021
+
2022
+ it 'will genereate unique constraint names' do
2023
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
2024
+ migrate_up(<<~EOS.strip)
2025
+ create_table :categories, id: :bigint, options: "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin" do |t|
2026
+ t.string :name, limit: 250, null: true, charset: "utf8mb4", collation: "utf8mb4_bin"
2027
+ end
2028
+ create_table :advertisers, id: :bigint, options: "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin" do |t|
2029
+ t.string :name, limit: 250, null: true, charset: "utf8mb4", collation: "utf8mb4_bin"
2030
+ t.integer :category_id, limit: 8, null: false
2031
+ end
2032
+ create_table :affiliates, id: :bigint, options: "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin" do |t|
2033
+ t.string :name, limit: 250, null: true, charset: "utf8mb4", collation: "utf8mb4_bin"
2034
+ t.integer :category_id, limit: 8, null: false
2035
+ end
2036
+ add_index :advertisers, [:category_id], name: :on_category_id
2037
+ add_index :affiliates, [:category_id], name: :on_category_id
2038
+ add_foreign_key :advertisers, :categories, column: :category_id, name: :index_advertisers_on_category_id
2039
+ add_foreign_key :affiliates, :categories, column: :category_id, name: :index_affiliates_on_category_id
2040
+ EOS
2041
+ )
2042
+ migrate
2043
+
2044
+ nuke_model_class(Advertiser)
2045
+ nuke_model_class(Affiliate)
2046
+ end
2047
+ end if !defined?(SQLite3) && ActiveRecord::VERSION::MAJOR >= 5
2048
+
1949
2049
  describe 'serialize' do
1950
2050
  before do
1951
2051
  class Ad < ActiveRecord::Base
@@ -2130,8 +2230,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
2130
2230
  end
2131
2231
  end
2132
2232
 
2133
- context "for Rails #{Rails::VERSION::MAJOR}" do
2134
- if Rails::VERSION::MAJOR >= 5
2233
+ context "for Rails #{ActiveSupport::VERSION::MAJOR}" do
2234
+ if ActiveSupport::VERSION::MAJOR >= 5
2135
2235
  let(:optional_true) { { optional: true } }
2136
2236
  let(:optional_false) { { optional: false } }
2137
2237
  else
@@ -2141,80 +2241,202 @@ RSpec.describe 'DeclareSchema Migration Generator' do
2141
2241
  let(:optional_flag) { { false => optional_false, true => optional_true } }
2142
2242
 
2143
2243
  describe 'belongs_to' do
2144
- before do
2145
- unless defined?(AdCategory)
2146
- class AdCategory < ActiveRecord::Base
2147
- declare_schema { }
2244
+ context 'with AdCategory and Advert in DB' do
2245
+ before do
2246
+ unless defined?(AdCategory)
2247
+ class AdCategory < ActiveRecord::Base
2248
+ declare_schema { }
2249
+ end
2148
2250
  end
2149
- end
2150
2251
 
2151
- class Advert < ActiveRecord::Base
2152
- declare_schema do
2153
- string :name, limit: 250, null: true
2154
- integer :category_id, limit: 8
2155
- integer :nullable_category_id, limit: 8, null: true
2252
+ class Advert < ActiveRecord::Base
2253
+ declare_schema do
2254
+ string :name, limit: 250, null: true
2255
+ integer :category_id, limit: 8
2256
+ integer :nullable_category_id, limit: 8, null: true
2257
+ end
2156
2258
  end
2259
+ up = Generators::DeclareSchema::Migration::Migrator.run.first
2260
+ ActiveRecord::Migration.class_eval(up)
2157
2261
  end
2158
- up = Generators::DeclareSchema::Migration::Migrator.run.first
2159
- ActiveRecord::Migration.class_eval(up)
2160
- end
2161
2262
 
2162
- it 'passes through optional: when given' do
2163
- class AdvertBelongsTo < ActiveRecord::Base
2164
- self.table_name = 'adverts'
2165
- declare_schema { }
2166
- reset_column_information
2167
- belongs_to :ad_category, optional: true
2168
- end
2169
- expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional_true)
2170
- end
2171
-
2172
- describe 'contradictory settings' do # contradictory settings are ok--for example, during migration
2173
- it 'passes through optional: true, null: false' do
2263
+ it 'passes through optional: when given' do
2174
2264
  class AdvertBelongsTo < ActiveRecord::Base
2175
2265
  self.table_name = 'adverts'
2176
2266
  declare_schema { }
2177
2267
  reset_column_information
2178
- belongs_to :ad_category, optional: true, null: false
2268
+ belongs_to :ad_category, optional: true
2179
2269
  end
2180
2270
  expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional_true)
2181
- expect(AdvertBelongsTo.field_specs['ad_category_id'].options&.[](:null)).to eq(false)
2182
2271
  end
2183
2272
 
2184
- it 'passes through optional: false, null: true' do
2185
- class AdvertBelongsTo < ActiveRecord::Base
2186
- self.table_name = 'adverts'
2187
- declare_schema { }
2188
- reset_column_information
2189
- belongs_to :ad_category, optional: false, null: true
2273
+ describe 'contradictory settings' do # contradictory settings are ok--for example, during migration
2274
+ it 'passes through optional: true, null: false' do
2275
+ class AdvertBelongsTo < ActiveRecord::Base
2276
+ self.table_name = 'adverts'
2277
+ declare_schema { }
2278
+ reset_column_information
2279
+ belongs_to :ad_category, optional: true, null: false
2280
+ end
2281
+ expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional_true)
2282
+ expect(AdvertBelongsTo.field_specs['ad_category_id'].options&.[](:null)).to eq(false)
2283
+ end
2284
+
2285
+ it 'passes through optional: false, null: true' do
2286
+ class AdvertBelongsTo < ActiveRecord::Base
2287
+ self.table_name = 'adverts'
2288
+ declare_schema { }
2289
+ reset_column_information
2290
+ belongs_to :ad_category, optional: false, null: true
2291
+ end
2292
+ expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional_false)
2293
+ expect(AdvertBelongsTo.field_specs['ad_category_id'].options&.[](:null)).to eq(true)
2294
+ end
2295
+ end
2296
+
2297
+ [false, true].each do |nullable|
2298
+ context "nullable=#{nullable}" do
2299
+ it 'infers optional: from null:' do
2300
+ eval <<~EOS
2301
+ class AdvertBelongsTo < ActiveRecord::Base
2302
+ declare_schema { }
2303
+ belongs_to :ad_category, null: #{nullable}
2304
+ end
2305
+ EOS
2306
+ expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional_flag[nullable])
2307
+ expect(AdvertBelongsTo.field_specs['ad_category_id'].options&.[](:null)).to eq(nullable)
2308
+ end
2309
+
2310
+ it 'infers null: from optional:' do
2311
+ eval <<~EOS
2312
+ class AdvertBelongsTo < ActiveRecord::Base
2313
+ declare_schema { }
2314
+ belongs_to :ad_category, optional: #{nullable}
2315
+ end
2316
+ EOS
2317
+ expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional_flag[nullable])
2318
+ expect(AdvertBelongsTo.field_specs['ad_category_id'].options&.[](:null)).to eq(nullable)
2319
+ end
2190
2320
  end
2191
- expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional_false)
2192
- expect(AdvertBelongsTo.field_specs['ad_category_id'].options&.[](:null)).to eq(true)
2321
+ end
2322
+
2323
+ it 'deprecates limit:' do
2324
+ expect(ActiveSupport::Deprecation).to receive(:warn).with("belongs_to limit: is deprecated since it is now inferred")
2325
+ eval <<~EOS
2326
+ class UsingLimit < ActiveRecord::Base
2327
+ declare_schema { }
2328
+ belongs_to :ad_category, limit: 4
2329
+ end
2330
+ EOS
2193
2331
  end
2194
2332
  end
2195
2333
 
2196
- [false, true].each do |nullable|
2197
- context "nullable=#{nullable}" do
2198
- it 'infers optional: from null:' do
2199
- eval <<~EOS
2200
- class AdvertBelongsTo < ActiveRecord::Base
2201
- declare_schema { }
2202
- belongs_to :ad_category, null: #{nullable}
2203
- end
2204
- EOS
2205
- expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional_flag[nullable])
2206
- expect(AdvertBelongsTo.field_specs['ad_category_id'].options&.[](:null)).to eq(nullable)
2334
+ context 'when parent object PKs have different limits' do
2335
+ before do
2336
+ class IdDefault < ActiveRecord::Base
2337
+ declare_schema { }
2338
+ end
2339
+ class Id4 < ActiveRecord::Base
2340
+ declare_schema id: :integer do
2341
+ end
2207
2342
  end
2343
+ class Id8 < ActiveRecord::Base
2344
+ declare_schema id: :bigint do
2345
+ end
2346
+ end
2347
+ class Fk < ActiveRecord::Base
2348
+ declare_schema { }
2349
+ belongs_to :id_default, (ActiveSupport::VERSION::MAJOR < 5 ? { constraint: false } : {})
2350
+ belongs_to :id4, (ActiveSupport::VERSION::MAJOR < 5 ? { constraint: false } : {})
2351
+ belongs_to :id8, (ActiveSupport::VERSION::MAJOR < 5 ? { constraint: false } : {})
2352
+ end
2353
+ end
2208
2354
 
2209
- it 'infers null: from optional:' do
2210
- eval <<~EOS
2211
- class AdvertBelongsTo < ActiveRecord::Base
2212
- declare_schema { }
2213
- belongs_to :ad_category, optional: #{nullable}
2214
- end
2215
- EOS
2216
- expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional_flag[nullable])
2217
- expect(AdvertBelongsTo.field_specs['ad_category_id'].options&.[](:null)).to eq(nullable)
2355
+ it 'creates the proper PKs' do
2356
+ up = Generators::DeclareSchema::Migration::Migrator.run.first
2357
+
2358
+ create_id4_defaults = up.split("\n").grep(/create_table :id_defaults/).first
2359
+ expect(create_id4_defaults).to be, up
2360
+ expect(create_id4_defaults).to match(/, id: :bigint/)
2361
+
2362
+ create_id4s = up.split("\n").grep(/create_table :id4s/).first
2363
+ expect(create_id4s).to be, up
2364
+ expect(create_id4s).to match(/, id: :integer/)
2365
+
2366
+ create_id8s = up.split("\n").grep(/create_table :id8s/).first
2367
+ expect(create_id8s).to be, up
2368
+ expect(create_id8s).to match(/, id: :bigint/)
2369
+ end
2370
+
2371
+ it 'infers the correct FK type from the create_table id: type' do
2372
+ up = Generators::DeclareSchema::Migration::Migrator.run.first
2373
+
2374
+ create_fks = up.split("\n").grep(/t\.integer /).map { |command| command.gsub(', null: false', '').gsub(/^ +/, '') }
2375
+ if defined?(SQLite3)
2376
+ create_fks.map! { |command| command.gsub(/limit: [a-z0-9]+/, 'limit: X') }
2377
+ expect(create_fks).to eq([
2378
+ 't.integer :id_default_id, limit: X',
2379
+ 't.integer :id4_id, limit: X',
2380
+ 't.integer :id8_id, limit: X'
2381
+ ]), up
2382
+ else
2383
+ expect(create_fks).to eq([
2384
+ 't.integer :id_default_id, limit: 8',
2385
+ 't.integer :id4_id, limit: 4',
2386
+ 't.integer :id8_id, limit: 8'
2387
+ ]), up
2388
+ end
2389
+ end
2390
+
2391
+ context "when parent objects were migrated before and later definitions don't have explicit id:" do
2392
+ before do
2393
+ up = Generators::DeclareSchema::Migration::Migrator.run.first
2394
+ ActiveRecord::Migration.class_eval up
2395
+ nuke_model_class(IdDefault)
2396
+ nuke_model_class(Id4)
2397
+ nuke_model_class(Id8)
2398
+ nuke_model_class(Fk)
2399
+ ActiveRecord::Base.connection.schema_cache.clear!
2400
+
2401
+
2402
+ class NewIdDefault < ActiveRecord::Base
2403
+ self.table_name = 'id_defaults'
2404
+ declare_schema { }
2405
+ end
2406
+ class NewId4 < ActiveRecord::Base
2407
+ self.table_name = 'id4s'
2408
+ declare_schema { }
2409
+ end
2410
+ class NewId8 < ActiveRecord::Base
2411
+ self.table_name = 'id8s'
2412
+ declare_schema { }
2413
+ end
2414
+ class NewFk < ActiveRecord::Base
2415
+ declare_schema { }
2416
+ belongs_to :new_id_default
2417
+ belongs_to :new_id4
2418
+ belongs_to :new_id8
2419
+ end
2420
+ end
2421
+
2422
+ it 'infers the correct FK :integer limit: ' do
2423
+ up = Generators::DeclareSchema::Migration::Migrator.run.first
2424
+
2425
+ create_fks = up.split("\n").grep(/t\.integer /).map { |command| command.gsub(', null: false', '').gsub(/^ +/, '') }
2426
+ if defined?(SQLite3)
2427
+ create_fks.map! { |command| command.gsub(/limit: [a-z0-9]+/, 'limit: X') }
2428
+ expect(create_fks).to eq([
2429
+ 't.integer :new_id_default_id, limit: X',
2430
+ 't.integer :new_id4_id, limit: X',
2431
+ 't.integer :new_id8_id, limit: X'
2432
+ ]), up
2433
+ else
2434
+ expect(create_fks).to eq([
2435
+ 't.integer :new_id_default_id, limit: 8',
2436
+ 't.integer :new_id4_id, limit: 4',
2437
+ 't.integer :new_id8_id, limit: 8'
2438
+ ]), up
2439
+ end
2218
2440
  end
2219
2441
  end
2220
2442
  end
@@ -2237,13 +2459,13 @@ RSpec.describe 'DeclareSchema Migration Generator' do
2237
2459
  migration_content = File.read(migrations.first)
2238
2460
  first_line = migration_content.split("\n").first
2239
2461
  base_class = first_line.split(' < ').last
2240
- expect(base_class).to eq("(Rails::VERSION::MAJOR >= 5 ? ActiveRecord::Migration[4.2] : ActiveRecord::Migration)")
2462
+ expect(base_class).to eq("(ActiveSupport::VERSION::MAJOR >= 5 ? ActiveRecord::Migration[4.2] : ActiveRecord::Migration)")
2241
2463
  end
2242
2464
  end
2243
2465
 
2244
2466
  context 'Does not generate migrations' do
2245
2467
  it 'for aliased fields bigint -> integer limit 8' do
2246
- if Rails::VERSION::MAJOR >= 5 || !ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/)
2468
+ if ActiveSupport::VERSION::MAJOR >= 5 || !ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/)
2247
2469
  class Advert < active_record_base_class.constantize
2248
2470
  declare_schema do
2249
2471
  bigint :price
@@ -2255,7 +2477,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
2255
2477
  migrations = Dir.glob('db/migrate/*declare_schema_migration*.rb')
2256
2478
  expect(migrations.size).to eq(1), migrations.inspect
2257
2479
 
2258
- if defined?(Mysql2) && Rails::VERSION::MAJOR < 5
2480
+ if defined?(Mysql2) && ActiveSupport::VERSION::MAJOR < 5
2259
2481
  ActiveRecord::Base.connection.execute("ALTER TABLE adverts ADD PRIMARY KEY (id)")
2260
2482
  end
2261
2483