declare_schema 1.4.0.colin.9 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -1
- data/Gemfile.lock +1 -1
- data/lib/declare_schema/model/index_definition.rb +1 -1
- data/lib/declare_schema/model.rb +50 -5
- data/lib/declare_schema/schema_change/column_add.rb +4 -2
- data/lib/declare_schema/version.rb +1 -1
- data/lib/declare_schema.rb +13 -3
- data/lib/generators/declare_schema/migration/migrator.rb +1 -1
- data/spec/lib/declare_schema/migration_generator_spec.rb +48 -0
- data/spec/lib/declare_schema_spec.rb +40 -0
- 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: a00635546beea8512ef5678ea8d182612f6a6e5b099d6a13c4287e52312e8490
|
4
|
+
data.tar.gz: f5ba19f1fd2dc5d3c66923dda796e413f4a3bcd4586764b254c4345602ebb984
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4cd37d8cf76460b9af36e1f05c7e5c42f370eae8fec0936e563a6dcd74e7176d1d8c66e6fa464d7e58429be220be0dbb5b6a7f5bf7c19dfb0ff834e53771e806
|
7
|
+
data.tar.gz: '0870d0173821829d81c8033fda33921c8de00f7b1ec5aa2b9da56fb15b7d73b716a14058be0dc2ad595bc2bf2a6b9b66c8cdcbb612dd91fd21d88c0992b0e057'
|
data/CHANGELOG.md
CHANGED
@@ -4,12 +4,21 @@ Inspired by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
4
4
|
|
5
5
|
Note: this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
-
## [1.4] -
|
7
|
+
## [1.4.0] - 2024-01-24
|
8
8
|
### Added
|
9
9
|
- Added support for partial indexes with `length:` option.
|
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.6] - 2024-01-22
|
14
|
+
### Fixed
|
15
|
+
- Add missing commits around connection: and Array check for composite declared primary key.
|
16
|
+
|
17
|
+
## [1.3.5] - 2024-01-22
|
18
|
+
### Fixed
|
19
|
+
- Make `default_charset=` and `default_collation=` lazy so they don't use the database connection to check the
|
20
|
+
MySQL version. Instead, that is checked the first time `default_charset` or `default_collation` is called.
|
21
|
+
|
13
22
|
## [1.3.4] - 2024-01-18
|
14
23
|
### Fixed
|
15
24
|
- Add test for migrating `has_and_belongs_to_many` associations and fix them to properly declare their
|
data/Gemfile.lock
CHANGED
@@ -18,7 +18,7 @@ module DeclareSchema
|
|
18
18
|
|
19
19
|
def initialize(columns, table_name:, name: nil, allow_equivalent: false, unique: false, where: nil, length: nil)
|
20
20
|
@table_name = table_name
|
21
|
-
@name = name || self.class.default_index_name(table_name, columns)
|
21
|
+
@name = (name || self.class.default_index_name(table_name, columns)).to_s
|
22
22
|
@columns = Array.wrap(columns).map(&:to_s)
|
23
23
|
@explicit_name = @name if !allow_equivalent
|
24
24
|
unique.in?([false, true]) or raise ArgumentError, "unique must be true or false: got #{unique.inspect}"
|
data/lib/declare_schema/model.rb
CHANGED
@@ -50,10 +50,23 @@ module DeclareSchema
|
|
50
50
|
|
51
51
|
module ClassMethods
|
52
52
|
def index(columns, name: nil, allow_equivalent: false, unique: false, where: nil, length: nil)
|
53
|
-
|
53
|
+
index_definition = ::DeclareSchema::Model::IndexDefinition.new(
|
54
54
|
columns,
|
55
55
|
name: name, table_name: table_name, allow_equivalent: allow_equivalent, unique: unique, where: where, length: length
|
56
56
|
)
|
57
|
+
|
58
|
+
if (equivalent = index_definitions.find { index_definition.equivalent?(_1) }) # differs only by name
|
59
|
+
if equivalent == index_definition
|
60
|
+
# identical is always idempotent
|
61
|
+
else
|
62
|
+
# equivalent is idempotent iff allow_equivalent: true passed
|
63
|
+
allow_equivalent or
|
64
|
+
raise ArgumentError, "equivalent index definition found (pass allow_equivalent: true to ignore):\n" \
|
65
|
+
"#{index_definition.inspect}\n#{equivalent.inspect}"
|
66
|
+
end
|
67
|
+
else
|
68
|
+
index_definitions << index_definition
|
69
|
+
end
|
57
70
|
end
|
58
71
|
|
59
72
|
def primary_key_index(*columns)
|
@@ -61,11 +74,13 @@ module DeclareSchema
|
|
61
74
|
end
|
62
75
|
|
63
76
|
def constraint(foreign_key_column, parent_table_name: nil, constraint_name: nil, parent_class_name: nil, dependent: nil)
|
64
|
-
|
77
|
+
constraint_definition = ::DeclareSchema::Model::ForeignKeyDefinition.new(
|
65
78
|
foreign_key_column.to_s,
|
66
79
|
constraint_name: constraint_name,
|
67
80
|
child_table_name: table_name, parent_table_name: parent_table_name, parent_class_name: parent_class_name, dependent: dependent
|
68
81
|
)
|
82
|
+
|
83
|
+
constraint_definitions << constraint_definition # Set<> implements idempotent insert.
|
69
84
|
end
|
70
85
|
|
71
86
|
# tell the migration generator to ignore the named index. Useful for existing indexes, or for indexes
|
@@ -100,7 +115,37 @@ module DeclareSchema
|
|
100
115
|
# 1. creates a FieldSpec for the foreign key
|
101
116
|
# 2. declares an index on the foreign key (optional)
|
102
117
|
# 3. declares a foreign_key constraint (optional)
|
118
|
+
# @param name [Symbol] the name of the association to pass to super
|
119
|
+
# @param scope [Proc] the scope of the association to pass to super
|
120
|
+
# @option options [Boolean] :optional (default: false) whether the foreign key column should be nullable and whether
|
121
|
+
# ActiveRecord should validate presence of the foreign key (passed through to super)
|
122
|
+
# @option options [Boolean] :null (default: inferred from options[:optional]) whether the foreign key column should be nullable
|
123
|
+
# (`null:` should only be passed if it is the inverse of `optional:`; otherwise it is redundant)
|
124
|
+
# @option options [Integer] :limit (default: inferred from the primary key limit:) the limit of the foreign key column size (4 or 8)
|
125
|
+
# @option options [Boolean|Hash<Symbol>] :index (default: true) whether to create an index on the foreign key; can be true or false
|
126
|
+
# or a hash of options to pass to the index declaration, with keys like { name: ..., unique: ... }
|
127
|
+
# @option options [Boolean] :allow_equivalent (default: false) whether to allow an existing index with a different name
|
128
|
+
# @option options [Boolean|String] :constraint (default: true) whether to create a foreign key constraint on the foreign key;
|
129
|
+
# may be true or false or a string to use as the constraint name
|
130
|
+
# @option options [Boolean] :polymorphic (default: false) whether this is a polymorphic belongs_to with a _type column next to
|
131
|
+
# the foreign key _id column (also passed through to super)
|
132
|
+
# @option options [Boolean] :far_end_dependent (default: nil) whether to add a dependent: :delete to the far end of the foreign key
|
133
|
+
# constraint
|
134
|
+
# @option options [String] :foreign_type (default: "#{name}_type") the name prefix for the _type column for a polymorphic belongs_to
|
135
|
+
# (passed through to super)
|
136
|
+
# Other options are passed through to super
|
103
137
|
def belongs_to(name, scope = nil, **options)
|
138
|
+
if options[:null].in?([true, false]) && options[:optional] == options[:null]
|
139
|
+
STDERR.puts("[declare_schema warning] belongs_to #{name.inspect}, null: with the same value as optional: is redundant; omit null: #{options[:null]} (called from #{caller[0]})")
|
140
|
+
elsif !options.has_key?(:optional)
|
141
|
+
case options[:null]
|
142
|
+
when true
|
143
|
+
STDERR.puts("[declare_schema] belongs_to #{name.inspect}, null: true is deprecated in favor of optional: true (called from #{caller[0]})")
|
144
|
+
when false
|
145
|
+
STDERR.puts("[declare_schema] belongs_to #{name.inspect}, null: false is implied and can be omitted (called from #{caller[0]})")
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
104
149
|
column_options = {}
|
105
150
|
|
106
151
|
column_options[:null] = if options.has_key?(:null)
|
@@ -125,18 +170,18 @@ module DeclareSchema
|
|
125
170
|
index_options = {} # create an index
|
126
171
|
case index_value
|
127
172
|
when String, Symbol
|
128
|
-
ActiveSupport::Deprecation.warn("belongs_to #{name.inspect}, index: 'name' is deprecated; use index: { name: 'name' } instead (in #{self.name})")
|
173
|
+
ActiveSupport::Deprecation.warn("[declare_schema] belongs_to #{name.inspect}, index: 'name' is deprecated; use index: { name: 'name' } instead (in #{self.name})")
|
129
174
|
index_options[:name] = index_value.to_s
|
130
175
|
when true
|
131
176
|
when nil
|
132
177
|
when Hash
|
133
178
|
index_options = index_value
|
134
179
|
else
|
135
|
-
raise ArgumentError, "belongs_to #{name.inspect}, index: must be true or false or a Hash; got #{index_value.inspect} (in #{self.name})"
|
180
|
+
raise ArgumentError, "[declare_schema] belongs_to #{name.inspect}, index: must be true or false or a Hash; got #{index_value.inspect} (in #{self.name})"
|
136
181
|
end
|
137
182
|
|
138
183
|
if options.has_key?(:unique)
|
139
|
-
ActiveSupport::Deprecation.warn("belongs_to #{name.inspect}, unique: true|false is deprecated; use index: { unique: true|false } instead (in #{self.name})")
|
184
|
+
ActiveSupport::Deprecation.warn("[declare_schema] belongs_to #{name.inspect}, unique: true|false is deprecated; use index: { unique: true|false } instead (in #{self.name})")
|
140
185
|
index_options[:unique] = options.delete(:unique)
|
141
186
|
end
|
142
187
|
|
@@ -6,8 +6,10 @@ module DeclareSchema
|
|
6
6
|
module SchemaChange
|
7
7
|
class ColumnAdd < Base
|
8
8
|
def initialize(table_name, column_name, column_type, **column_options)
|
9
|
-
|
10
|
-
|
9
|
+
table_name.is_a?(String) || table_name.is_a?(Symbol) or raise ArgumentError, "must provide String|Symbol table_name; got #{table_name.inspect}"
|
10
|
+
column_name.is_a?(String) || column_name.is_a?(Symbol) or raise ArgumentError, "must provide String|Symbol column_name; got #{column_name.inspect}"
|
11
|
+
@table_name = table_name
|
12
|
+
@column_name = column_name
|
11
13
|
@column_type = column_type or raise ArgumentError, "must provide column_type"
|
12
14
|
@column_options = column_options
|
13
15
|
end
|
data/lib/declare_schema.rb
CHANGED
@@ -36,7 +36,7 @@ module DeclareSchema
|
|
36
36
|
|
37
37
|
class << self
|
38
38
|
attr_writer :mysql_version
|
39
|
-
attr_reader :
|
39
|
+
attr_reader :default_text_limit, :default_string_limit, :default_null,
|
40
40
|
:default_generate_foreign_keys, :default_generate_indexing, :db_migrate_command,
|
41
41
|
:max_index_and_constraint_name_length
|
42
42
|
|
@@ -81,12 +81,22 @@ module DeclareSchema
|
|
81
81
|
|
82
82
|
def default_charset=(charset)
|
83
83
|
charset.is_a?(String) or raise ArgumentError, "charset must be a string (got #{charset.inspect})"
|
84
|
-
@
|
84
|
+
@default_charset_before_normalization = charset
|
85
|
+
@default_charset = nil
|
86
|
+
end
|
87
|
+
|
88
|
+
def default_charset
|
89
|
+
@default_charset ||= normalize_charset(@default_charset_before_normalization)
|
85
90
|
end
|
86
91
|
|
87
92
|
def default_collation=(collation)
|
88
93
|
collation.is_a?(String) or raise ArgumentError, "collation must be a string (got #{collation.inspect})"
|
89
|
-
@
|
94
|
+
@default_collation_before_normalization = collation
|
95
|
+
@default_collation = nil
|
96
|
+
end
|
97
|
+
|
98
|
+
def default_collation
|
99
|
+
@default_collation ||= normalize_collation(@default_collation_before_normalization)
|
90
100
|
end
|
91
101
|
|
92
102
|
def default_text_limit=(text_limit)
|
@@ -352,7 +352,7 @@ module Generators
|
|
352
352
|
|
353
353
|
db_columns = model.connection.columns(current_table_name).index_by(&:name)
|
354
354
|
if (pk = model._declared_primary_key.presence)
|
355
|
-
pk_was_in_db_columns = db_columns.delete(pk)
|
355
|
+
pk_was_in_db_columns = pk.is_a?(Array) || db_columns.delete(pk)
|
356
356
|
end
|
357
357
|
|
358
358
|
model_column_names = model.field_specs.keys.map(&:to_s)
|
@@ -1437,5 +1437,53 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
1437
1437
|
end
|
1438
1438
|
end
|
1439
1439
|
end
|
1440
|
+
|
1441
|
+
context 'index' do
|
1442
|
+
before do
|
1443
|
+
class Advert < active_record_base_class.constantize
|
1444
|
+
declare_schema { }
|
1445
|
+
belongs_to :ad_category
|
1446
|
+
end
|
1447
|
+
end
|
1448
|
+
|
1449
|
+
it "is idempotent and doesn't raise" do
|
1450
|
+
expect do
|
1451
|
+
Advert.index [:ad_category_id], name: :index_adverts_on_ad_category_id
|
1452
|
+
end.to_not change { Advert.index_definitions.size }
|
1453
|
+
end
|
1454
|
+
|
1455
|
+
it "when equivalent but not marked to allow, it raises" do
|
1456
|
+
expect do
|
1457
|
+
Advert.index [:ad_category_id], name: :on_ad_category_id
|
1458
|
+
end.to raise_exception(ArgumentError, /equivalent index definition found/i)
|
1459
|
+
end
|
1460
|
+
|
1461
|
+
it "when equivalent and marked to allow, it is idempotent and doesn't raise" do
|
1462
|
+
expect do
|
1463
|
+
Advert.index [:ad_category_id], name: :on_ad_category_id, allow_equivalent: true
|
1464
|
+
end.to_not change { Advert.index_definitions.size }
|
1465
|
+
end
|
1466
|
+
|
1467
|
+
context 'constraint' do
|
1468
|
+
before do
|
1469
|
+
class Advert < active_record_base_class.constantize
|
1470
|
+
declare_schema { }
|
1471
|
+
belongs_to :ad_category
|
1472
|
+
end
|
1473
|
+
end
|
1474
|
+
|
1475
|
+
it "when exactly equal, it is idempotent and doesn't raise" do
|
1476
|
+
expect do
|
1477
|
+
Advert.constraint :ad_category_id, parent_table_name: 'ad_categories', constraint_name: :index_adverts_on_ad_category_id, parent_class_name: 'AdCategory'
|
1478
|
+
end.to_not change { Advert.index_definitions.size }
|
1479
|
+
end
|
1480
|
+
|
1481
|
+
it "when equivalent, it is idempotent and doesn't raise" do
|
1482
|
+
expect do
|
1483
|
+
Advert.constraint :ad_category_id, parent_table_name: 'ad_categories', constraint_name: :constraint_1, parent_class_name: 'AdCategory'
|
1484
|
+
end.to_not change { Advert.index_definitions.size }
|
1485
|
+
end
|
1486
|
+
end
|
1487
|
+
end
|
1440
1488
|
end
|
1441
1489
|
end
|
@@ -37,6 +37,26 @@ RSpec.describe DeclareSchema do
|
|
37
37
|
it { is_expected.to eq("utf8mb3") }
|
38
38
|
end
|
39
39
|
end
|
40
|
+
|
41
|
+
context 'when MySQL version not known yet' do
|
42
|
+
before { described_class.remove_instance_variable('@mysql_version') rescue nil }
|
43
|
+
after { described_class.remove_instance_variable('@mysql_version') rescue nil }
|
44
|
+
|
45
|
+
context 'when set' do
|
46
|
+
let(:connection) { double("connection", select_value: "8.0.21") }
|
47
|
+
|
48
|
+
it "is lazy, so it doesn't use the database connection until read" do
|
49
|
+
expect(ActiveRecord::Base).to receive(:connection) do
|
50
|
+
@connection_called = true
|
51
|
+
connection
|
52
|
+
end
|
53
|
+
described_class.default_charset = "utf8"
|
54
|
+
expect(@connection_called).to be_falsey
|
55
|
+
described_class.default_charset
|
56
|
+
expect(@connection_called).to be_truthy
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
40
60
|
end
|
41
61
|
|
42
62
|
describe '#default_collation' do
|
@@ -81,6 +101,26 @@ RSpec.describe DeclareSchema do
|
|
81
101
|
it { is_expected.to eq("utf8mb3_general_ci") }
|
82
102
|
end
|
83
103
|
end
|
104
|
+
|
105
|
+
context 'when MySQL version not known yet' do
|
106
|
+
before { described_class.remove_instance_variable('@mysql_version') rescue nil }
|
107
|
+
after { described_class.remove_instance_variable('@mysql_version') rescue nil }
|
108
|
+
|
109
|
+
context 'when set' do
|
110
|
+
let(:connection) { double("connection", select_value: "8.0.21") }
|
111
|
+
|
112
|
+
it "is lazy, so it doesn't use the database connection until read" do
|
113
|
+
expect(ActiveRecord::Base).to receive(:connection) do
|
114
|
+
@connection_called = true
|
115
|
+
connection
|
116
|
+
end
|
117
|
+
described_class.default_collation = "utf8_general_ci"
|
118
|
+
expect(@connection_called).to be_falsey
|
119
|
+
described_class.default_collation
|
120
|
+
expect(@connection_called).to be_truthy
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
84
124
|
end
|
85
125
|
|
86
126
|
describe '#default_text_limit' do
|
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
|
4
|
+
version: 1.4.0
|
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-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|