declare_schema 0.8.0.pre.6 → 0.10.1

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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/declare_schema_build.yml +1 -1
  3. data/CHANGELOG.md +28 -1
  4. data/Gemfile.lock +1 -1
  5. data/README.md +91 -13
  6. data/lib/declare_schema.rb +46 -0
  7. data/lib/declare_schema/dsl.rb +39 -0
  8. data/lib/declare_schema/extensions/active_record/fields_declaration.rb +23 -4
  9. data/lib/declare_schema/model.rb +51 -59
  10. data/lib/declare_schema/model/field_spec.rb +11 -8
  11. data/lib/declare_schema/model/foreign_key_definition.rb +4 -8
  12. data/lib/declare_schema/model/habtm_model_shim.rb +1 -1
  13. data/lib/declare_schema/model/index_definition.rb +0 -19
  14. data/lib/declare_schema/schema_change/all.rb +22 -0
  15. data/lib/declare_schema/schema_change/base.rb +45 -0
  16. data/lib/declare_schema/schema_change/column_add.rb +27 -0
  17. data/lib/declare_schema/schema_change/column_change.rb +32 -0
  18. data/lib/declare_schema/schema_change/column_remove.rb +20 -0
  19. data/lib/declare_schema/schema_change/column_rename.rb +23 -0
  20. data/lib/declare_schema/schema_change/foreign_key_add.rb +25 -0
  21. data/lib/declare_schema/schema_change/foreign_key_remove.rb +20 -0
  22. data/lib/declare_schema/schema_change/index_add.rb +33 -0
  23. data/lib/declare_schema/schema_change/index_remove.rb +20 -0
  24. data/lib/declare_schema/schema_change/primary_key_change.rb +33 -0
  25. data/lib/declare_schema/schema_change/table_add.rb +37 -0
  26. data/lib/declare_schema/schema_change/table_change.rb +36 -0
  27. data/lib/declare_schema/schema_change/table_remove.rb +22 -0
  28. data/lib/declare_schema/schema_change/table_rename.rb +22 -0
  29. data/lib/declare_schema/version.rb +1 -1
  30. data/lib/generators/declare_schema/migration/migrator.rb +189 -202
  31. data/lib/generators/declare_schema/support/model.rb +4 -4
  32. data/spec/lib/declare_schema/api_spec.rb +7 -7
  33. data/spec/lib/declare_schema/field_declaration_dsl_spec.rb +41 -15
  34. data/spec/lib/declare_schema/field_spec_spec.rb +50 -6
  35. data/spec/lib/declare_schema/generator_spec.rb +3 -3
  36. data/spec/lib/declare_schema/interactive_primary_key_spec.rb +116 -26
  37. data/spec/lib/declare_schema/migration_generator_spec.rb +1891 -845
  38. data/spec/lib/declare_schema/model/column_spec.rb +47 -17
  39. data/spec/lib/declare_schema/model/foreign_key_definition_spec.rb +134 -57
  40. data/spec/lib/declare_schema/model/habtm_model_shim_spec.rb +3 -3
  41. data/spec/lib/declare_schema/model/index_definition_spec.rb +188 -77
  42. data/spec/lib/declare_schema/model/table_options_definition_spec.rb +75 -11
  43. data/spec/lib/declare_schema/schema_change/base_spec.rb +75 -0
  44. data/spec/lib/declare_schema/schema_change/column_add_spec.rb +30 -0
  45. data/spec/lib/declare_schema/schema_change/column_change_spec.rb +33 -0
  46. data/spec/lib/declare_schema/schema_change/column_remove_spec.rb +30 -0
  47. data/spec/lib/declare_schema/schema_change/column_rename_spec.rb +28 -0
  48. data/spec/lib/declare_schema/schema_change/foreign_key_add_spec.rb +29 -0
  49. data/spec/lib/declare_schema/schema_change/foreign_key_remove_spec.rb +29 -0
  50. data/spec/lib/declare_schema/schema_change/index_add_spec.rb +56 -0
  51. data/spec/lib/declare_schema/schema_change/index_remove_spec.rb +29 -0
  52. data/spec/lib/declare_schema/schema_change/primary_key_change_spec.rb +69 -0
  53. data/spec/lib/declare_schema/schema_change/table_add_spec.rb +50 -0
  54. data/spec/lib/declare_schema/schema_change/table_change_spec.rb +30 -0
  55. data/spec/lib/declare_schema/schema_change/table_remove_spec.rb +27 -0
  56. data/spec/lib/declare_schema/schema_change/table_rename_spec.rb +27 -0
  57. data/spec/lib/declare_schema_spec.rb +101 -0
  58. data/spec/lib/generators/declare_schema/migration/migrator_spec.rb +71 -13
  59. data/spec/support/acceptance_spec_helpers.rb +2 -2
  60. metadata +33 -3
  61. data/test_responses.txt +0 -2
@@ -85,14 +85,6 @@ RSpec.describe DeclareSchema::Model::Column do
85
85
  end
86
86
 
87
87
  describe 'instance methods' do
88
- before do
89
- class ColumnTestModel < ActiveRecord::Base
90
- fields do
91
- title :string, limit: 127, null: false
92
- count :integer, null: false
93
- end
94
- end
95
- end
96
88
  let(:model) { ColumnTestModel }
97
89
  let(:type) { :integer }
98
90
  let(:current_table_name) { model.table_name }
@@ -108,18 +100,56 @@ RSpec.describe DeclareSchema::Model::Column do
108
100
  sql_type_metadata: {}) }
109
101
  subject { described_class.new(model, current_table_name, column) }
110
102
 
111
- describe '#type' do
112
- it 'returns type' do
113
- expect(subject.type).to eq(type)
103
+ context 'Using fields' do
104
+ before do
105
+ class ColumnTestModel < ActiveRecord::Base
106
+ fields do
107
+ title :string, limit: 127, null: false
108
+ count :integer, null: false
109
+ end
110
+ end
111
+ end
112
+
113
+ describe '#type' do
114
+ it 'returns type' do
115
+ expect(subject.type).to eq(type)
116
+ end
117
+ end
118
+
119
+ describe '#schema_attributes' do
120
+ it 'returns a hash with relevant key/values' do
121
+ if defined?(Mysql2)
122
+ expect(subject.schema_attributes).to eq(type: :integer, null: false, limit: 4)
123
+ else
124
+ expect(subject.schema_attributes).to eq(type: :integer, null: false)
125
+ end
126
+ end
114
127
  end
115
128
  end
116
129
 
117
- describe '#schema_attributes' do
118
- it 'returns a hash with relevant key/values' do
119
- if defined?(Mysql2)
120
- expect(subject.schema_attributes).to eq(type: :integer, null: false, limit: 4)
121
- else
122
- expect(subject.schema_attributes).to eq(type: :integer, null: false)
130
+ context 'Using declare_schema' do
131
+ before do
132
+ class ColumnTestModel < ActiveRecord::Base
133
+ declare_schema do
134
+ string :title, limit: 127, null: false
135
+ integer :count, null: false
136
+ end
137
+ end
138
+ end
139
+
140
+ describe '#type' do
141
+ it 'returns type' do
142
+ expect(subject.type).to eq(type)
143
+ end
144
+ end
145
+
146
+ describe '#schema_attributes' do
147
+ it 'returns a hash with relevant key/values' do
148
+ if defined?(Mysql2)
149
+ expect(subject.schema_attributes).to eq(type: :integer, null: false, limit: 4)
150
+ else
151
+ expect(subject.schema_attributes).to eq(type: :integer, null: false)
152
+ end
123
153
  end
124
154
  end
125
155
  end
@@ -3,89 +3,166 @@
3
3
  require_relative '../../../../lib/declare_schema/model/foreign_key_definition'
4
4
 
5
5
  RSpec.describe DeclareSchema::Model::ForeignKeyDefinition do
6
- before do
7
- load File.expand_path('../prepare_testapp.rb', __dir__)
6
+ let(:model_class) { Network }
7
+
8
+ context 'Using fields' do
9
+ before do
10
+ load File.expand_path('../prepare_testapp.rb', __dir__)
8
11
 
9
- class Network < ActiveRecord::Base
10
- fields do
11
- name :string, limit: 127, index: true
12
+ class Network < ActiveRecord::Base
13
+ fields do
14
+ name :string, limit: 127, index: true
12
15
 
13
- timestamps
16
+ timestamps
17
+ end
14
18
  end
15
19
  end
16
- end
17
20
 
18
- let(:model_class) { Network }
21
+ describe 'instance methods' do
22
+ let(:connection) { instance_double(ActiveRecord::Base.connection.class) }
23
+ let(:model) { instance_double('Model', table_name: 'models', connection: connection) }
24
+ let(:foreign_key) { :network_id }
25
+ let(:options) { {} }
26
+ subject { described_class.new(model, foreign_key, options)}
19
27
 
20
- describe 'instance methods' do
21
- let(:connection) { instance_double(ActiveRecord::Base.connection.class) }
22
- let(:model) { instance_double('Model', table_name: 'models', connection: connection) }
23
- let(:foreign_key) { :network_id }
24
- let(:options) { {} }
25
- subject { described_class.new(model, foreign_key, options)}
26
-
27
- before do
28
- allow(connection).to receive(:index_name).with('models', column: 'network_id') { 'on_network_id' }
29
- end
30
-
31
- describe '#initialize' do
32
- it 'normalizes symbols to strings' do
33
- expect(subject.foreign_key).to eq('network_id')
34
- expect(subject.parent_table_name).to eq('networks')
28
+ before do
29
+ allow(connection).to receive(:index_name).with('models', column: 'network_id') { 'on_network_id' }
35
30
  end
36
31
 
37
- context 'when most options passed' do
38
- let(:options) { { parent_table: :networks, foreign_key: :the_network_id, index_name: :index_on_network_id } }
39
-
32
+ describe '#initialize' do
40
33
  it 'normalizes symbols to strings' do
41
34
  expect(subject.foreign_key).to eq('network_id')
42
- expect(subject.foreign_key_name).to eq('the_network_id')
43
35
  expect(subject.parent_table_name).to eq('networks')
44
- expect(subject.foreign_key).to eq('network_id')
45
- expect(subject.constraint_name).to eq('index_on_network_id')
46
- expect(subject.on_delete_cascade).to be_falsey
47
36
  end
48
- end
49
37
 
50
- context 'when all options passed' do
51
- let(:foreign_key) { nil }
52
- let(:options) { { parent_table: :networks, foreign_key: :the_network_id, index_name: :index_on_network_id,
53
- constraint_name: :constraint_1, dependent: :delete } }
38
+ context 'when most options passed' do
39
+ let(:options) { { parent_table: :networks, foreign_key: :the_network_id, index_name: :index_on_network_id } }
40
+
41
+ it 'normalizes symbols to strings' do
42
+ expect(subject.foreign_key).to eq('network_id')
43
+ expect(subject.foreign_key_name).to eq('the_network_id')
44
+ expect(subject.parent_table_name).to eq('networks')
45
+ expect(subject.foreign_key).to eq('network_id')
46
+ expect(subject.constraint_name).to eq('index_on_network_id')
47
+ expect(subject.on_delete_cascade).to be_falsey
48
+ end
49
+ end
54
50
 
55
- it 'normalizes symbols to strings' do
56
- expect(subject.foreign_key).to be_nil
57
- expect(subject.foreign_key_name).to eq('the_network_id')
58
- expect(subject.parent_table_name).to eq('networks')
59
- expect(subject.constraint_name).to eq('constraint_1')
60
- expect(subject.on_delete_cascade).to be_truthy
51
+ context 'when all options passed' do
52
+ let(:foreign_key) { nil }
53
+ let(:options) { { parent_table: :networks, foreign_key: :the_network_id, index_name: :index_on_network_id,
54
+ constraint_name: :constraint_1, dependent: :delete } }
55
+
56
+ it 'normalizes symbols to strings' do
57
+ expect(subject.foreign_key).to be_nil
58
+ expect(subject.foreign_key_name).to eq('the_network_id')
59
+ expect(subject.parent_table_name).to eq('networks')
60
+ expect(subject.constraint_name).to eq('constraint_1')
61
+ expect(subject.on_delete_cascade).to be_truthy
62
+ end
61
63
  end
62
64
  end
63
65
  end
64
66
 
65
- describe '#to_add_statement' do
66
- it 'returns add_foreign_key command' do
67
- expect(subject.to_add_statement).to eq('add_foreign_key("models", "networks", column: "network_id", name: "on_network_id")')
67
+ describe 'class << self' do
68
+ let(:connection) { instance_double(ActiveRecord::Base.connection.class) }
69
+ let(:model) { instance_double('Model', table_name: 'models', connection: connection) }
70
+ let(:old_table_name) { 'networks' }
71
+ before do
72
+ allow(connection).to receive(:quote_table_name).with('networks') { 'networks' }
73
+ allow(connection).to receive(:select_rows) { [['CONSTRAINT `constraint` FOREIGN KEY (`network_id`) REFERENCES `networks` (`id`)']] }
74
+ allow(connection).to receive(:index_name).with('models', column: 'network_id') { }
75
+ end
76
+
77
+ describe '.for_model' do
78
+ subject { described_class.for_model(model, old_table_name) }
79
+
80
+ it 'returns new object' do
81
+ expect(subject.size).to eq(1), subject.inspect
82
+ expect(subject.first).to be_kind_of(described_class)
83
+ expect(subject.first.foreign_key).to eq('network_id')
84
+ end
68
85
  end
69
86
  end
70
87
  end
71
88
 
72
- describe 'class << self' do
73
- let(:connection) { instance_double(ActiveRecord::Base.connection.class) }
74
- let(:model) { instance_double('Model', table_name: 'models', connection: connection) }
75
- let(:old_table_name) { 'networks' }
89
+ context 'Using declare_schema' do
76
90
  before do
77
- allow(connection).to receive(:quote_table_name).with('networks') { 'networks' }
78
- allow(connection).to receive(:select_rows) { [['CONSTRAINT `constraint` FOREIGN KEY (`network_id`) REFERENCES `networks` (`id`)']] }
79
- allow(connection).to receive(:index_name).with('models', column: 'network_id') { }
91
+ load File.expand_path('../prepare_testapp.rb', __dir__)
92
+
93
+ class Network < ActiveRecord::Base
94
+ declare_schema do
95
+ string :name, limit: 127, index: true
96
+
97
+ timestamps
98
+ end
99
+ end
100
+ end
101
+
102
+ describe 'instance methods' do
103
+ let(:connection) { instance_double(ActiveRecord::Base.connection.class) }
104
+ let(:model) { instance_double('Model', table_name: 'models', connection: connection) }
105
+ let(:foreign_key) { :network_id }
106
+ let(:options) { {} }
107
+ subject { described_class.new(model, foreign_key, options)}
108
+
109
+ before do
110
+ allow(connection).to receive(:index_name).with('models', column: 'network_id') { 'on_network_id' }
111
+ end
112
+
113
+ describe '#initialize' do
114
+ it 'normalizes symbols to strings' do
115
+ expect(subject.foreign_key).to eq('network_id')
116
+ expect(subject.parent_table_name).to eq('networks')
117
+ end
118
+
119
+ context 'when most options passed' do
120
+ let(:options) { { parent_table: :networks, foreign_key: :the_network_id, index_name: :index_on_network_id } }
121
+
122
+ it 'normalizes symbols to strings' do
123
+ expect(subject.foreign_key).to eq('network_id')
124
+ expect(subject.foreign_key_name).to eq('the_network_id')
125
+ expect(subject.parent_table_name).to eq('networks')
126
+ expect(subject.foreign_key).to eq('network_id')
127
+ expect(subject.constraint_name).to eq('index_on_network_id')
128
+ expect(subject.on_delete_cascade).to be_falsey
129
+ end
130
+ end
131
+
132
+ context 'when all options passed' do
133
+ let(:foreign_key) { nil }
134
+ let(:options) { { parent_table: :networks, foreign_key: :the_network_id, index_name: :index_on_network_id,
135
+ constraint_name: :constraint_1, dependent: :delete } }
136
+
137
+ it 'normalizes symbols to strings' do
138
+ expect(subject.foreign_key).to be_nil
139
+ expect(subject.foreign_key_name).to eq('the_network_id')
140
+ expect(subject.parent_table_name).to eq('networks')
141
+ expect(subject.constraint_name).to eq('constraint_1')
142
+ expect(subject.on_delete_cascade).to be_truthy
143
+ end
144
+ end
145
+ end
80
146
  end
81
147
 
82
- describe '.for_model' do
83
- subject { described_class.for_model(model, old_table_name) }
148
+ describe 'class << self' do
149
+ let(:connection) { instance_double(ActiveRecord::Base.connection.class) }
150
+ let(:model) { instance_double('Model', table_name: 'models', connection: connection) }
151
+ let(:old_table_name) { 'networks' }
152
+ before do
153
+ allow(connection).to receive(:quote_table_name).with('networks') { 'networks' }
154
+ allow(connection).to receive(:select_rows) { [['CONSTRAINT `constraint` FOREIGN KEY (`network_id`) REFERENCES `networks` (`id`)']] }
155
+ allow(connection).to receive(:index_name).with('models', column: 'network_id') { }
156
+ end
84
157
 
85
- it 'returns new object' do
86
- expect(subject.size).to eq(1), subject.inspect
87
- expect(subject.first).to be_kind_of(described_class)
88
- expect(subject.first.foreign_key).to eq('network_id')
158
+ describe '.for_model' do
159
+ subject { described_class.for_model(model, old_table_name) }
160
+
161
+ it 'returns new object' do
162
+ expect(subject.size).to eq(1), subject.inspect
163
+ expect(subject.first).to be_kind_of(described_class)
164
+ expect(subject.first.foreign_key).to eq('network_id')
165
+ end
89
166
  end
90
167
  end
91
168
  end
@@ -88,13 +88,13 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
88
88
 
89
89
  describe '#primary_key' do
90
90
  it 'returns false' do
91
- expect(subject.primary_key).to eq(false)
91
+ expect(subject._defined_primary_key).to eq(false)
92
92
  end
93
93
  end
94
94
 
95
- describe '#defined_primary_key' do
95
+ describe '#_defined_primary_key' do
96
96
  it 'returns false' do
97
- expect(subject.defined_primary_key).to eq(false)
97
+ expect(subject._defined_primary_key).to eq(false)
98
98
  end
99
99
  end
100
100
 
@@ -10,119 +10,230 @@ require_relative '../../../../lib/declare_schema/model/index_definition'
10
10
  # unless you manually create any Sqlite tables with UNIQUE constraints.
11
11
 
12
12
  RSpec.describe DeclareSchema::Model::IndexDefinition do
13
- before do
14
- load File.expand_path('../prepare_testapp.rb', __dir__)
13
+ let(:model_class) { IndexDefinitionTestModel }
14
+
15
+ context 'Using fields' do
16
+ before do
17
+ load File.expand_path('../prepare_testapp.rb', __dir__)
15
18
 
16
- class IndexDefinitionTestModel < ActiveRecord::Base
17
- fields do
18
- name :string, limit: 127, index: true
19
+ class IndexDefinitionTestModel < ActiveRecord::Base
20
+ fields do
21
+ name :string, limit: 127, index: true
19
22
 
20
- timestamps
23
+ timestamps
24
+ end
21
25
  end
22
- end
23
26
 
24
- class IndexDefinitionCompoundIndexModel < ActiveRecord::Base
25
- fields do
26
- fk1_id :integer
27
- fk2_id :integer
27
+ class IndexDefinitionCompoundIndexModel < ActiveRecord::Base
28
+ fields do
29
+ fk1_id :integer
30
+ fk2_id :integer
28
31
 
29
- timestamps
32
+ timestamps
33
+ end
30
34
  end
31
35
  end
32
- end
33
36
 
34
- let(:model_class) { IndexDefinitionTestModel }
37
+ describe 'instance methods' do
38
+ let(:model) { model_class.new }
39
+ subject { declared_class.new(model_class) }
35
40
 
36
- describe 'instance methods' do
37
- let(:model) { model_class.new }
38
- subject { declared_class.new(model_class) }
41
+ it 'has index_definitions' do
42
+ expect(model_class.index_definitions).to be_kind_of(Array)
43
+ expect(model_class.index_definitions.map(&:name)).to eq(['on_name'])
44
+ expect([:name, :fields, :unique].map { |attr| model_class.index_definitions[0].send(attr)}).to eq(
45
+ ['on_name', ['name'], false]
46
+ )
47
+ end
39
48
 
40
- it 'has index_definitions' do
41
- expect(model_class.index_definitions).to be_kind_of(Array)
42
- expect(model_class.index_definitions.map(&:name)).to eq(['on_name'])
43
- expect([:name, :fields, :unique].map { |attr| model_class.index_definitions[0].send(attr)}).to eq(
44
- ['on_name', ['name'], false]
45
- )
49
+ it 'has index_definitions_with_primary_key' do
50
+ expect(model_class.index_definitions_with_primary_key).to be_kind_of(Array)
51
+ result = model_class.index_definitions_with_primary_key.sort_by(&:name)
52
+ expect(result.map(&:name)).to eq(['PRIMARY', 'on_name'])
53
+ expect([:name, :fields, :unique].map { |attr| result[0].send(attr)}).to eq(
54
+ ['PRIMARY', ['id'], true]
55
+ )
56
+ expect([:name, :fields, :unique].map { |attr| result[1].send(attr)}).to eq(
57
+ ['on_name', ['name'], false]
58
+ )
59
+ end
46
60
  end
47
61
 
48
- it 'has index_definitions_with_primary_key' do
49
- expect(model_class.index_definitions_with_primary_key).to be_kind_of(Array)
50
- result = model_class.index_definitions_with_primary_key.sort_by(&:name)
51
- expect(result.map(&:name)).to eq(['PRIMARY', 'on_name'])
52
- expect([:name, :fields, :unique].map { |attr| result[0].send(attr)}).to eq(
53
- ['PRIMARY', ['id'], true]
54
- )
55
- expect([:name, :fields, :unique].map { |attr| result[1].send(attr)}).to eq(
56
- ['on_name', ['name'], false]
57
- )
62
+ describe 'class methods' do
63
+ describe 'index_name' do
64
+ it 'works with a single column' do
65
+ expect(described_class.index_name('parent_id')).to eq('on_parent_id')
66
+ end
67
+
68
+ it 'works with many columns' do
69
+ expect(described_class.index_name(['a', 'b', 'c'])).to eq('on_a_and_b_and_c')
70
+ end
71
+ end
72
+
73
+ context 'with a migrated database' do
74
+ before do
75
+ ActiveRecord::Base.connection.execute <<~EOS
76
+ CREATE TABLE index_definition_test_models (
77
+ id INTEGER NOT NULL PRIMARY KEY,
78
+ name #{if defined?(Sqlite3) then 'TEXT' else 'VARCHAR(255)' end} NOT NULL
79
+ )
80
+ EOS
81
+ ActiveRecord::Base.connection.execute <<~EOS
82
+ CREATE UNIQUE INDEX index_definition_test_models_on_name ON index_definition_test_models(name)
83
+ EOS
84
+ ActiveRecord::Base.connection.execute <<~EOS
85
+ CREATE TABLE index_definition_compound_index_models (
86
+ fk1_id INTEGER NOT NULL,
87
+ fk2_id INTEGER NOT NULL,
88
+ PRIMARY KEY (fk1_id, fk2_id)
89
+ )
90
+ EOS
91
+ ActiveRecord::Base.connection.schema_cache.clear!
92
+ end
93
+
94
+ describe 'for_model' do
95
+ subject { described_class.for_model(model_class) }
96
+
97
+ context 'with single-column PK' do
98
+ it 'returns the indexes for the model' do
99
+ expect(subject.size).to eq(2), subject.inspect
100
+ expect([:name, :columns, :unique].map { |attr| subject[0].send(attr) }).to eq(
101
+ ['index_definition_test_models_on_name', ['name'], true]
102
+ )
103
+ expect([:name, :columns, :unique].map { |attr| subject[1].send(attr) }).to eq(
104
+ ['PRIMARY', ['id'], true]
105
+ )
106
+ end
107
+ end
108
+
109
+ context 'with compound-column PK' do
110
+ let(:model_class) { IndexDefinitionCompoundIndexModel }
111
+
112
+ it 'returns the indexes for the model' do
113
+ if Rails::VERSION::MAJOR < 5
114
+ expect(model_class.connection).to receive(:primary_key).with('index_definition_compound_index_models').and_return(nil)
115
+ connection_stub = instance_double(ActiveRecord::Base.connection.class, "connection")
116
+ expect(connection_stub).to receive(:indexes).
117
+ with('index_definition_compound_index_models').
118
+ and_return([DeclareSchema::Model::IndexDefinition.new(model_class, ['fk1_id', 'fk2_id'], name: 'PRIMARY')])
119
+
120
+ expect(model_class.connection).to receive(:dup).and_return(connection_stub)
121
+ end
122
+
123
+ expect(subject.size).to eq(1), subject.inspect
124
+ expect([:name, :columns, :unique].map { |attr| subject[0].send(attr) }).to eq(
125
+ ['PRIMARY', ['fk1_id', 'fk2_id'], true]
126
+ )
127
+ end
128
+ end
129
+ end
130
+ end
58
131
  end
59
132
  end
60
133
 
61
- describe 'class methods' do
62
- describe 'index_name' do
63
- it 'works with a single column' do
64
- expect(described_class.index_name('parent_id')).to eq('on_parent_id')
134
+ context 'Using declare_schema' do
135
+ before do
136
+ load File.expand_path('../prepare_testapp.rb', __dir__)
137
+
138
+ class IndexDefinitionTestModel < ActiveRecord::Base
139
+ declare_schema do
140
+ string :name, limit: 127, index: true
141
+
142
+ timestamps
143
+ end
65
144
  end
66
145
 
67
- it 'works with many columns' do
68
- expect(described_class.index_name(['a', 'b', 'c'])).to eq('on_a_and_b_and_c')
146
+ class IndexDefinitionCompoundIndexModel < ActiveRecord::Base
147
+ declare_schema do
148
+ integer :fk1_id
149
+ integer :fk2_id
150
+
151
+ timestamps
152
+ end
69
153
  end
70
154
  end
71
155
 
72
- context 'with a migrated database' do
73
- before do
74
- ActiveRecord::Base.connection.execute <<~EOS
156
+ describe 'instance methods' do
157
+ let(:model) { model_class.new }
158
+ subject { declared_class.new(model_class) }
159
+
160
+ it 'has index_definitions' do
161
+ expect(model_class.index_definitions).to be_kind_of(Array)
162
+ expect(model_class.index_definitions.map(&:name)).to eq(['on_name'])
163
+ expect([:name, :fields, :unique].map { |attr| model_class.index_definitions[0].send(attr)}).to eq(
164
+ ['on_name', ['name'], false]
165
+ )
166
+ end
167
+
168
+ it 'has index_definitions_with_primary_key' do
169
+ expect(model_class.index_definitions_with_primary_key).to be_kind_of(Array)
170
+ result = model_class.index_definitions_with_primary_key.sort_by(&:name)
171
+ expect(result.map(&:name)).to eq(['PRIMARY', 'on_name'])
172
+ expect([:name, :fields, :unique].map { |attr| result[0].send(attr)}).to eq(
173
+ ['PRIMARY', ['id'], true]
174
+ )
175
+ expect([:name, :fields, :unique].map { |attr| result[1].send(attr)}).to eq(
176
+ ['on_name', ['name'], false]
177
+ )
178
+ end
179
+ end
180
+
181
+ describe 'class << self' do
182
+ context 'with a migrated database' do
183
+ before do
184
+ ActiveRecord::Base.connection.execute <<~EOS
75
185
  CREATE TABLE index_definition_test_models (
76
186
  id INTEGER NOT NULL PRIMARY KEY,
77
187
  name #{if defined?(Sqlite3) then 'TEXT' else 'VARCHAR(255)' end} NOT NULL
78
188
  )
79
- EOS
80
- ActiveRecord::Base.connection.execute <<~EOS
189
+ EOS
190
+ ActiveRecord::Base.connection.execute <<~EOS
81
191
  CREATE UNIQUE INDEX index_definition_test_models_on_name ON index_definition_test_models(name)
82
- EOS
83
- ActiveRecord::Base.connection.execute <<~EOS
192
+ EOS
193
+ ActiveRecord::Base.connection.execute <<~EOS
84
194
  CREATE TABLE index_definition_compound_index_models (
85
195
  fk1_id INTEGER NOT NULL,
86
196
  fk2_id INTEGER NOT NULL,
87
197
  PRIMARY KEY (fk1_id, fk2_id)
88
198
  )
89
- EOS
90
- ActiveRecord::Base.connection.schema_cache.clear!
91
- end
92
-
93
- describe 'for_model' do
94
- subject { described_class.for_model(model_class) }
199
+ EOS
200
+ ActiveRecord::Base.connection.schema_cache.clear!
201
+ end
95
202
 
96
- context 'with single-column PK' do
97
- it 'returns the indexes for the model' do
98
- expect(subject.size).to eq(2), subject.inspect
99
- expect([:name, :columns, :unique].map { |attr| subject[0].send(attr) }).to eq(
100
- ['index_definition_test_models_on_name', ['name'], true]
101
- )
102
- expect([:name, :columns, :unique].map { |attr| subject[1].send(attr) }).to eq(
103
- ['PRIMARY', ['id'], true]
104
- )
203
+ describe 'for_model' do
204
+ subject { described_class.for_model(model_class) }
205
+
206
+ context 'with single-column PK' do
207
+ it 'returns the indexes for the model' do
208
+ expect(subject.size).to eq(2), subject.inspect
209
+ expect([:name, :columns, :unique].map { |attr| subject[0].send(attr) }).to eq(
210
+ ['index_definition_test_models_on_name', ['name'], true]
211
+ )
212
+ expect([:name, :columns, :unique].map { |attr| subject[1].send(attr) }).to eq(
213
+ ['PRIMARY', ['id'], true]
214
+ )
215
+ end
105
216
  end
106
- end
107
217
 
108
- context 'with compound-column PK' do
109
- let(:model_class) { IndexDefinitionCompoundIndexModel }
218
+ context 'with compound-column PK' do
219
+ let(:model_class) { IndexDefinitionCompoundIndexModel }
110
220
 
111
- it 'returns the indexes for the model' do
112
- if Rails::VERSION::MAJOR < 5
113
- expect(model_class.connection).to receive(:primary_key).with('index_definition_compound_index_models').and_return(nil)
114
- connection_stub = instance_double(ActiveRecord::Base.connection.class, "connection")
115
- expect(connection_stub).to receive(:indexes).
116
- with('index_definition_compound_index_models').
117
- and_return([DeclareSchema::Model::IndexDefinition.new(model_class, ['fk1_id', 'fk2_id'], name: 'PRIMARY')])
221
+ it 'returns the indexes for the model' do
222
+ if Rails::VERSION::MAJOR < 5
223
+ expect(model_class.connection).to receive(:primary_key).with('index_definition_compound_index_models').and_return(nil)
224
+ connection_stub = instance_double(ActiveRecord::Base.connection.class, "connection")
225
+ expect(connection_stub).to receive(:indexes).
226
+ with('index_definition_compound_index_models').
227
+ and_return([DeclareSchema::Model::IndexDefinition.new(model_class, ['fk1_id', 'fk2_id'], name: 'PRIMARY')])
118
228
 
119
- expect(model_class.connection).to receive(:dup).and_return(connection_stub)
120
- end
229
+ expect(model_class.connection).to receive(:dup).and_return(connection_stub)
230
+ end
121
231
 
122
- expect(subject.size).to eq(1), subject.inspect
123
- expect([:name, :columns, :unique].map { |attr| subject[0].send(attr) }).to eq(
124
- ['PRIMARY', ['fk1_id', 'fk2_id'], true]
125
- )
232
+ expect(subject.size).to eq(1), subject.inspect
233
+ expect([:name, :columns, :unique].map { |attr| subject[0].send(attr) }).to eq(
234
+ ['PRIMARY', ['fk1_id', 'fk2_id'], true]
235
+ )
236
+ end
126
237
  end
127
238
  end
128
239
  end