declare_schema 1.4.0.colin.8 → 1.4.0.colin.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -1
- data/Gemfile.lock +1 -1
- data/lib/declare_schema/model/habtm_model_shim.rb +19 -12
- data/lib/declare_schema/model/index_definition.rb +22 -14
- data/lib/declare_schema/model.rb +10 -9
- data/lib/declare_schema/version.rb +1 -1
- data/lib/generators/declare_schema/migration/migrator.rb +16 -11
- data/spec/lib/declare_schema/migration_generator_spec.rb +46 -6
- data/spec/lib/declare_schema/model/habtm_model_shim_spec.rb +45 -20
- data/spec/lib/declare_schema/model/index_definition_spec.rb +69 -5
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7e20de5eeec8ea9436d2fd674f5ff33f20888cfb87e17456fd278a38504bbede
|
4
|
+
data.tar.gz: 259d8dd463292836e40d3fe07261743e714d588440d8a988183daa32f0c8a4d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18dc843b60c81ace18bc692f1e8d76038d50695081ab846b14f0653aa4d6a755666417f4d7190429aa83e9eb9fffba02d61c71b2a5c44ef7dbf69baf401e7e31
|
7
|
+
data.tar.gz: 94bf1af85ecdc252e08f6453018edc6a2f216e65aa6d7fce0ab7399058097d6a0e1a834e02d350789e8e6fb3485050979dde543e2036e048cc914dd6ffb5cdad
|
data/CHANGELOG.md
CHANGED
@@ -10,7 +10,12 @@ Note: this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0
|
|
10
10
|
### Changed
|
11
11
|
- Deprecate index: 'name' and unique: true|false in favor of index: { name: 'name', unique: true|false }.
|
12
12
|
|
13
|
-
## [1.3.
|
13
|
+
## [1.3.4] - 2024-01-18
|
14
|
+
### Fixed
|
15
|
+
- Add test for migrating `has_and_belongs_to_many` associations and fix them to properly declare their
|
16
|
+
2 foreign keys as the primary key of the join table, rather than just a unique index.
|
17
|
+
|
18
|
+
## [1.3.3] - 2024-01-17
|
14
19
|
### Fixed
|
15
20
|
- Fix a MySQL 8 bug where MySQL 8+ renames charset 'utf8' to 'utf8mb3' and collation 'utf8_general_ci' to
|
16
21
|
'utf8mb3_unicode_ci'.
|
data/Gemfile.lock
CHANGED
@@ -4,15 +4,17 @@ module DeclareSchema
|
|
4
4
|
module Model
|
5
5
|
class HabtmModelShim
|
6
6
|
class << self
|
7
|
-
def from_reflection(
|
8
|
-
new(
|
9
|
-
|
7
|
+
def from_reflection(reflection)
|
8
|
+
new(reflection.join_table,
|
9
|
+
[reflection.foreign_key, reflection.association_foreign_key],
|
10
|
+
[reflection.active_record.table_name, reflection.klass.table_name],
|
11
|
+
connection: reflection.active_record.connection)
|
10
12
|
end
|
11
13
|
end
|
12
14
|
|
13
|
-
attr_reader :join_table, :foreign_keys, :parent_table_names
|
15
|
+
attr_reader :join_table, :foreign_keys, :parent_table_names, :connection
|
14
16
|
|
15
|
-
def initialize(join_table, foreign_keys, parent_table_names)
|
17
|
+
def initialize(join_table, foreign_keys, parent_table_names, connection:)
|
16
18
|
foreign_keys.is_a?(Array) && foreign_keys.size == 2 or
|
17
19
|
raise ArgumentError, "foreign_keys must be <Array[2]>; got #{foreign_keys.inspect}"
|
18
20
|
parent_table_names.is_a?(Array) && parent_table_names.size == 2 or
|
@@ -20,6 +22,7 @@ module DeclareSchema
|
|
20
22
|
@join_table = join_table
|
21
23
|
@foreign_keys = foreign_keys.sort # Rails requires these be in alphabetical order
|
22
24
|
@parent_table_names = @foreign_keys == foreign_keys ? parent_table_names : parent_table_names.reverse # match the above sort
|
25
|
+
@connection = connection
|
23
26
|
end
|
24
27
|
|
25
28
|
def _table_options
|
@@ -41,17 +44,21 @@ module DeclareSchema
|
|
41
44
|
end
|
42
45
|
|
43
46
|
def _declared_primary_key
|
44
|
-
|
47
|
+
foreign_keys
|
45
48
|
end
|
46
49
|
|
47
|
-
def
|
48
|
-
|
49
|
-
IndexDefinition.new(foreign_keys,
|
50
|
-
|
51
|
-
])
|
50
|
+
def index_definitions
|
51
|
+
[
|
52
|
+
IndexDefinition.new(foreign_keys.last, table_name: table_name, unique: false) # index for queries where we only have the last foreign key
|
53
|
+
]
|
52
54
|
end
|
53
55
|
|
54
|
-
|
56
|
+
def index_definitions_with_primary_key
|
57
|
+
[
|
58
|
+
*index_definitions,
|
59
|
+
IndexDefinition.new(foreign_keys, table_name: table_name, name: Model::IndexDefinition::PRIMARY_KEY_NAME, unique: true) # creates a primary composite key on both foreign keys
|
60
|
+
]
|
61
|
+
end
|
55
62
|
|
56
63
|
def ignore_indexes
|
57
64
|
@ignore_indexes ||= Set.new
|
@@ -16,12 +16,9 @@ module DeclareSchema
|
|
16
16
|
|
17
17
|
PRIMARY_KEY_NAME = "PRIMARY"
|
18
18
|
|
19
|
-
# Caller needs to pass either name or table_name. The table_name is not remembered; it is just used to compute the
|
20
|
-
# default name if no name is given.
|
21
19
|
def initialize(columns, table_name:, name: nil, allow_equivalent: false, unique: false, where: nil, length: nil)
|
22
20
|
@table_name = table_name
|
23
21
|
@name = name || self.class.default_index_name(table_name, columns)
|
24
|
-
@name.to_s == 'index_adverts_on_Advert' and binding.pry
|
25
22
|
@columns = Array.wrap(columns).map(&:to_s)
|
26
23
|
@explicit_name = @name if !allow_equivalent
|
27
24
|
unique.in?([false, true]) or raise ArgumentError, "unique must be true or false: got #{unique.inspect}"
|
@@ -38,7 +35,7 @@ module DeclareSchema
|
|
38
35
|
@where = where.start_with?('(') ? where : "(#{where})"
|
39
36
|
end
|
40
37
|
|
41
|
-
@length = length
|
38
|
+
@length = self.class.normalize_index_length(length, columns: @columns)
|
42
39
|
end
|
43
40
|
|
44
41
|
class << self
|
@@ -57,16 +54,7 @@ module DeclareSchema
|
|
57
54
|
raise "primary key on #{table_name} was not unique on #{primary_key_columns} (was unique=#{index.unique} on #{index.columns})"
|
58
55
|
primary_key_found = true
|
59
56
|
end
|
60
|
-
length
|
61
|
-
case lengths = index.lengths
|
62
|
-
when {}
|
63
|
-
nil
|
64
|
-
when Hash
|
65
|
-
lengths.size == 1 ? lengths.values.first : lengths
|
66
|
-
else
|
67
|
-
lengths
|
68
|
-
end
|
69
|
-
new(index.columns, name: index.name, table_name: table_name, unique: index.unique, where: index.where, length: length)
|
57
|
+
new(index.columns, name: index.name, table_name: table_name, unique: index.unique, where: index.where, length: index.lengths)
|
70
58
|
end.compact
|
71
59
|
|
72
60
|
if !primary_key_found
|
@@ -86,6 +74,26 @@ module DeclareSchema
|
|
86
74
|
"Default index name '#{index_name}' exceeds configured limit of #{DeclareSchema.max_index_and_constraint_name_length} characters. Use the `name:` option to give it a shorter name, or adjust DeclareSchema.max_index_and_constraint_name_length if you know your database can accept longer names."
|
87
75
|
end
|
88
76
|
|
77
|
+
# This method normalizes the length option to be either nil or a Hash of Symbol column names to lengths,
|
78
|
+
# so that we can safely compare what the user specified with what we get when querying the database schema.
|
79
|
+
# @return [Hash<Symbol, nil>]
|
80
|
+
def normalize_index_length(length, columns:)
|
81
|
+
case length
|
82
|
+
when nil, {}
|
83
|
+
nil
|
84
|
+
when Integer
|
85
|
+
if columns.size == 1
|
86
|
+
{ columns.first.to_sym => length }
|
87
|
+
else
|
88
|
+
raise ArgumentError, "Index length of Integer only allowed when exactly one column; got #{length.inspect} for #{columns.inspect}"
|
89
|
+
end
|
90
|
+
when Hash
|
91
|
+
length.transform_keys(&:to_sym)
|
92
|
+
else
|
93
|
+
raise ArgumentError, "Index length must be nil or Integer or a Hash of column names to lengths; got #{length.inspect} for #{columns.inspect}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
89
97
|
private
|
90
98
|
|
91
99
|
SHA_SUFFIX_LENGTH = 4
|
data/lib/declare_schema/model.rb
CHANGED
@@ -111,31 +111,32 @@ module DeclareSchema
|
|
111
111
|
column_options[:default] = options.delete(:default) if options.has_key?(:default)
|
112
112
|
if options.has_key?(:limit)
|
113
113
|
options.delete(:limit)
|
114
|
-
ActiveSupport::Deprecation.warn("belongs_to limit: is deprecated since it is now inferred")
|
114
|
+
ActiveSupport::Deprecation.warn("belongs_to #{name.inspect}, limit: is deprecated since it is now inferred")
|
115
115
|
end
|
116
116
|
|
117
117
|
# index: true means create an index on the foreign key
|
118
118
|
# index: false means do not create an index on the foreign key
|
119
119
|
# index: { ... } means create an index on the foreign key with the given options
|
120
120
|
index_value = options.delete(:index)
|
121
|
-
if index_value
|
122
|
-
|
121
|
+
if index_value == false # don't create an index
|
122
|
+
options.delete(:unique)
|
123
|
+
options.delete(:allow_equivalent)
|
124
|
+
else
|
125
|
+
index_options = {} # create an index
|
123
126
|
case index_value
|
124
127
|
when String, Symbol
|
125
|
-
|
128
|
+
ActiveSupport::Deprecation.warn("belongs_to #{name.inspect}, index: 'name' is deprecated; use index: { name: 'name' } instead (in #{self.name})")
|
126
129
|
index_options[:name] = index_value.to_s
|
127
|
-
when false
|
128
|
-
raise ArgumentError, "belongs_to index: false contradicts others options #{options.inspect} (in #{name})"
|
129
130
|
when true
|
130
131
|
when nil
|
131
132
|
when Hash
|
132
133
|
index_options = index_value
|
133
134
|
else
|
134
|
-
raise ArgumentError, "belongs_to index: must be true or false or a Hash; got #{index_value.inspect} (in #{name})"
|
135
|
+
raise ArgumentError, "belongs_to #{name.inspect}, index: must be true or false or a Hash; got #{index_value.inspect} (in #{self.name})"
|
135
136
|
end
|
136
137
|
|
137
138
|
if options.has_key?(:unique)
|
138
|
-
|
139
|
+
ActiveSupport::Deprecation.warn("belongs_to #{name.inspect}, unique: true|false is deprecated; use index: { unique: true|false } instead (in #{self.name})")
|
139
140
|
index_options[:unique] = options.delete(:unique)
|
140
141
|
end
|
141
142
|
|
@@ -186,7 +187,7 @@ module DeclareSchema
|
|
186
187
|
end
|
187
188
|
|
188
189
|
if ::DeclareSchema.default_generate_foreign_keys && constraint_name != false
|
189
|
-
constraint(foreign_key_column, constraint_name: constraint_name || index_options&.[](:name), parent_class_name: reflection.
|
190
|
+
constraint(foreign_key_column, constraint_name: constraint_name || index_options&.[](:name), parent_class_name: reflection.class_name, dependent: dependent_delete)
|
190
191
|
end
|
191
192
|
end
|
192
193
|
end
|
@@ -204,8 +204,8 @@ module Generators
|
|
204
204
|
end
|
205
205
|
end
|
206
206
|
# generate shims for HABTM models
|
207
|
-
habtm_tables.each do |name,
|
208
|
-
models_by_table_name[name] = ::DeclareSchema::Model::HabtmModelShim.from_reflection(
|
207
|
+
habtm_tables.each do |name, reflections|
|
208
|
+
models_by_table_name[name] = ::DeclareSchema::Model::HabtmModelShim.from_reflection(reflections.first)
|
209
209
|
end
|
210
210
|
model_table_names = models_by_table_name.keys
|
211
211
|
|
@@ -222,8 +222,8 @@ module Generators
|
|
222
222
|
::DeclareSchema::SchemaChange::TableRemove.new(t, add_table_back(t))
|
223
223
|
end
|
224
224
|
|
225
|
-
creates = to_create.map do |
|
226
|
-
model = models_by_table_name[
|
225
|
+
creates = to_create.map do |table_name|
|
226
|
+
model = models_by_table_name[table_name]
|
227
227
|
disable_auto_increment = model.try(:disable_auto_increment)
|
228
228
|
|
229
229
|
primary_key_definition =
|
@@ -240,10 +240,13 @@ module Generators
|
|
240
240
|
table_options_definition = ::DeclareSchema::Model::TableOptionsDefinition.new(model.table_name, **table_options_for_model(model))
|
241
241
|
table_options = create_table_options(model, disable_auto_increment)
|
242
242
|
|
243
|
-
table_add = ::DeclareSchema::SchemaChange::TableAdd.new(
|
244
|
-
|
245
|
-
|
246
|
-
|
243
|
+
table_add = ::DeclareSchema::SchemaChange::TableAdd.new(
|
244
|
+
table_name,
|
245
|
+
primary_key_definition + field_definitions,
|
246
|
+
table_options,
|
247
|
+
sql_options: table_options_definition.settings
|
248
|
+
)
|
249
|
+
|
247
250
|
[
|
248
251
|
table_add,
|
249
252
|
*Array((create_indexes(model) if ::DeclareSchema.default_generate_indexing)),
|
@@ -256,9 +259,9 @@ module Generators
|
|
256
259
|
fk_changes = []
|
257
260
|
table_options_changes = []
|
258
261
|
|
259
|
-
to_change.each do |
|
260
|
-
model = models_by_table_name[
|
261
|
-
table = to_rename.key(
|
262
|
+
to_change.each do |table_name|
|
263
|
+
model = models_by_table_name[table_name]
|
264
|
+
table = to_rename.key(table_name) || model.table_name
|
262
265
|
if table.in?(db_tables)
|
263
266
|
change, index_change, fk_change, table_options_change = change_table(model, table)
|
264
267
|
changes << change
|
@@ -312,6 +315,8 @@ module Generators
|
|
312
315
|
{ id: false }
|
313
316
|
elsif primary_key == "id"
|
314
317
|
{ id: :bigint }
|
318
|
+
elsif primary_key.is_a?(Array)
|
319
|
+
{ primary_key: primary_key.map(&:to_sym) }
|
315
320
|
else
|
316
321
|
{ primary_key: primary_key.to_sym }
|
317
322
|
end.merge(model._table_options)
|
@@ -414,7 +414,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
414
414
|
|
415
415
|
# You can specify the index name with index: 'name' [deprecated]
|
416
416
|
|
417
|
-
expect(
|
417
|
+
expect(ActiveSupport::Deprecation).to receive(:warn).with(/belongs_to :category, index: 'name' is deprecated; use index: \{ name: 'name' \} instead/i)
|
418
418
|
|
419
419
|
class Category < ActiveRecord::Base; end
|
420
420
|
class Advert < ActiveRecord::Base
|
@@ -487,8 +487,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
487
487
|
|
488
488
|
# You can add an index to a field definition
|
489
489
|
|
490
|
-
expect(
|
491
|
-
expect(
|
490
|
+
expect(ActiveSupport::Deprecation).to receive(:warn).with(/belongs_to :category, index: 'name' is deprecated; use index: \{ name: 'name' \} instead/i)
|
491
|
+
expect(ActiveSupport::Deprecation).to receive(:warn).with(/belongs_to :category, unique: true\|false is deprecated; use index: \{ unique: true\|false \} instead/i)
|
492
492
|
|
493
493
|
class Advert < ActiveRecord::Base
|
494
494
|
declare_schema do
|
@@ -572,7 +572,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
572
572
|
expect(Generators::DeclareSchema::Migration::Migrator.run).to(
|
573
573
|
migrate_up(<<~EOS.strip)
|
574
574
|
add_column :adverts, :title, :string, limit: 250, null: true#{charset_and_collation}
|
575
|
-
add_index :adverts, [:title], name: :my_index, length: 10
|
575
|
+
add_index :adverts, [:title], name: :my_index, length: { title: 10 }
|
576
576
|
EOS
|
577
577
|
)
|
578
578
|
|
@@ -820,6 +820,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
820
820
|
expect(User.field_specs.keys).to eq(['company'])
|
821
821
|
expect(User.field_specs['company'].options[:ruby_default]&.call).to eq("BigCorp")
|
822
822
|
|
823
|
+
nuke_model_class(User)
|
824
|
+
|
823
825
|
## validates
|
824
826
|
|
825
827
|
# DeclareSchema can accept a validates hash in the field options.
|
@@ -840,6 +842,44 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
840
842
|
up, _down = Generators::DeclareSchema::Migration::Migrator.run
|
841
843
|
ActiveRecord::Migration.class_eval(up)
|
842
844
|
expect(Ad.field_specs['company'].options[:validates].inspect).to eq("{:presence=>true, :uniqueness=>{:case_sensitive=>false}}")
|
845
|
+
|
846
|
+
# DeclareSchema supports has_and_belongs_to_many relationships and generates the intersection ("join") table
|
847
|
+
# with appropriate primary key, indexes, and foreign keys.
|
848
|
+
|
849
|
+
class Advertiser < ActiveRecord::Base
|
850
|
+
declare_schema do
|
851
|
+
string :name, limit: 250
|
852
|
+
end
|
853
|
+
has_and_belongs_to_many :creatives
|
854
|
+
end
|
855
|
+
class Creative < ActiveRecord::Base
|
856
|
+
declare_schema do
|
857
|
+
string :url, limit: 500
|
858
|
+
end
|
859
|
+
has_and_belongs_to_many :advertisers
|
860
|
+
end
|
861
|
+
|
862
|
+
expect(Generators::DeclareSchema::Migration::Migrator.run).to(
|
863
|
+
migrate_up(<<~EOS.strip)
|
864
|
+
create_table :advertisers, id: :bigint#{create_table_charset_and_collation} do |t|
|
865
|
+
t.string :name, limit: 250, null: false#{charset_and_collation}
|
866
|
+
end
|
867
|
+
create_table :advertisers_creatives, primary_key: [:advertiser_id, :creative_id]#{create_table_charset_and_collation} do |t|
|
868
|
+
t.integer :advertiser_id, limit: 8, null: false
|
869
|
+
t.integer :creative_id, limit: 8, null: false
|
870
|
+
end
|
871
|
+
create_table :creatives, id: :bigint#{create_table_charset_and_collation} do |t|
|
872
|
+
t.string :url, limit: 500, null: false#{charset_and_collation}
|
873
|
+
end
|
874
|
+
add_index :advertisers_creatives, [:creative_id], name: :index_advertisers_creatives_on_creative_id
|
875
|
+
add_foreign_key :advertisers_creatives, :advertisers, column: :advertiser_id, name: :advertisers_creatives_FK1
|
876
|
+
add_foreign_key :advertisers_creatives, :creatives, column: :creative_id, name: :advertisers_creatives_FK2
|
877
|
+
EOS
|
878
|
+
)
|
879
|
+
|
880
|
+
nuke_model_class(Ad)
|
881
|
+
nuke_model_class(Advertiser)
|
882
|
+
nuke_model_class(Creative)
|
843
883
|
end
|
844
884
|
|
845
885
|
context 'models with the same parent foreign key relation' do
|
@@ -863,7 +903,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
863
903
|
end
|
864
904
|
end
|
865
905
|
|
866
|
-
it 'will
|
906
|
+
it 'will generate unique constraint names' do
|
867
907
|
expect(Generators::DeclareSchema::Migration::Migrator.run).to(
|
868
908
|
migrate_up(<<~EOS.strip)
|
869
909
|
create_table :categories, id: :bigint, options: "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin" do |t|
|
@@ -1160,7 +1200,7 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
1160
1200
|
end
|
1161
1201
|
|
1162
1202
|
it 'deprecates limit:' do
|
1163
|
-
expect(ActiveSupport::Deprecation).to receive(:warn).with("belongs_to limit: is deprecated since it is now inferred")
|
1203
|
+
expect(ActiveSupport::Deprecation).to receive(:warn).with("belongs_to :ad_category, limit: is deprecated since it is now inferred")
|
1164
1204
|
eval <<~EOS
|
1165
1205
|
class UsingLimit < ActiveRecord::Base
|
1166
1206
|
declare_schema { }
|
@@ -11,6 +11,7 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
|
|
11
11
|
let(:join_table) { "customers_users" }
|
12
12
|
let(:foreign_keys) { ["user_id", "customer_id"] }
|
13
13
|
let(:parent_table_names) { ["users", "customers"] }
|
14
|
+
let(:connection) { instance_double(ActiveRecord::Base.connection.class, "connection") }
|
14
15
|
|
15
16
|
before do
|
16
17
|
load File.expand_path('../prepare_testapp.rb', __dir__)
|
@@ -30,8 +31,11 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
|
|
30
31
|
foreign_key: foreign_keys.first,
|
31
32
|
association_foreign_key: foreign_keys.last,
|
32
33
|
active_record: User,
|
33
|
-
class_name: 'Customer'
|
34
|
+
class_name: 'Customer',
|
35
|
+
klass: Customer) }
|
34
36
|
it 'returns a new object' do
|
37
|
+
expect(User).to receive(:connection).and_return(connection)
|
38
|
+
|
35
39
|
result = described_class.from_reflection(reflection)
|
36
40
|
|
37
41
|
expect(result).to be_a(described_class)
|
@@ -42,9 +46,7 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
|
|
42
46
|
end
|
43
47
|
|
44
48
|
describe 'instance methods' do
|
45
|
-
|
46
|
-
|
47
|
-
subject { described_class.new(join_table, foreign_keys, parent_table_names) }
|
49
|
+
subject { described_class.new(join_table, foreign_keys, parent_table_names, connection: connection) }
|
48
50
|
|
49
51
|
describe '#initialize' do
|
50
52
|
it 'stores initialization attributes' do
|
@@ -53,6 +55,12 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
|
|
53
55
|
end
|
54
56
|
end
|
55
57
|
|
58
|
+
describe '#connection' do
|
59
|
+
it 'returns the connection' do
|
60
|
+
expect(subject.connection).to be(connection)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
56
64
|
describe '#table_options' do
|
57
65
|
it 'returns empty hash' do
|
58
66
|
expect(subject._table_options).to eq({})
|
@@ -85,14 +93,14 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
|
|
85
93
|
end
|
86
94
|
|
87
95
|
describe '#primary_key' do
|
88
|
-
it 'returns false' do
|
89
|
-
expect(subject.
|
96
|
+
it 'returns false because there is no single-column PK for ActiveRecord to use' do
|
97
|
+
expect(subject.primary_key).to eq(false)
|
90
98
|
end
|
91
99
|
end
|
92
100
|
|
93
101
|
describe '#_declared_primary_key' do
|
94
|
-
it 'returns
|
95
|
-
expect(subject._declared_primary_key).to eq(
|
102
|
+
it 'returns the foreign key pair that are used as the primary key in the database' do
|
103
|
+
expect(subject._declared_primary_key).to eq(["customer_id", "user_id"])
|
96
104
|
end
|
97
105
|
end
|
98
106
|
|
@@ -101,10 +109,10 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
|
|
101
109
|
index_definitions = subject.index_definitions_with_primary_key
|
102
110
|
expect(index_definitions.size).to eq(2), index_definitions.inspect
|
103
111
|
|
104
|
-
expect(index_definitions.
|
105
|
-
expect(index_definitions.
|
106
|
-
expect(index_definitions.
|
107
|
-
expect(index_definitions.
|
112
|
+
expect(index_definitions.last).to be_a(::DeclareSchema::Model::IndexDefinition)
|
113
|
+
expect(index_definitions.last.name).to eq('PRIMARY')
|
114
|
+
expect(index_definitions.last.fields).to eq(foreign_keys.reverse)
|
115
|
+
expect(index_definitions.last.unique).to be_truthy
|
108
116
|
end
|
109
117
|
end
|
110
118
|
|
@@ -127,21 +135,38 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
|
|
127
135
|
it 'returns two index definitions and does not raise a IndexNameTooLongError' do
|
128
136
|
indexes = subject.index_definitions_with_primary_key.to_a
|
129
137
|
expect(indexes.size).to eq(2), indexes.inspect
|
130
|
-
expect(indexes.first).to be_a(::DeclareSchema::Model::IndexDefinition)
|
131
|
-
expect(indexes.first.name).to eq('PRIMARY')
|
132
|
-
expect(indexes.first.fields).to eq(foreign_keys)
|
133
|
-
expect(indexes.first.unique).to be_truthy
|
134
138
|
expect(indexes.last).to be_a(::DeclareSchema::Model::IndexDefinition)
|
135
|
-
expect(indexes.last.name).to eq('
|
136
|
-
expect(indexes.last.fields).to eq(
|
137
|
-
expect(indexes.last.unique).to
|
139
|
+
expect(indexes.last.name).to eq('PRIMARY')
|
140
|
+
expect(indexes.last.fields).to eq(foreign_keys)
|
141
|
+
expect(indexes.last.unique).to be_truthy
|
142
|
+
expect(indexes.first).to be_a(::DeclareSchema::Model::IndexDefinition)
|
143
|
+
expect(indexes.first.name).to eq('index_advertiser_campaigns_tracking_pixels_on_campaign_id')
|
144
|
+
expect(indexes.first.fields).to eq([foreign_keys.last])
|
145
|
+
expect(indexes.first.unique).to be_falsey
|
138
146
|
end
|
139
147
|
end
|
140
148
|
|
141
149
|
describe '#index_definitions' do
|
142
|
-
it 'returns
|
150
|
+
it 'returns index_definitions' do
|
143
151
|
indexes = subject.index_definitions
|
152
|
+
expect(indexes.size).to eq(1), indexes.inspect
|
153
|
+
expect(indexes.first.columns).to eq(["user_id"])
|
154
|
+
options = [:name, :unique, :where].map { |k| [k, indexes.first.send(k)] }.to_h
|
155
|
+
expect(options).to eq(name: "index_customers_users_on_user_id",
|
156
|
+
unique: false,
|
157
|
+
where: nil)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe '#index_definitions_with_primary_key' do
|
162
|
+
it 'returns index_definitions_with_primary_key' do
|
163
|
+
indexes = subject.index_definitions_with_primary_key
|
144
164
|
expect(indexes.size).to eq(2), indexes.inspect
|
165
|
+
expect(indexes.last.columns).to eq(["customer_id", "user_id"])
|
166
|
+
options = [:name, :unique, :where].map { |k| [k, indexes.last.send(k)] }.to_h
|
167
|
+
expect(options).to eq(name: "PRIMARY",
|
168
|
+
unique: true,
|
169
|
+
where: nil)
|
145
170
|
end
|
146
171
|
end
|
147
172
|
|
@@ -77,13 +77,14 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
|
|
77
77
|
let(:options) { { table_name: table_name, length: length } }
|
78
78
|
|
79
79
|
context 'with integer length' do
|
80
|
+
let(:fields) { ['last_name'] }
|
80
81
|
let(:length) { 2 }
|
81
82
|
|
82
|
-
it { is_expected.to eq(
|
83
|
+
it { is_expected.to eq(last_name: 2) }
|
83
84
|
end
|
84
85
|
|
85
86
|
context 'with Hash length' do
|
86
|
-
let(:length) { {
|
87
|
+
let(:length) { { first_name: 2 } }
|
87
88
|
|
88
89
|
it { is_expected.to eq(length) }
|
89
90
|
end
|
@@ -91,7 +92,7 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
|
|
91
92
|
|
92
93
|
describe '#options' do
|
93
94
|
subject { instance.options }
|
94
|
-
let(:options) { { name: 'my_index', table_name: table_name, unique: false, where: "(
|
95
|
+
let(:options) { { name: 'my_index', table_name: table_name, unique: false, where: "(last_name like 'a%')", length: { last_name: 10, first_name: 5 } } }
|
95
96
|
|
96
97
|
it { is_expected.to eq(options.except(:table_name)) }
|
97
98
|
end
|
@@ -163,7 +164,7 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
|
|
163
164
|
it 'returns the indexes for the model' do
|
164
165
|
expect(subject.map(&:to_key)).to eq([
|
165
166
|
["index_definition_test_models_on_name", ["name"], { unique: true, where: nil, length: nil }],
|
166
|
-
(["index_definition_test_models_on_name_partial", ["name"], { unique: false, where: nil, length: 10 }] if defined?(Mysql2)),
|
167
|
+
(["index_definition_test_models_on_name_partial", ["name"], { unique: false, where: nil, length: { name: 10 } }] if defined?(Mysql2)),
|
167
168
|
["PRIMARY", ["id"], { unique: true, where: nil, length: nil }]
|
168
169
|
].compact)
|
169
170
|
end
|
@@ -184,7 +185,7 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
|
|
184
185
|
|
185
186
|
it 'skips the ignored index' do
|
186
187
|
expect(subject.map(&:to_key)).to eq([
|
187
|
-
(["index_definition_test_models_on_name_partial", ["name"], { unique: false, where: nil, length: 10 }] if defined?(Mysql2)),
|
188
|
+
(["index_definition_test_models_on_name_partial", ["name"], { unique: false, where: nil, length: { name: 10 } }] if defined?(Mysql2)),
|
188
189
|
["PRIMARY", ["id"], { length: nil, unique: true, where: nil }]
|
189
190
|
].compact)
|
190
191
|
end
|
@@ -259,6 +260,69 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
|
|
259
260
|
end
|
260
261
|
end
|
261
262
|
end
|
263
|
+
|
264
|
+
describe '.normalize_index_length' do
|
265
|
+
let(:columns) { [:last_name] }
|
266
|
+
subject { described_class.normalize_index_length(length, columns: columns) }
|
267
|
+
|
268
|
+
context 'with nil length' do
|
269
|
+
let(:length) { nil }
|
270
|
+
|
271
|
+
it { is_expected.to eq(nil) }
|
272
|
+
end
|
273
|
+
|
274
|
+
context 'when Integer' do
|
275
|
+
let(:length) { 10 }
|
276
|
+
|
277
|
+
it { is_expected.to eq(last_name: length) }
|
278
|
+
|
279
|
+
context 'with multiple columns' do
|
280
|
+
let(:columns) { ["last_name", "first_name"] }
|
281
|
+
|
282
|
+
it { expect { subject }.to raise_exception(ArgumentError, /Index length of Integer only allowed when exactly one column; got 10 for \["last_name", "first_name"]/i) }
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
context 'when empty Hash' do
|
287
|
+
let(:length) { {} }
|
288
|
+
|
289
|
+
it { is_expected.to eq(nil) }
|
290
|
+
end
|
291
|
+
|
292
|
+
context 'when Hash' do
|
293
|
+
let(:length) { { last_name: 10 } }
|
294
|
+
|
295
|
+
it { is_expected.to eq(length) }
|
296
|
+
end
|
297
|
+
|
298
|
+
context 'when Hash with String key' do
|
299
|
+
let(:length) { { "last_name" => 10 } }
|
300
|
+
|
301
|
+
it { is_expected.to eq(last_name: 10) }
|
302
|
+
end
|
303
|
+
|
304
|
+
context 'with multiple columns' do
|
305
|
+
let(:columns) { [:last_name, :first_name] }
|
306
|
+
|
307
|
+
context 'when Hash with String keys' do
|
308
|
+
let(:length) { { "last_name" => 10, "first_name" => 5 } }
|
309
|
+
|
310
|
+
it { is_expected.to eq(last_name: 10, first_name: 5) }
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
context 'with nil length' do
|
315
|
+
let(:length) { nil }
|
316
|
+
|
317
|
+
it { is_expected.to eq(nil) }
|
318
|
+
end
|
319
|
+
|
320
|
+
context 'with an invalid length' do
|
321
|
+
let(:length) { 10.5 }
|
322
|
+
|
323
|
+
it { expect { subject }.to raise_exception(ArgumentError, /length must be nil or Integer or a Hash of column names to lengths; got 10\.5 for \[:last_name]/i) }
|
324
|
+
end
|
325
|
+
end
|
262
326
|
end
|
263
327
|
# TODO: fill out remaining tests
|
264
328
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: declare_schema
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.0.colin.
|
4
|
+
version: 1.4.0.colin.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Invoca Development adapted from hobo_fields by Tom Locke
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-01-
|
11
|
+
date: 2024-01-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|