declare_schema 0.3.0.pre.2 → 0.4.2

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.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeclareSchema
4
- VERSION = "0.3.0.pre.2"
4
+ VERSION = "0.4.2"
5
5
  end
@@ -45,14 +45,14 @@ module Generators
45
45
  false # no single-column primary key
46
46
  end
47
47
 
48
- def index_specs_with_primary_key
48
+ def index_definitions_with_primary_key
49
49
  [
50
- ::DeclareSchema::Model::IndexSpec.new(self, foreign_keys, unique: true, name: "PRIMARY_KEY"),
51
- ::DeclareSchema::Model::IndexSpec.new(self, foreign_keys.last) # not unique by itself; combines with primary key to be unique
50
+ ::DeclareSchema::Model::IndexDefinition.new(self, foreign_keys, unique: true, name: ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME),
51
+ ::DeclareSchema::Model::IndexDefinition.new(self, foreign_keys.last) # not unique by itself; combines with primary key to be unique
52
52
  ]
53
53
  end
54
54
 
55
- alias_method :index_specs, :index_specs_with_primary_key
55
+ alias_method :index_definitions, :index_definitions_with_primary_key
56
56
 
57
57
  def ignore_indexes
58
58
  []
@@ -60,8 +60,8 @@ module Generators
60
60
 
61
61
  def constraint_specs
62
62
  [
63
- ::DeclareSchema::Model::ForeignKeySpec.new(self, foreign_keys.first, parent_table: foreign_key_classes.first.table_name, constraint_name: "#{join_table}_FK1", dependent: :delete),
64
- ::DeclareSchema::Model::ForeignKeySpec.new(self, foreign_keys.last, parent_table: foreign_key_classes.last.table_name, constraint_name: "#{join_table}_FK2", dependent: :delete)
63
+ ::DeclareSchema::Model::ForeignKeyDefinition.new(self, foreign_keys.first, parent_table: foreign_key_classes.first.table_name, constraint_name: "#{join_table}_FK1", dependent: :delete),
64
+ ::DeclareSchema::Model::ForeignKeyDefinition.new(self, foreign_keys.last, parent_table: foreign_key_classes.last.table_name, constraint_name: "#{join_table}_FK2", dependent: :delete)
65
65
  ]
66
66
  end
67
67
  end
@@ -341,7 +341,7 @@ module Generators
341
341
  end
342
342
 
343
343
  def create_indexes(model)
344
- model.index_specs.map { |i| i.to_add_statement(model.table_name) }
344
+ model.index_definitions.map { |i| i.to_add_statement(model.table_name) }
345
345
  end
346
346
 
347
347
  def create_constraints(model)
@@ -413,7 +413,7 @@ module Generators
413
413
  col_name = old_names[c] || c
414
414
  col = db_columns[col_name]
415
415
  spec = model.field_specs[c]
416
- if spec.different_to?(col) # TODO: DRY this up to a diff function that returns the differences. It's different if it has differences. -Colin
416
+ if spec.different_to?(col) # TODO: TECH-4814 DRY this up to a diff function that returns the differences. It's different if it has differences. -Colin
417
417
  change_spec = fk_field_options(model, c)
418
418
  change_spec[:limit] ||= spec.limit if (spec.sql_type != :text ||
419
419
  ::DeclareSchema::Model::FieldSpec.mysql_text_limits?) &&
@@ -449,8 +449,8 @@ module Generators
449
449
  return [[], []] if Migrator.disable_constraints
450
450
 
451
451
  new_table_name = model.table_name
452
- existing_indexes = ::DeclareSchema::Model::IndexSpec.for_model(model, old_table_name)
453
- model_indexes_with_equivalents = model.index_specs_with_primary_key
452
+ existing_indexes = ::DeclareSchema::Model::IndexDefinition.for_model(model, old_table_name)
453
+ model_indexes_with_equivalents = model.index_definitions_with_primary_key
454
454
  model_indexes = model_indexes_with_equivalents.map do |i|
455
455
  if i.explicit_name.nil?
456
456
  if ex = existing_indexes.find { |e| i != e && e.equivalent?(i) }
@@ -458,20 +458,20 @@ module Generators
458
458
  end
459
459
  end || i
460
460
  end
461
- existing_has_primary_key = existing_indexes.any? { |i| i.name == 'PRIMARY_KEY' }
462
- model_has_primary_key = model_indexes.any? { |i| i.name == 'PRIMARY_KEY' }
461
+ existing_has_primary_key = existing_indexes.any? { |i| i.name == ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME }
462
+ model_has_primary_key = model_indexes.any? { |i| i.name == ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME }
463
463
 
464
464
  add_indexes_init = model_indexes - existing_indexes
465
465
  drop_indexes_init = existing_indexes - model_indexes
466
466
  undo_add_indexes = []
467
467
  undo_drop_indexes = []
468
468
  add_indexes = add_indexes_init.map do |i|
469
- undo_add_indexes << drop_index(old_table_name, i.name) unless i.name == "PRIMARY_KEY"
469
+ undo_add_indexes << drop_index(old_table_name, i.name) unless i.name == ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME
470
470
  i.to_add_statement(new_table_name, existing_has_primary_key)
471
471
  end
472
472
  drop_indexes = drop_indexes_init.map do |i|
473
473
  undo_drop_indexes << i.to_add_statement(old_table_name, model_has_primary_key)
474
- drop_index(new_table_name, i.name) unless i.name == "PRIMARY_KEY"
474
+ drop_index(new_table_name, i.name) unless i.name == ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME
475
475
  end.compact
476
476
 
477
477
  # the order is important here - adding a :unique, for instance needs to remove then add
@@ -489,7 +489,7 @@ module Generators
489
489
  return [[], []] if Migrator.disable_indexing
490
490
 
491
491
  new_table_name = model.table_name
492
- existing_fks = ::DeclareSchema::Model::ForeignKeySpec.for_model(model, old_table_name)
492
+ existing_fks = ::DeclareSchema::Model::ForeignKeyDefinition.for_model(model, old_table_name)
493
493
  model_fks = model.constraint_specs
494
494
  add_fks = model_fks - existing_fks
495
495
  drop_fks = existing_fks - model_fks
@@ -1,4 +1,4 @@
1
- class <%= @migration_class_name %> < ActiveRecord::Migration<%= ('[4.2]' if Rails::VERSION::MAJOR >= 5) %>
1
+ class <%= @migration_class_name %> < (Rails::VERSION::MAJOR >= 5 ? ActiveRecord::Migration[4.2] : ActiveRecord::Migration)
2
2
  def self.up
3
3
  <%= @up %>
4
4
  end
@@ -10,14 +10,14 @@ RSpec.describe 'DeclareSchema API' do
10
10
 
11
11
  describe 'example models' do
12
12
  it 'generates a model' do
13
- expect(system("bundle exec rails generate declare_schema:model advert title:string body:text")).to be_truthy
13
+ generate_model 'advert', 'title:string', 'body:text'
14
14
 
15
15
  # The above will generate the test, fixture and a model file like this:
16
16
  # model_declaration = Rails::Generators.invoke('declare_schema:model', ['advert2', 'title:string', 'body:text'])
17
17
  # expect(model_declaration.first).to eq([["Advert"], nil, "app/models/advert.rb", nil,
18
18
  # [["AdvertTest"], "test/models/advert_test.rb", nil, "test/fixtures/adverts.yml"]])
19
19
 
20
- expect(File.read("#{TESTAPP_PATH}/app/models/advert.rb")).to eq(<<~EOS)
20
+ expect_model_definition_to_eq('advert', <<~EOS)
21
21
  class Advert < #{active_record_base_class}
22
22
 
23
23
  fields do
@@ -27,7 +27,8 @@ RSpec.describe 'DeclareSchema API' do
27
27
 
28
28
  end
29
29
  EOS
30
- system("rm -rf #{TESTAPP_PATH}/app/models/advert2.rb #{TESTAPP_PATH}/test/models/advert2.rb #{TESTAPP_PATH}/test/fixtures/advert2.rb")
30
+
31
+ clean_up_model('advert2')
31
32
 
32
33
  # The migration generator uses this information to create a migration.
33
34
  # The following creates and runs the migration:
@@ -36,13 +37,11 @@ RSpec.describe 'DeclareSchema API' do
36
37
 
37
38
  # We're now ready to start demonstrating the API
38
39
 
39
- Rails.application.config.autoload_paths += ["#{TESTAPP_PATH}/app/models"]
40
-
41
- $LOAD_PATH << "#{TESTAPP_PATH}/app/models"
40
+ load_models
42
41
 
43
- unless Rails::VERSION::MAJOR >= 6
42
+ if Rails::VERSION::MAJOR == 5
44
43
  # TODO: get this to work on Travis for Rails 6
45
- Rails::Generators.invoke('declare_schema:migration', %w[-n -m])
44
+ generate_migrations '-n', '-m'
46
45
  end
47
46
 
48
47
  require 'advert'
@@ -6,22 +6,20 @@ RSpec.describe 'DeclareSchema Migration Generator' do
6
6
  end
7
7
 
8
8
  it "generates nested models" do
9
- Rails::Generators.invoke('declare_schema:model', %w[alpha/beta one:string two:integer])
9
+ generate_model 'alpha/beta', 'one:string', 'two:integer'
10
10
 
11
- expect(File.exist?('app/models/alpha/beta.rb')).to be_truthy
12
-
13
- expect(File.read('app/models/alpha/beta.rb')).to eq(<<~EOS)
11
+ expect_model_definition_to_eq('alpha/beta', <<~EOS)
14
12
  class Alpha::Beta < #{active_record_base_class}
15
-
13
+
16
14
  fields do
17
15
  one :string, limit: 255
18
16
  two :integer
19
17
  end
20
-
18
+
21
19
  end
22
20
  EOS
23
21
 
24
- expect(File.read('app/models/alpha.rb')).to eq(<<~EOS)
22
+ expect_model_definition_to_eq('alpha', <<~EOS)
25
23
  module Alpha
26
24
  def self.table_name_prefix
27
25
  'alpha_'
@@ -29,9 +27,9 @@ RSpec.describe 'DeclareSchema Migration Generator' do
29
27
  end
30
28
  EOS
31
29
 
32
- expect(File.read('test/models/alpha/beta_test.rb')).to eq(<<~EOS)
30
+ expect_test_definition_to_eq('alpha/beta', <<~EOS)
33
31
  require 'test_helper'
34
-
32
+
35
33
  class Alpha::BetaTest < ActiveSupport::TestCase
36
34
  # test "the truth" do
37
35
  # assert true
@@ -39,7 +37,50 @@ RSpec.describe 'DeclareSchema Migration Generator' do
39
37
  end
40
38
  EOS
41
39
 
42
- expect(File.exist?('test/fixtures/alpha/beta.yml')).to be_truthy
40
+ case Rails::VERSION::MAJOR
41
+ when 4
42
+ expect_test_fixture_to_eq('alpha/beta', <<~EOS)
43
+ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
44
+
45
+ # This model initially had no columns defined. If you add columns to the
46
+ # model remove the '{}' from the fixture names and add the columns immediately
47
+ # below each fixture, per the syntax in the comments below
48
+ #
49
+ one: {}
50
+ # column: value
51
+ #
52
+ two: {}
53
+ # column: value
54
+ EOS
55
+ when 5
56
+ expect_test_fixture_to_eq('alpha/beta', <<~EOS)
57
+ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
58
+
59
+ # This model initially had no columns defined. If you add columns to the
60
+ # model remove the '{}' from the fixture names and add the columns immediately
61
+ # below each fixture, per the syntax in the comments below
62
+ #
63
+ one: {}
64
+ # column: value
65
+ #
66
+ two: {}
67
+ # column: value
68
+ EOS
69
+ when 6
70
+ expect_test_fixture_to_eq('alpha/beta', <<~EOS)
71
+ # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
72
+
73
+ # This model initially had no columns defined. If you add columns to the
74
+ # model remove the '{}' from the fixture names and add the columns immediately
75
+ # below each fixture, per the syntax in the comments below
76
+ #
77
+ one: {}
78
+ # column: value
79
+ #
80
+ two: {}
81
+ # column: value
82
+ EOS
83
+ end
43
84
 
44
85
  $LOAD_PATH << "#{TESTAPP_PATH}/app/models"
45
86
 
@@ -12,7 +12,7 @@ RSpec.describe 'DeclareSchema Migration Generator interactive primary key' do
12
12
  self.primary_key = "foo_id"
13
13
  end
14
14
 
15
- Rails::Generators.invoke('declare_schema:migration', %w[-n -m])
15
+ generate_migrations '-n', '-m'
16
16
  expect(Foo.primary_key).to eq('foo_id')
17
17
 
18
18
  ### migrate from
@@ -24,28 +24,30 @@ RSpec.describe 'DeclareSchema Migration Generator interactive primary key' do
24
24
  end
25
25
 
26
26
  puts "\n\e[45m Please enter 'id' (no quotes) at the next prompt \e[0m"
27
- Rails::Generators.invoke('declare_schema:migration', %w[-n -m])
27
+ generate_migrations '-n', '-m'
28
28
  expect(Foo.primary_key).to eq('id')
29
29
 
30
30
  nuke_model_class(Foo)
31
31
 
32
32
  ### migrate to
33
33
 
34
- # rename to custom primary_key
35
- class Foo < ActiveRecord::Base
36
- fields do
34
+ if Rails::VERSION::MAJOR >= 5
35
+ # rename to custom primary_key
36
+ class Foo < ActiveRecord::Base
37
+ fields do
38
+ end
39
+ self.primary_key = "foo_id"
37
40
  end
38
- self.primary_key = "foo_id"
39
- end
40
41
 
41
- puts "\n\e[45m Please enter 'drop id' (no quotes) at the next prompt \e[0m"
42
- Rails::Generators.invoke('declare_schema:migration', %w[-n -m])
43
- expect(Foo.primary_key).to eq('foo_id')
42
+ puts "\n\e[45m Please enter 'drop id' (no quotes) at the next prompt \e[0m"
43
+ generate_migrations '-n', '-m'
44
+ expect(Foo.primary_key).to eq('foo_id')
44
45
 
45
- ### ensure it doesn't cause further migrations
46
+ ### ensure it doesn't cause further migrations
46
47
 
47
- # check no further migrations
48
- up, down = Generators::DeclareSchema::Migration::Migrator.run
49
- expect(up).to eq("")
48
+ # check no further migrations
49
+ up = Generators::DeclareSchema::Migration::Migrator.run.first
50
+ expect(up).to eq("")
51
+ end
50
52
  end
51
53
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'rails'
4
+
3
5
  RSpec.describe 'DeclareSchema Migration Generator' do
4
6
  before do
5
7
  load File.expand_path('prepare_testapp.rb', __dir__)
@@ -9,14 +11,12 @@ RSpec.describe 'DeclareSchema Migration Generator' do
9
11
  it 'generates migrations' do
10
12
  ## The migration generator -- introduction
11
13
 
12
- up_down = Generators::DeclareSchema::Migration::Migrator.run
13
- expect(up_down).to eq(["", ""])
14
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to migrate_up("").and migrate_down("")
14
15
 
15
16
  class Advert < ActiveRecord::Base
16
17
  end
17
18
 
18
- up_down = Generators::DeclareSchema::Migration::Migrator.run
19
- expect(up_down).to eq(["", ""])
19
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to migrate_up("").and migrate_down("")
20
20
 
21
21
  Generators::DeclareSchema::Migration::Migrator.ignore_tables = ["green_fishes"]
22
22
 
@@ -28,17 +28,27 @@ RSpec.describe 'DeclareSchema Migration Generator' do
28
28
  name :string, limit: 255, null: true
29
29
  end
30
30
  end
31
- up, down = Generators::DeclareSchema::Migration::Migrator.run
32
- expect(up).to eq(<<~EOS.strip)
33
- create_table :adverts, id: :bigint do |t|
34
- t.string :name, limit: 255
35
- end
36
- EOS
37
- expect(down).to eq("drop_table :adverts")
31
+
32
+ up, _ = Generators::DeclareSchema::Migration::Migrator.run.tap do |migrations|
33
+ expect(migrations).to(
34
+ migrate_up(<<~EOS.strip)
35
+ create_table :adverts, id: :bigint do |t|
36
+ t.string :name, limit: 255
37
+ end
38
+ EOS
39
+ .and migrate_down("drop_table :adverts")
40
+ )
41
+ end
38
42
 
39
43
  ActiveRecord::Migration.class_eval(up)
40
44
  expect(Advert.columns.map(&:name)).to eq(["id", "name"])
41
45
 
46
+ if Rails::VERSION::MAJOR < 5
47
+ # Rails 4 sqlite driver doesn't create PK properly. Fix that by dropping and recreating.
48
+ ActiveRecord::Base.connection.execute("drop table adverts")
49
+ ActiveRecord::Base.connection.execute('CREATE TABLE "adverts" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255))')
50
+ end
51
+
42
52
  class Advert < ActiveRecord::Base
43
53
  fields do
44
54
  name :string, limit: 255, null: true
@@ -46,19 +56,20 @@ RSpec.describe 'DeclareSchema Migration Generator' do
46
56
  published_at :datetime, null: true
47
57
  end
48
58
  end
49
- up, down = migrate
50
- expect(up).to eq(<<~EOS.strip)
51
- add_column :adverts, :body, :text
52
- add_column :adverts, :published_at, :datetime
53
59
 
54
- add_index :adverts, [:id], unique: true, name: 'PRIMARY_KEY'
55
- EOS
56
- # TODO: ^ add_index should not be there?
60
+ Advert.connection.schema_cache.clear!
61
+ Advert.reset_column_information
57
62
 
58
- expect(down).to eq(<<~EOS.strip)
59
- remove_column :adverts, :body
60
- remove_column :adverts, :published_at
61
- EOS
63
+ expect(migrate).to(
64
+ migrate_up(<<~EOS.strip)
65
+ add_column :adverts, :body, :text
66
+ add_column :adverts, :published_at, :datetime
67
+ EOS
68
+ .and migrate_down(<<~EOS.strip)
69
+ remove_column :adverts, :body
70
+ remove_column :adverts, :published_at
71
+ EOS
72
+ )
62
73
 
63
74
  Advert.field_specs.clear # not normally needed
64
75
  class Advert < ActiveRecord::Base
@@ -68,9 +79,11 @@ RSpec.describe 'DeclareSchema Migration Generator' do
68
79
  end
69
80
  end
70
81
 
71
- up, down = migrate
72
- expect(up).to eq("remove_column :adverts, :published_at")
73
- expect(down).to eq("add_column :adverts, :published_at, :datetime")
82
+ expect(migrate).to(
83
+ migrate_up("remove_column :adverts, :published_at").and(
84
+ migrate_down("add_column :adverts, :published_at, :datetime")
85
+ )
86
+ )
74
87
 
75
88
  nuke_model_class(Advert)
76
89
  class Advert < ActiveRecord::Base
@@ -80,20 +93,22 @@ RSpec.describe 'DeclareSchema Migration Generator' do
80
93
  end
81
94
  end
82
95
 
83
- up, down = Generators::DeclareSchema::Migration::Migrator.run
84
- expect(up).to eq(<<~EOS.strip)
85
- add_column :adverts, :title, :string, limit: 255
86
- remove_column :adverts, :name
87
- EOS
88
-
89
- expect(down).to eq(<<~EOS.strip)
90
- remove_column :adverts, :title
91
- add_column :adverts, :name, :string, limit: 255
92
- EOS
93
-
94
- up, down = Generators::DeclareSchema::Migration::Migrator.run(adverts: { name: :title })
95
- expect(up).to eq("rename_column :adverts, :name, :title")
96
- expect(down).to eq("rename_column :adverts, :title, :name")
96
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
97
+ migrate_up(<<~EOS.strip)
98
+ add_column :adverts, :title, :string, limit: 255
99
+ remove_column :adverts, :name
100
+ EOS
101
+ .and migrate_down(<<~EOS.strip)
102
+ remove_column :adverts, :title
103
+ add_column :adverts, :name, :string, limit: 255
104
+ EOS
105
+ )
106
+
107
+ expect(Generators::DeclareSchema::Migration::Migrator.run(adverts: { name: :title })).to(
108
+ migrate_up("rename_column :adverts, :name, :title").and(
109
+ migrate_down("rename_column :adverts, :title, :name")
110
+ )
111
+ )
97
112
 
98
113
  migrate
99
114
 
@@ -104,9 +119,11 @@ RSpec.describe 'DeclareSchema Migration Generator' do
104
119
  end
105
120
  end
106
121
 
107
- up_down = Generators::DeclareSchema::Migration::Migrator.run
108
- expect(up_down).to eq(["change_column :adverts, :title, :text",
109
- "change_column :adverts, :title, :string, limit: 255"])
122
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
123
+ migrate_up("change_column :adverts, :title, :text").and(
124
+ migrate_down("change_column :adverts, :title, :string, limit: 255")
125
+ )
126
+ )
110
127
 
111
128
  class Advert < ActiveRecord::Base
112
129
  fields do
@@ -115,11 +132,14 @@ RSpec.describe 'DeclareSchema Migration Generator' do
115
132
  end
116
133
  end
117
134
 
118
- up, down = migrate
119
- expect(up.split(',').slice(0,3).join(',')).to eq('change_column :adverts, :title, :string')
120
- expect(up.split(',').slice(3,2).sort.join(',')).to eq(" default: \"Untitled\", limit: 255")
121
- expect(down).to eq("change_column :adverts, :title, :string, limit: 255")
122
-
135
+ expect(migrate).to(
136
+ migrate_up(<<~EOS.strip)
137
+ change_column :adverts, :title, :string, limit: 255, default: "Untitled"
138
+ EOS
139
+ .and migrate_down(<<~EOS.strip)
140
+ change_column :adverts, :title, :string, limit: 255
141
+ EOS
142
+ )
123
143
 
124
144
  ### Limits
125
145
 
@@ -129,8 +149,9 @@ RSpec.describe 'DeclareSchema Migration Generator' do
129
149
  end
130
150
  end
131
151
 
132
- up, down = Generators::DeclareSchema::Migration::Migrator.run
133
- expect(up).to eq("add_column :adverts, :price, :integer, limit: 2")
152
+ up, _ = Generators::DeclareSchema::Migration::Migrator.run.tap do |migrations|
153
+ expect(migrations).to migrate_up("add_column :adverts, :price, :integer, limit: 2")
154
+ end
134
155
 
135
156
  # Now run the migration, then change the limit:
136
157
 
@@ -141,9 +162,14 @@ RSpec.describe 'DeclareSchema Migration Generator' do
141
162
  end
142
163
  end
143
164
 
144
- up, down = Generators::DeclareSchema::Migration::Migrator.run
145
- expect(up).to eq("change_column :adverts, :price, :integer, limit: 3")
146
- expect(down).to eq("change_column :adverts, :price, :integer, limit: 2")
165
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
166
+ migrate_up(<<~EOS.strip)
167
+ change_column :adverts, :price, :integer, limit: 3
168
+ EOS
169
+ .and migrate_down(<<~EOS.strip)
170
+ change_column :adverts, :price, :integer, limit: 2
171
+ EOS
172
+ )
147
173
 
148
174
  # Note that limit on a decimal column is ignored (use :scale and :precision)
149
175
 
@@ -154,8 +180,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
154
180
  end
155
181
  end
156
182
 
157
- up, down = Generators::DeclareSchema::Migration::Migrator.run
158
- expect(up).to eq("add_column :adverts, :price, :decimal")
183
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to migrate_up("add_column :adverts, :price, :decimal")
159
184
 
160
185
  # Limits are generally not needed for `text` fields, because by default, `text` fields will use the maximum size
161
186
  # allowed for that database type (0xffffffff for LONGTEXT in MySQL unlimited in Postgres, 1 billion in Sqlite).
@@ -170,12 +195,13 @@ RSpec.describe 'DeclareSchema Migration Generator' do
170
195
  end
171
196
  end
172
197
 
173
- up, down = Generators::DeclareSchema::Migration::Migrator.run
174
- expect(up).to eq(<<~EOS.strip)
175
- add_column :adverts, :price, :decimal
176
- add_column :adverts, :notes, :text, null: false
177
- add_column :adverts, :description, :text, null: false
178
- EOS
198
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
199
+ migrate_up(<<~EOS.strip)
200
+ add_column :adverts, :price, :decimal
201
+ add_column :adverts, :notes, :text, null: false
202
+ add_column :adverts, :description, :text, null: false
203
+ EOS
204
+ )
179
205
 
180
206
  # (There is no limit on `add_column ... :description` above since these tests are run against SQLite.)
181
207
 
@@ -194,11 +220,12 @@ RSpec.describe 'DeclareSchema Migration Generator' do
194
220
  end
195
221
  end
196
222
 
197
- up, down = Generators::DeclareSchema::Migration::Migrator.run
198
- expect(up).to eq(<<~EOS.strip)
199
- add_column :adverts, :notes, :text, null: false, limit: 4294967295
200
- add_column :adverts, :description, :text, null: false, limit: 255
201
- EOS
223
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
224
+ migrate_up(<<~EOS.strip)
225
+ add_column :adverts, :notes, :text, null: false, limit: 4294967295
226
+ add_column :adverts, :description, :text, null: false, limit: 255
227
+ EOS
228
+ )
202
229
 
203
230
  Advert.field_specs.delete :notes
204
231
 
@@ -237,9 +264,14 @@ RSpec.describe 'DeclareSchema Migration Generator' do
237
264
  end
238
265
  end
239
266
 
240
- up, down = Generators::DeclareSchema::Migration::Migrator.run
241
- expect(up).to eq("change_column :adverts, :description, :text, limit: 4294967295, null: false")
242
- expect(down).to eq("change_column :adverts, :description, :text")
267
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
268
+ migrate_up(<<~EOS.strip)
269
+ change_column :adverts, :description, :text, limit: 4294967295, null: false
270
+ EOS
271
+ .and migrate_down(<<~EOS.strip)
272
+ change_column :adverts, :description, :text
273
+ EOS
274
+ )
243
275
 
244
276
  # TODO TECH-4814: The above test should have this output:
245
277
  # TODO => "change_column :adverts, :description, :text, limit: 255
@@ -252,9 +284,14 @@ RSpec.describe 'DeclareSchema Migration Generator' do
252
284
  end
253
285
  end
254
286
 
255
- up, down = Generators::DeclareSchema::Migration::Migrator.run
256
- expect(up).to eq("change_column :adverts, :description, :text, limit: 4294967295, null: false")
257
- expect(down).to eq("change_column :adverts, :description, :text")
287
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
288
+ migrate_up(<<~EOS.strip)
289
+ change_column :adverts, :description, :text, limit: 4294967295, null: false
290
+ EOS
291
+ .and migrate_down(<<~EOS.strip)
292
+ change_column :adverts, :description, :text
293
+ EOS
294
+ )
258
295
  ::DeclareSchema::Model::FieldSpec::instance_variable_set(:@mysql_text_limits, false)
259
296
 
260
297
  Advert.field_specs.clear
@@ -266,7 +303,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
266
303
  end
267
304
  end
268
305
 
269
- up, down = Generators::DeclareSchema::Migration::Migrator.run
306
+ up = Generators::DeclareSchema::Migration::Migrator.run.first
270
307
  ActiveRecord::Migration.class_eval up
271
308
  Advert.connection.schema_cache.clear!
272
309
  Advert.reset_column_information
@@ -278,63 +315,82 @@ RSpec.describe 'DeclareSchema Migration Generator' do
278
315
 
279
316
  class Category < ActiveRecord::Base; end
280
317
  class Advert < ActiveRecord::Base
318
+ fields do
319
+ name :string, limit: 255, null: true
320
+ end
281
321
  belongs_to :category
282
322
  end
283
323
 
284
- up, down = Generators::DeclareSchema::Migration::Migrator.run
285
- expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
286
- add_column :adverts, :category_id, :integer, limit: 8, null: false
287
- add_index :adverts, [:category_id], name: 'on_category_id'
288
- EOS
289
- expect(down.sub(/\n+/, "\n")).to eq(<<~EOS.strip)
290
- remove_column :adverts, :category_id
291
- remove_index :adverts, name: :on_category_id rescue ActiveRecord::StatementInvalid
292
- EOS
324
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
325
+ migrate_up(<<~EOS.strip)
326
+ add_column :adverts, :category_id, :integer, limit: 8, null: false
327
+
328
+ add_index :adverts, [:category_id], name: 'on_category_id'
329
+ EOS
330
+ .and migrate_down(<<~EOS.strip)
331
+ remove_column :adverts, :category_id
332
+
333
+ remove_index :adverts, name: :on_category_id rescue ActiveRecord::StatementInvalid
334
+ EOS
335
+ )
293
336
 
294
337
  Advert.field_specs.delete(:category_id)
295
- Advert.index_specs.delete_if {|spec| spec.fields==["category_id"]}
338
+ Advert.index_definitions.delete_if { |spec| spec.fields==["category_id"] }
296
339
 
297
340
  # If you specify a custom foreign key, the migration generator observes that:
298
341
 
299
342
  class Category < ActiveRecord::Base; end
300
343
  class Advert < ActiveRecord::Base
344
+ fields { }
301
345
  belongs_to :category, foreign_key: "c_id", class_name: 'Category'
302
346
  end
303
- up, down = Generators::DeclareSchema::Migration::Migrator.run
304
- expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
305
- add_column :adverts, :c_id, :integer, limit: 8, null: false
306
- add_index :adverts, [:c_id], name: 'on_c_id'
307
- EOS
347
+
348
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
349
+ migrate_up(<<~EOS.strip)
350
+ add_column :adverts, :c_id, :integer, limit: 8, null: false
351
+
352
+ add_index :adverts, [:c_id], name: 'on_c_id'
353
+ EOS
354
+ )
308
355
 
309
356
  Advert.field_specs.delete(:c_id)
310
- Advert.index_specs.delete_if { |spec| spec.fields == ["c_id"] }
357
+ Advert.index_definitions.delete_if { |spec| spec.fields == ["c_id"] }
311
358
 
312
359
  # You can avoid generating the index by specifying `index: false`
313
360
 
314
361
  class Category < ActiveRecord::Base; end
315
362
  class Advert < ActiveRecord::Base
363
+ fields { }
316
364
  belongs_to :category, index: false
317
365
  end
318
- up, down = Generators::DeclareSchema::Migration::Migrator.run
319
- expect(up.gsub(/\n+/, "\n")).to eq("add_column :adverts, :category_id, :integer, limit: 8, null: false")
366
+
367
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
368
+ migrate_up(<<~EOS.strip)
369
+ add_column :adverts, :category_id, :integer, limit: 8, null: false
370
+ EOS
371
+ )
320
372
 
321
373
  Advert.field_specs.delete(:category_id)
322
- Advert.index_specs.delete_if { |spec| spec.fields == ["category_id"] }
374
+ Advert.index_definitions.delete_if { |spec| spec.fields == ["category_id"] }
323
375
 
324
376
  # You can specify the index name with :index
325
377
 
326
378
  class Category < ActiveRecord::Base; end
327
379
  class Advert < ActiveRecord::Base
380
+ fields { }
328
381
  belongs_to :category, index: 'my_index'
329
382
  end
330
- up, down = Generators::DeclareSchema::Migration::Migrator.run
331
- expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
332
- add_column :adverts, :category_id, :integer, limit: 8, null: false
333
- add_index :adverts, [:category_id], name: 'my_index'
334
- EOS
383
+
384
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
385
+ migrate_up(<<~EOS.strip)
386
+ add_column :adverts, :category_id, :integer, limit: 8, null: false
387
+
388
+ add_index :adverts, [:category_id], name: 'my_index'
389
+ EOS
390
+ )
335
391
 
336
392
  Advert.field_specs.delete(:category_id)
337
- Advert.index_specs.delete_if { |spec| spec.fields == ["category_id"] }
393
+ Advert.index_definitions.delete_if { |spec| spec.fields == ["category_id"] }
338
394
 
339
395
  ### Timestamps and Optimimistic Locking
340
396
 
@@ -347,17 +403,19 @@ RSpec.describe 'DeclareSchema Migration Generator' do
347
403
  optimistic_lock
348
404
  end
349
405
  end
350
- up, down = Generators::DeclareSchema::Migration::Migrator.run
351
- expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
352
- add_column :adverts, :created_at, :datetime
353
- add_column :adverts, :updated_at, :datetime
354
- add_column :adverts, :lock_version, :integer, null: false, default: 1
355
- EOS
356
- expect(down.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
357
- remove_column :adverts, :created_at
358
- remove_column :adverts, :updated_at
359
- remove_column :adverts, :lock_version
360
- EOS
406
+
407
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
408
+ migrate_up(<<~EOS.strip)
409
+ add_column :adverts, :created_at, :datetime
410
+ add_column :adverts, :updated_at, :datetime
411
+ add_column :adverts, :lock_version, :integer, null: false, default: 1
412
+ EOS
413
+ .and migrate_down(<<~EOS.strip)
414
+ remove_column :adverts, :created_at
415
+ remove_column :adverts, :updated_at
416
+ remove_column :adverts, :lock_version
417
+ EOS
418
+ )
361
419
 
362
420
  Advert.field_specs.delete(:updated_at)
363
421
  Advert.field_specs.delete(:created_at)
@@ -372,13 +430,16 @@ RSpec.describe 'DeclareSchema Migration Generator' do
372
430
  title :string, index: true, limit: 255, null: true
373
431
  end
374
432
  end
375
- up, down = Generators::DeclareSchema::Migration::Migrator.run
376
- expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
377
- add_column :adverts, :title, :string, limit: 255
378
- add_index :adverts, [:title], name: 'on_title'
379
- EOS
380
433
 
381
- Advert.index_specs.delete_if { |spec| spec.fields==["title"] }
434
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
435
+ migrate_up(<<~EOS.strip)
436
+ add_column :adverts, :title, :string, limit: 255
437
+
438
+ add_index :adverts, [:title], name: 'on_title'
439
+ EOS
440
+ )
441
+
442
+ Advert.index_definitions.delete_if { |spec| spec.fields==["title"] }
382
443
 
383
444
  # You can ask for a unique index
384
445
 
@@ -387,13 +448,16 @@ RSpec.describe 'DeclareSchema Migration Generator' do
387
448
  title :string, index: true, unique: true, null: true, limit: 255
388
449
  end
389
450
  end
390
- up, down = Generators::DeclareSchema::Migration::Migrator.run
391
- expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
392
- add_column :adverts, :title, :string, limit: 255
393
- add_index :adverts, [:title], unique: true, name: 'on_title'
394
- EOS
395
451
 
396
- Advert.index_specs.delete_if { |spec| spec.fields == ["title"] }
452
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
453
+ migrate_up(<<~EOS.strip)
454
+ add_column :adverts, :title, :string, limit: 255
455
+
456
+ add_index :adverts, [:title], unique: true, name: 'on_title'
457
+ EOS
458
+ )
459
+
460
+ Advert.index_definitions.delete_if { |spec| spec.fields == ["title"] }
397
461
 
398
462
  # You can specify the name for the index
399
463
 
@@ -402,52 +466,64 @@ RSpec.describe 'DeclareSchema Migration Generator' do
402
466
  title :string, index: 'my_index', limit: 255, null: true
403
467
  end
404
468
  end
405
- up, down = Generators::DeclareSchema::Migration::Migrator.run
406
- expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
407
- add_column :adverts, :title, :string, limit: 255
408
- add_index :adverts, [:title], name: 'my_index'
409
- EOS
410
469
 
411
- Advert.index_specs.delete_if {|spec| spec.fields==["title"]}
470
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
471
+ migrate_up(<<~EOS.strip)
472
+ add_column :adverts, :title, :string, limit: 255
473
+
474
+ add_index :adverts, [:title], name: 'my_index'
475
+ EOS
476
+ )
477
+
478
+ Advert.index_definitions.delete_if { |spec| spec.fields==["title"] }
412
479
 
413
480
  # You can ask for an index outside of the fields block
414
481
 
415
482
  class Advert < ActiveRecord::Base
416
483
  index :title
417
484
  end
418
- up, down = Generators::DeclareSchema::Migration::Migrator.run
419
- expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
420
- add_column :adverts, :title, :string, limit: 255
421
- add_index :adverts, [:title], name: 'on_title'
422
- EOS
423
485
 
424
- Advert.index_specs.delete_if { |spec| spec.fields == ["title"] }
486
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
487
+ migrate_up(<<~EOS.strip)
488
+ add_column :adverts, :title, :string, limit: 255
489
+
490
+ add_index :adverts, [:title], name: 'on_title'
491
+ EOS
492
+ )
493
+
494
+ Advert.index_definitions.delete_if { |spec| spec.fields == ["title"] }
425
495
 
426
496
  # The available options for the index function are `:unique` and `:name`
427
497
 
428
498
  class Advert < ActiveRecord::Base
429
499
  index :title, unique: true, name: 'my_index'
430
500
  end
431
- up, down = Generators::DeclareSchema::Migration::Migrator.run
432
- expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
433
- add_column :adverts, :title, :string, limit: 255
434
- add_index :adverts, [:title], unique: true, name: 'my_index'
435
- EOS
436
501
 
437
- Advert.index_specs.delete_if { |spec| spec.fields == ["title"] }
502
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
503
+ migrate_up(<<~EOS.strip)
504
+ add_column :adverts, :title, :string, limit: 255
505
+
506
+ add_index :adverts, [:title], unique: true, name: 'my_index'
507
+ EOS
508
+ )
509
+
510
+ Advert.index_definitions.delete_if { |spec| spec.fields == ["title"] }
438
511
 
439
512
  # You can create an index on more than one field
440
513
 
441
514
  class Advert < ActiveRecord::Base
442
515
  index [:title, :category_id]
443
516
  end
444
- up, down = Generators::DeclareSchema::Migration::Migrator.run
445
- expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
446
- add_column :adverts, :title, :string, limit: 255
447
- add_index :adverts, [:title, :category_id], name: 'on_title_and_category_id'
448
- EOS
449
517
 
450
- Advert.index_specs.delete_if { |spec| spec.fields==["title", "category_id"] }
518
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
519
+ migrate_up(<<~EOS.strip)
520
+ add_column :adverts, :title, :string, limit: 255
521
+
522
+ add_index :adverts, [:title, :category_id], name: 'on_title_and_category_id'
523
+ EOS
524
+ )
525
+
526
+ Advert.index_definitions.delete_if { |spec| spec.fields==["title", "category_id"] }
451
527
 
452
528
  # Finally, you can specify that the migration generator should completely ignore an
453
529
  # index by passing its name to ignore_index in the model.
@@ -468,19 +544,24 @@ RSpec.describe 'DeclareSchema Migration Generator' do
468
544
  Advert.connection.schema_cache.clear!
469
545
  Advert.reset_column_information
470
546
 
471
- up, down = Generators::DeclareSchema::Migration::Migrator.run("adverts" => "ads")
472
- expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
473
- rename_table :adverts, :ads
474
- add_column :ads, :title, :string, limit: 255
475
- add_column :ads, :body, :text
476
- add_index :ads, [:id], unique: true, name: 'PRIMARY_KEY'
477
- EOS
478
- expect(down.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
479
- remove_column :ads, :title
480
- remove_column :ads, :body
481
- rename_table :ads, :adverts
482
- add_index :adverts, [:id], unique: true, name: 'PRIMARY_KEY'
483
- EOS
547
+ expect(Generators::DeclareSchema::Migration::Migrator.run("adverts" => "ads")).to(
548
+ migrate_up(<<~EOS.strip)
549
+ rename_table :adverts, :ads
550
+
551
+ add_column :ads, :title, :string, limit: 255
552
+ add_column :ads, :body, :text
553
+
554
+ add_index :ads, [:id], unique: true, name: 'PRIMARY'
555
+ EOS
556
+ .and migrate_down(<<~EOS.strip)
557
+ remove_column :ads, :title
558
+ remove_column :ads, :body
559
+
560
+ rename_table :ads, :adverts
561
+
562
+ add_index :adverts, [:id], unique: true, name: 'PRIMARY'
563
+ EOS
564
+ )
484
565
 
485
566
  # Set the table name back to what it should be and confirm we're in sync:
486
567
 
@@ -504,21 +585,27 @@ RSpec.describe 'DeclareSchema Migration Generator' do
504
585
  body :text, null: true
505
586
  end
506
587
  end
507
- up, down = Generators::DeclareSchema::Migration::Migrator.run("adverts" => "advertisements")
508
- expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
509
- rename_table :adverts, :advertisements
510
- add_column :advertisements, :title, :string, limit: 255
511
- add_column :advertisements, :body, :text
512
- remove_column :advertisements, :name
513
- add_index :advertisements, [:id], unique: true, name: 'PRIMARY_KEY'
514
- EOS
515
- expect(down.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
516
- remove_column :advertisements, :title
517
- remove_column :advertisements, :body
518
- add_column :adverts, :name, :string, limit: 255
519
- rename_table :advertisements, :adverts
520
- add_index :adverts, [:id], unique: true, name: 'PRIMARY_KEY'
521
- EOS
588
+
589
+ expect(Generators::DeclareSchema::Migration::Migrator.run("adverts" => "advertisements")).to(
590
+ migrate_up(<<~EOS.strip)
591
+ rename_table :adverts, :advertisements
592
+
593
+ add_column :advertisements, :title, :string, limit: 255
594
+ add_column :advertisements, :body, :text
595
+ remove_column :advertisements, :name
596
+
597
+ add_index :advertisements, [:id], unique: true, name: 'PRIMARY'
598
+ EOS
599
+ .and migrate_down(<<~EOS.strip)
600
+ remove_column :advertisements, :title
601
+ remove_column :advertisements, :body
602
+ add_column :adverts, :name, :string, limit: 255
603
+
604
+ rename_table :advertisements, :adverts
605
+
606
+ add_index :adverts, [:id], unique: true, name: 'PRIMARY'
607
+ EOS
608
+ )
522
609
 
523
610
  ### Drop a table
524
611
 
@@ -528,9 +615,24 @@ RSpec.describe 'DeclareSchema Migration Generator' do
528
615
 
529
616
  # Dropping tables is where the automatic down-migration really comes in handy:
530
617
 
531
- up, down = Generators::DeclareSchema::Migration::Migrator.run
532
- expect(up).to eq("drop_table :adverts")
533
- expect(down.gsub(/,.*/m, '')).to eq("create_table \"adverts\"")
618
+ rails4_table_create = <<~EOS.strip
619
+ create_table "adverts", force: :cascade do |t|
620
+ t.string "name", limit: 255
621
+ end
622
+ EOS
623
+
624
+ rails5_table_create = <<~EOS.strip
625
+ create_table "adverts", id: :integer, force: :cascade do |t|
626
+ t.string "name", limit: 255
627
+ end
628
+ EOS
629
+
630
+ expect(Generators::DeclareSchema::Migration::Migrator.run).to(
631
+ migrate_up(<<~EOS.strip)
632
+ drop_table :adverts
633
+ EOS
634
+ .and migrate_down(Rails::VERSION::MAJOR >= 5 ? rails5_table_create : rails4_table_create)
635
+ )
534
636
 
535
637
  ## STI
536
638
 
@@ -544,27 +646,33 @@ RSpec.describe 'DeclareSchema Migration Generator' do
544
646
  title :string, default: "Untitled", limit: 255, null: true
545
647
  end
546
648
  end
547
- up, down = Generators::DeclareSchema::Migration::Migrator.run
649
+ up = Generators::DeclareSchema::Migration::Migrator.run.first
548
650
  ActiveRecord::Migration.class_eval(up)
549
651
 
550
652
  class FancyAdvert < Advert
551
653
  end
552
654
  class SuperFancyAdvert < FancyAdvert
553
655
  end
554
- up, down = Generators::DeclareSchema::Migration::Migrator.run
555
- expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
556
- add_column :adverts, :type, :string, limit: 255
557
- add_index :adverts, [:type], name: 'on_type'
558
- EOS
559
- expect(down.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
560
- remove_column :adverts, :type
561
- remove_index :adverts, name: :on_type rescue ActiveRecord::StatementInvalid
562
- EOS
656
+
657
+ up, _ = Generators::DeclareSchema::Migration::Migrator.run do |migrations|
658
+ expect(migrations).to(
659
+ migrate_up(<<~EOS.strip)
660
+ add_column :adverts, :type, :string, limit: 255
661
+
662
+ add_index :adverts, [:type], name: 'on_type'
663
+ EOS
664
+ .and migrate_down(<<~EOS.strip)
665
+ remove_column :adverts, :type
666
+
667
+ remove_index :adverts, name: :on_type rescue ActiveRecord::StatementInvalid
668
+ EOS
669
+ )
670
+ end
563
671
 
564
672
  Advert.field_specs.delete(:type)
565
673
  nuke_model_class(SuperFancyAdvert)
566
674
  nuke_model_class(FancyAdvert)
567
- Advert.index_specs.delete_if { |spec| spec.fields==["type"] }
675
+ Advert.index_definitions.delete_if { |spec| spec.fields==["type"] }
568
676
 
569
677
  ## Coping with multiple changes
570
678
 
@@ -592,16 +700,17 @@ RSpec.describe 'DeclareSchema Migration Generator' do
592
700
  body :text, null: true
593
701
  end
594
702
  end
595
- up, down = Generators::DeclareSchema::Migration::Migrator.run(adverts: { title: :name })
596
- expect(up).to eq(<<~EOS.strip)
597
- rename_column :adverts, :title, :name
598
- change_column :adverts, :name, :string, limit: 255, default: \"No Name\"
599
- EOS
600
703
 
601
- expect(down).to eq(<<~EOS.strip)
602
- rename_column :adverts, :name, :title
603
- change_column :adverts, :title, :string, limit: 255, default: \"Untitled\"
604
- EOS
704
+ expect(Generators::DeclareSchema::Migration::Migrator.run(adverts: { title: :name })).to(
705
+ migrate_up(<<~EOS.strip)
706
+ rename_column :adverts, :title, :name
707
+ change_column :adverts, :name, :string, limit: 255, default: "No Name"
708
+ EOS
709
+ .and migrate_down(<<~EOS.strip)
710
+ rename_column :adverts, :name, :title
711
+ change_column :adverts, :title, :string, limit: 255, default: "Untitled"
712
+ EOS
713
+ )
605
714
 
606
715
  ### Rename a table and add a column
607
716
 
@@ -613,13 +722,17 @@ RSpec.describe 'DeclareSchema Migration Generator' do
613
722
  created_at :datetime
614
723
  end
615
724
  end
616
- up, down = Generators::DeclareSchema::Migration::Migrator.run(adverts: :ads)
617
- expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
618
- rename_table :adverts, :ads
619
- add_column :ads, :created_at, :datetime, null: false
620
- change_column :ads, :title, :string, limit: 255, null: false, default: \"Untitled\"
621
- add_index :ads, [:id], unique: true, name: 'PRIMARY_KEY'
622
- EOS
725
+
726
+ expect(Generators::DeclareSchema::Migration::Migrator.run(adverts: :ads)).to(
727
+ migrate_up(<<~EOS.strip)
728
+ rename_table :adverts, :ads
729
+
730
+ add_column :ads, :created_at, :datetime, null: false
731
+ change_column :ads, :title, :string, limit: 255, null: false, default: \"Untitled\"
732
+
733
+ add_index :ads, [:id], unique: true, name: 'PRIMARY'
734
+ EOS
735
+ )
623
736
 
624
737
  class Advert < ActiveRecord::Base
625
738
  fields do
@@ -640,11 +753,14 @@ RSpec.describe 'DeclareSchema Migration Generator' do
640
753
  end
641
754
  self.primary_key = "advert_id"
642
755
  end
643
- up, _down = Generators::DeclareSchema::Migration::Migrator.run(adverts: { id: :advert_id })
644
- expect(up.gsub(/\n+/, "\n")).to eq(<<~EOS.strip)
645
- rename_column :adverts, :id, :advert_id
646
- add_index :adverts, [:advert_id], unique: true, name: 'PRIMARY_KEY'
647
- EOS
756
+
757
+ expect(Generators::DeclareSchema::Migration::Migrator.run(adverts: { id: :advert_id })).to(
758
+ migrate_up(<<~EOS.strip)
759
+ rename_column :adverts, :id, :advert_id
760
+
761
+ add_index :adverts, [:advert_id], unique: true, name: 'PRIMARY'
762
+ EOS
763
+ )
648
764
 
649
765
  nuke_model_class(Advert)
650
766
  ActiveRecord::Base.connection.execute("drop table `adverts`;")
@@ -682,5 +798,299 @@ RSpec.describe 'DeclareSchema Migration Generator' do
682
798
  ActiveRecord::Migration.class_eval(up)
683
799
  expect(Ad.field_specs['company'].options[:validates].inspect).to eq("{:presence=>true, :uniqueness=>{:case_sensitive=>false}}")
684
800
  end
685
- end
686
801
 
802
+ describe 'serialize' do
803
+ before do
804
+ class Ad < ActiveRecord::Base
805
+ @serialize_args = []
806
+
807
+ class << self
808
+ attr_reader :serialize_args
809
+
810
+ def serialize(*args)
811
+ @serialize_args << args
812
+ end
813
+ end
814
+ end
815
+ end
816
+
817
+ describe 'untyped' do
818
+ it 'allows serialize: true' do
819
+ class Ad < ActiveRecord::Base
820
+ fields do
821
+ allow_list :text, limit: 0xFFFF, serialize: true
822
+ end
823
+ end
824
+
825
+ expect(Ad.serialize_args).to eq([[:allow_list]])
826
+ end
827
+
828
+ it 'converts defaults with .to_yaml' do
829
+ class Ad < ActiveRecord::Base
830
+ fields do
831
+ allow_list :string, limit: 255, serialize: true, null: true, default: []
832
+ allow_hash :string, limit: 255, serialize: true, null: true, default: {}
833
+ allow_string :string, limit: 255, serialize: true, null: true, default: ['abc']
834
+ allow_null :string, limit: 255, serialize: true, null: true, default: nil
835
+ end
836
+ end
837
+
838
+ expect(Ad.field_specs['allow_list'].default).to eq("--- []\n")
839
+ expect(Ad.field_specs['allow_hash'].default).to eq("--- {}\n")
840
+ expect(Ad.field_specs['allow_string'].default).to eq("---\n- abc\n")
841
+ expect(Ad.field_specs['allow_null'].default).to eq(nil)
842
+ end
843
+ end
844
+
845
+ describe 'Array' do
846
+ it 'allows serialize: Array' do
847
+ class Ad < ActiveRecord::Base
848
+ fields do
849
+ allow_list :string, limit: 255, serialize: Array, null: true
850
+ end
851
+ end
852
+
853
+ expect(Ad.serialize_args).to eq([[:allow_list, Array]])
854
+ end
855
+
856
+ it 'allows Array defaults' do
857
+ class Ad < ActiveRecord::Base
858
+ fields do
859
+ allow_list :string, limit: 255, serialize: Array, null: true, default: [2]
860
+ allow_string :string, limit: 255, serialize: Array, null: true, default: ['abc']
861
+ allow_empty :string, limit: 255, serialize: Array, null: true, default: []
862
+ allow_null :string, limit: 255, serialize: Array, null: true, default: nil
863
+ end
864
+ end
865
+
866
+ expect(Ad.field_specs['allow_list'].default).to eq("---\n- 2\n")
867
+ expect(Ad.field_specs['allow_string'].default).to eq("---\n- abc\n")
868
+ expect(Ad.field_specs['allow_empty'].default).to eq(nil)
869
+ expect(Ad.field_specs['allow_null'].default).to eq(nil)
870
+ end
871
+ end
872
+
873
+ describe 'Hash' do
874
+ it 'allows serialize: Hash' do
875
+ class Ad < ActiveRecord::Base
876
+ fields do
877
+ allow_list :string, limit: 255, serialize: Hash, null: true
878
+ end
879
+ end
880
+
881
+ expect(Ad.serialize_args).to eq([[:allow_list, Hash]])
882
+ end
883
+
884
+ it 'allows Hash defaults' do
885
+ class Ad < ActiveRecord::Base
886
+ fields do
887
+ allow_loc :string, limit: 255, serialize: Hash, null: true, default: { 'state' => 'CA' }
888
+ allow_hash :string, limit: 255, serialize: Hash, null: true, default: {}
889
+ allow_null :string, limit: 255, serialize: Hash, null: true, default: nil
890
+ end
891
+ end
892
+
893
+ expect(Ad.field_specs['allow_loc'].default).to eq("---\nstate: CA\n")
894
+ expect(Ad.field_specs['allow_hash'].default).to eq(nil)
895
+ expect(Ad.field_specs['allow_null'].default).to eq(nil)
896
+ end
897
+ end
898
+
899
+ describe 'JSON' do
900
+ it 'allows serialize: JSON' do
901
+ class Ad < ActiveRecord::Base
902
+ fields do
903
+ allow_list :string, limit: 255, serialize: JSON
904
+ end
905
+ end
906
+
907
+ expect(Ad.serialize_args).to eq([[:allow_list, JSON]])
908
+ end
909
+
910
+ it 'allows JSON defaults' do
911
+ class Ad < ActiveRecord::Base
912
+ fields do
913
+ allow_hash :string, limit: 255, serialize: JSON, null: true, default: { 'state' => 'CA' }
914
+ allow_empty_array :string, limit: 255, serialize: JSON, null: true, default: []
915
+ allow_empty_hash :string, limit: 255, serialize: JSON, null: true, default: {}
916
+ allow_null :string, limit: 255, serialize: JSON, null: true, default: nil
917
+ end
918
+ end
919
+
920
+ expect(Ad.field_specs['allow_hash'].default).to eq("{\"state\":\"CA\"}")
921
+ expect(Ad.field_specs['allow_empty_array'].default).to eq("[]")
922
+ expect(Ad.field_specs['allow_empty_hash'].default).to eq("{}")
923
+ expect(Ad.field_specs['allow_null'].default).to eq(nil)
924
+ end
925
+ end
926
+
927
+ class ValueClass
928
+ delegate :present?, :inspect, to: :@value
929
+
930
+ def initialize(value)
931
+ @value = value
932
+ end
933
+
934
+ class << self
935
+ def dump(object)
936
+ if object&.present?
937
+ object.inspect
938
+ end
939
+ end
940
+
941
+ def load(serialized)
942
+ if serialized
943
+ raise 'not used ???'
944
+ end
945
+ end
946
+ end
947
+ end
948
+
949
+ describe 'custom coder' do
950
+ it 'allows serialize: ValueClass' do
951
+ class Ad < ActiveRecord::Base
952
+ fields do
953
+ allow_list :string, limit: 255, serialize: ValueClass
954
+ end
955
+ end
956
+
957
+ expect(Ad.serialize_args).to eq([[:allow_list, ValueClass]])
958
+ end
959
+
960
+ it 'allows ValueClass defaults' do
961
+ class Ad < ActiveRecord::Base
962
+ fields do
963
+ allow_hash :string, limit: 255, serialize: ValueClass, null: true, default: ValueClass.new([2])
964
+ allow_empty_array :string, limit: 255, serialize: ValueClass, null: true, default: ValueClass.new([])
965
+ allow_null :string, limit: 255, serialize: ValueClass, null: true, default: nil
966
+ end
967
+ end
968
+
969
+ expect(Ad.field_specs['allow_hash'].default).to eq("[2]")
970
+ expect(Ad.field_specs['allow_empty_array'].default).to eq(nil)
971
+ expect(Ad.field_specs['allow_null'].default).to eq(nil)
972
+ end
973
+ end
974
+
975
+ it 'disallows serialize: with a non-string column type' do
976
+ expect do
977
+ class Ad < ActiveRecord::Base
978
+ fields do
979
+ allow_list :integer, limit: 8, serialize: true
980
+ end
981
+ end
982
+ end.to raise_exception(ArgumentError, /must be :string or :text/)
983
+ end
984
+ end
985
+
986
+ context "for Rails #{Rails::VERSION::MAJOR}" do
987
+ if Rails::VERSION::MAJOR >= 5
988
+ let(:optional_true) { { optional: true } }
989
+ let(:optional_false) { { optional: false } }
990
+ else
991
+ let(:optional_true) { {} }
992
+ let(:optional_false) { {} }
993
+ end
994
+ let(:optional_flag) { { false => optional_false, true => optional_true } }
995
+
996
+ describe 'belongs_to' do
997
+ before do
998
+ unless defined?(AdCategory)
999
+ class AdCategory < ActiveRecord::Base
1000
+ fields { }
1001
+ end
1002
+ end
1003
+
1004
+ class Advert < ActiveRecord::Base
1005
+ fields do
1006
+ name :string, limit: 255, null: true
1007
+ category_id :integer, limit: 8
1008
+ nullable_category_id :integer, limit: 8, null: true
1009
+ end
1010
+ end
1011
+ up = Generators::DeclareSchema::Migration::Migrator.run.first
1012
+ ActiveRecord::Migration.class_eval(up)
1013
+ end
1014
+
1015
+ it 'passes through optional: when given' do
1016
+ class AdvertBelongsTo < ActiveRecord::Base
1017
+ self.table_name = 'adverts'
1018
+ fields { }
1019
+ reset_column_information
1020
+ belongs_to :ad_category, optional: true
1021
+ end
1022
+ expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional_true)
1023
+ end
1024
+
1025
+ describe 'contradictory settings' do # contradictory settings are ok--for example, during migration
1026
+ it 'passes through optional: true, null: false' do
1027
+ class AdvertBelongsTo < ActiveRecord::Base
1028
+ self.table_name = 'adverts'
1029
+ fields { }
1030
+ reset_column_information
1031
+ belongs_to :ad_category, optional: true, null: false
1032
+ end
1033
+ expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional_true)
1034
+ expect(AdvertBelongsTo.field_specs['ad_category_id'].options&.[](:null)).to eq(false)
1035
+ end
1036
+
1037
+ it 'passes through optional: false, null: true' do
1038
+ class AdvertBelongsTo < ActiveRecord::Base
1039
+ self.table_name = 'adverts'
1040
+ fields { }
1041
+ reset_column_information
1042
+ belongs_to :ad_category, optional: false, null: true
1043
+ end
1044
+ expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional_false)
1045
+ expect(AdvertBelongsTo.field_specs['ad_category_id'].options&.[](:null)).to eq(true)
1046
+ end
1047
+ end
1048
+
1049
+ [false, true].each do |nullable|
1050
+ context "nullable=#{nullable}" do
1051
+ it 'infers optional: from null:' do
1052
+ eval <<~EOS
1053
+ class AdvertBelongsTo < ActiveRecord::Base
1054
+ fields { }
1055
+ belongs_to :ad_category, null: #{nullable}
1056
+ end
1057
+ EOS
1058
+ expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional_flag[nullable])
1059
+ expect(AdvertBelongsTo.field_specs['ad_category_id'].options&.[](:null)).to eq(nullable)
1060
+ end
1061
+
1062
+ it 'infers null: from optional:' do
1063
+ eval <<~EOS
1064
+ class AdvertBelongsTo < ActiveRecord::Base
1065
+ fields { }
1066
+ belongs_to :ad_category, optional: #{nullable}
1067
+ end
1068
+ EOS
1069
+ expect(AdvertBelongsTo.reflections['ad_category'].options).to eq(optional_flag[nullable])
1070
+ expect(AdvertBelongsTo.field_specs['ad_category_id'].options&.[](:null)).to eq(nullable)
1071
+ end
1072
+ end
1073
+ end
1074
+ end
1075
+ end
1076
+
1077
+ describe 'migration base class' do
1078
+ it 'adapts to Rails 4' do
1079
+ class Advert < active_record_base_class.constantize
1080
+ fields do
1081
+ title :string, limit: 100
1082
+ end
1083
+ end
1084
+
1085
+ generate_migrations '-n', '-m'
1086
+
1087
+ migrations = Dir.glob('db/migrate/*declare_schema_migration*.rb')
1088
+ expect(migrations.size).to eq(1), migrations.inspect
1089
+
1090
+ migration_content = File.read(migrations.first)
1091
+ first_line = migration_content.split("\n").first
1092
+ base_class = first_line.split(' < ').last
1093
+ expect(base_class).to eq("(Rails::VERSION::MAJOR >= 5 ? ActiveRecord::Migration[4.2] : ActiveRecord::Migration)")
1094
+ end
1095
+ end
1096
+ end