declare_schema 1.5.0.pre.1 → 2.1.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.devcontainer/Dockerfile +19 -0
- data/.devcontainer/boot.sh +1 -0
- data/.devcontainer/devcontainer.json +55 -0
- data/.devcontainer/docker-compose.yml +40 -0
- data/.github/workflows/declare_schema_build.yml +35 -11
- data/Appraisals +2 -10
- data/CHANGELOG.md +8 -1
- data/CODE-OF-CONDUCT.md +16 -0
- data/CONTRIBUTING.md +46 -0
- data/Gemfile +6 -1
- data/Gemfile.lock +88 -78
- data/README.md +38 -14
- data/Rakefile +5 -15
- data/catalog-info.yaml +35 -0
- data/config/brakeman.ignore +2 -2
- data/declare_schema.gemspec +1 -1
- data/gemfiles/{rails_6_1_sqlite3.gemfile → rails_6_1.gemfile} +3 -0
- data/gemfiles/{rails_7_0_sqlite3.gemfile → rails_7_0.gemfile} +3 -0
- data/gemfiles/{rails_7_1_sqlite3.gemfile → rails_7_1.gemfile} +3 -0
- data/gemfiles/{rails_7_0_mysql2.gemfile → rails_7_2.gemfile} +4 -1
- data/lib/declare_schema/command.rb +2 -8
- data/lib/declare_schema/model/column.rb +1 -1
- data/lib/declare_schema/model/foreign_key_definition.rb +9 -12
- data/lib/declare_schema/model/index_definition.rb +16 -8
- data/lib/declare_schema/model.rb +10 -14
- data/lib/declare_schema/schema_change/base.rb +4 -0
- data/lib/declare_schema/schema_change/primary_key_change.rb +19 -6
- data/lib/declare_schema/version.rb +1 -1
- data/lib/declare_schema.rb +20 -8
- data/lib/generators/declare_schema/migration/migration_generator.rb +17 -8
- data/lib/generators/declare_schema/migration/migrator.rb +16 -9
- data/spec/fixtures/migrations/mysql2/will_generate_unique_constraint_names_rails_6.txt +15 -0
- data/spec/fixtures/migrations/mysql2/will_generate_unique_constraint_names_rails_7.txt +15 -0
- data/spec/fixtures/migrations/postgresql/will_generate_unique_constraint_names_rails_6.txt +15 -0
- data/spec/fixtures/migrations/postgresql/will_generate_unique_constraint_names_rails_7.txt +15 -0
- data/spec/fixtures/migrations/sqlite3/will_generate_unique_constraint_names_rails_6.txt +15 -0
- data/spec/fixtures/migrations/sqlite3/will_generate_unique_constraint_names_rails_7.txt +15 -0
- data/spec/lib/declare_schema/api_spec.rb +1 -7
- data/spec/lib/declare_schema/field_declaration_dsl_spec.rb +3 -3
- data/spec/lib/declare_schema/field_spec_spec.rb +68 -45
- data/spec/lib/declare_schema/generator_spec.rb +29 -71
- data/spec/lib/declare_schema/interactive_primary_key_spec.rb +3 -10
- data/spec/lib/declare_schema/migration_generator_spec.rb +249 -255
- data/spec/lib/declare_schema/model/column_spec.rb +89 -41
- data/spec/lib/declare_schema/model/foreign_key_definition_spec.rb +14 -8
- data/spec/lib/declare_schema/model/habtm_model_shim_spec.rb +4 -9
- data/spec/lib/declare_schema/model/index_definition_spec.rb +18 -10
- data/spec/lib/declare_schema/model/table_options_definition_spec.rb +11 -11
- data/spec/lib/declare_schema/schema_change/base_spec.rb +5 -7
- data/spec/lib/declare_schema/schema_change/column_add_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/column_change_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/column_remove_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/column_rename_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/foreign_key_add_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/foreign_key_remove_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/index_add_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/index_remove_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/primary_key_change_spec.rb +39 -15
- data/spec/lib/declare_schema/schema_change/table_add_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/table_change_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/table_remove_spec.rb +1 -3
- data/spec/lib/declare_schema/schema_change/table_rename_spec.rb +1 -3
- data/spec/lib/generators/declare_schema/migration/migrator_spec.rb +0 -4
- data/spec/spec_helper.rb +3 -0
- data/spec/support/adapter_specific_test_helpers.rb +25 -0
- data/spec/{lib/declare_schema → support}/prepare_testapp.rb +3 -1
- data/spec/support/test_app_spec_helpers.rb +7 -0
- metadata +24 -11
- data/gemfiles/rails_6_1_mysql2.gemfile +0 -23
- data/gemfiles/rails_7_1_mysql2.gemfile +0 -23
data/README.md
CHANGED
@@ -2,6 +2,26 @@
|
|
2
2
|
|
3
3
|
Declare your Rails/ActiveRecord model schemas and have database migrations generated for you!
|
4
4
|
|
5
|
+
The `DeclareSchema` gem provides a DSL for declaring your model schemas in a block, and then generates the corresponding database migration for you. It also provides a way to configure the default schema for all models, and to ignore certain tables.
|
6
|
+
|
7
|
+
Currently tested against:
|
8
|
+
- Ruby 3.0+
|
9
|
+
- Rails 6.1+
|
10
|
+
- MySQL 5.7
|
11
|
+
- SQLite
|
12
|
+
- PostgreSQL 16
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Install the `DeclareSchema` gem directly:
|
17
|
+
```
|
18
|
+
$ gem install declare_schema
|
19
|
+
```
|
20
|
+
or add it to your `bundler` Gemfile:
|
21
|
+
```
|
22
|
+
gem 'declare_schema'
|
23
|
+
```
|
24
|
+
|
5
25
|
## Example
|
6
26
|
|
7
27
|
Make a model and declare your schema within a `declare_schema do ... end` block:
|
@@ -115,6 +135,8 @@ The following `index` options are supported:
|
|
115
135
|
- `length` (integer or hash) - The partial index length(s). If an integer is provided, it is used as the length for all columns. If a hash is provided, it is used to specify the length for individual columns, where the column names are given as `Symbol` hash keys.
|
116
136
|
- `where` (string) - The subset index predicate.
|
117
137
|
|
138
|
+
**Note:** The `DeclareSchema.max_index_and_constraint_name_length` setting is ignored when using `PostgreSQL` since that database does not have a limit on the length of index names, and requires globally unique index names.
|
139
|
+
|
118
140
|
## Usage without Rails
|
119
141
|
|
120
142
|
When using `DeclareSchema` without Rails, you can use the `declare_schema/rake` task to generate the migration file.
|
@@ -314,6 +336,9 @@ DeclareSchema.max_index_and_constraint_name_length = 64
|
|
314
336
|
If you know that your migrations will only be used on a database type with a different limit, you can
|
315
337
|
adjust this configuration value. A `nil` value means "unlimited".
|
316
338
|
|
339
|
+
**Note:** This setting is ignored when using `PostgreSQL` since that database does not have a limit on the length of index names,
|
340
|
+
and requires globally unique index names.
|
341
|
+
|
317
342
|
## Declaring Character Set and Collation
|
318
343
|
_Note: This feature currently only works for MySQL database configurations._
|
319
344
|
|
@@ -367,19 +392,18 @@ class Comment < ActiveRecord::Base
|
|
367
392
|
end
|
368
393
|
```
|
369
394
|
|
370
|
-
##
|
395
|
+
## Contributions
|
371
396
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
rake test:
|
384
|
-
rake test:all
|
397
|
+
Contributions to this project are always welcome. Please thoroughly read our [Contribution Guidelines](CONTRIBUTING.md) before starting any work.
|
398
|
+
|
399
|
+
### Local Development
|
400
|
+
|
401
|
+
Depending on the database you are developing for, you may need to install the appropriate database client and server in order to appropriately test your changes. To help with this, we've provided a [DevContainer](https://github.com/devcontainers) configuration that will set up a development environment for you. To use this, you will need to have Docker installed on your machine.
|
402
|
+
|
403
|
+
### Running Tests Locally
|
404
|
+
To run tests locally, you need to prepare a test application using the specific adapter you'd like to test against. For example, to test against MySQL:
|
405
|
+
|
406
|
+
```bash
|
407
|
+
bundle exec rake test:prepare_testapp[mysql,true]
|
408
|
+
bundle exec rake test:all
|
385
409
|
```
|
data/Rakefile
CHANGED
@@ -26,24 +26,14 @@ namespace "test" do
|
|
26
26
|
task all: :spec
|
27
27
|
|
28
28
|
desc "Prepare a rails application for testing"
|
29
|
-
task :prepare_testapp, :force do |_t, args|
|
29
|
+
task :prepare_testapp, [:adapter, :force] do |_t, args|
|
30
30
|
if args.force || !File.directory?(TESTAPP_PATH)
|
31
31
|
FileUtils.remove_entry_secure(TESTAPP_PATH, true)
|
32
|
-
sh %(#{BIN} new #{TESTAPP_PATH} --skip-wizard --skip-bundle --api)
|
32
|
+
sh %(#{BIN} new #{TESTAPP_PATH} --skip-wizard --skip-bundle --api -d #{args.adapter})
|
33
33
|
FileUtils.chdir(TESTAPP_PATH)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
sh "(echo 'H';
|
38
|
-
echo '1,$s/localhost/127.0.0.1/';
|
39
|
-
echo '/host:/';
|
40
|
-
echo 'a';
|
41
|
-
echo ' port: #{ENV['MYSQL_PORT']}';
|
42
|
-
echo '.';
|
43
|
-
echo w;
|
44
|
-
echo q) | ed #{TESTAPP_PATH}/config/database.yml || echo ed failed!"
|
45
|
-
end
|
46
|
-
rescue LoadError
|
34
|
+
if args.adapter == 'mysql'
|
35
|
+
sh "sed -i -e 's/host:.*/host: <%= ENV[\"MYSQL_HOST\"].presence || \"localhost\" %>/g' #{TESTAPP_PATH}/config/database.yml || echo sed failed!"
|
36
|
+
sh "sed -i -e 's/password:.*/password: <%= ENV[\"MYSQL_PASSWORD\"].presence %>/g' #{TESTAPP_PATH}/config/database.yml || echo sed failed!"
|
47
37
|
end
|
48
38
|
sh "bundle install"
|
49
39
|
sh "(echo '';
|
data/catalog-info.yaml
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# This file is partially auto-generated by the invoca-backstage-tools gem
|
2
|
+
# The following fields should not be edited manually as they are auto-generated
|
3
|
+
# based on the contents of the repo:
|
4
|
+
# - metadata.name
|
5
|
+
# - metadata.title
|
6
|
+
# - metadata.description
|
7
|
+
# - annotations.github.com/project-slug
|
8
|
+
# - invoca.com/version-repository-location
|
9
|
+
# - invoca.com/version-repository-name
|
10
|
+
# - spec.type
|
11
|
+
# - spec.owner
|
12
|
+
---
|
13
|
+
apiVersion: backstage.io/v1alpha1
|
14
|
+
kind: Component
|
15
|
+
metadata:
|
16
|
+
name: declare_schema-gem
|
17
|
+
title: DeclareSchema
|
18
|
+
annotations:
|
19
|
+
buildkite.com/project-slug: Invoca/declare_schema
|
20
|
+
github.com/project-slug: invoca/declare_schema
|
21
|
+
invoca.com/version-repository-location: rubygems
|
22
|
+
invoca.com/version-repository-name: declare_schema
|
23
|
+
endoflife.date/products: ruby@3.1
|
24
|
+
tags:
|
25
|
+
- ruby
|
26
|
+
- open-source
|
27
|
+
- gem
|
28
|
+
- executable
|
29
|
+
description: Declare your Rails/active_record model schemas and have database migrations
|
30
|
+
generated for you!
|
31
|
+
spec:
|
32
|
+
type: library
|
33
|
+
lifecycle: production
|
34
|
+
owner: octothorpe
|
35
|
+
dependsOn: []
|
data/config/brakeman.ignore
CHANGED
@@ -26,11 +26,11 @@
|
|
26
26
|
{
|
27
27
|
"warning_type": "Command Injection",
|
28
28
|
"warning_code": 14,
|
29
|
-
"fingerprint": "
|
29
|
+
"fingerprint": "6b90f8dd199afbdf79cc8c4d00a0853e0696b067d00c0fe93071e31b69de8628",
|
30
30
|
"check_name": "Execute",
|
31
31
|
"message": "Possible command injection",
|
32
32
|
"file": "lib/declare_schema/command.rb",
|
33
|
-
"line":
|
33
|
+
"line": 45,
|
34
34
|
"link": "https://brakemanscanner.org/docs/warning_types/command_injection/",
|
35
35
|
"code": "system(\"rails new #{\"new\"} #{(args * \" \")} -m #{File.join(Dir.tmpdir, \"declare_schema_app_template\")}#{begin\n (require(\"mysql2\")\n \" -d mysql\")\nrescue LoadError\n # do nothing\nend}\")",
|
36
36
|
"render_path": null,
|
data/declare_schema.gemspec
CHANGED
@@ -8,12 +8,15 @@ gem "mail"
|
|
8
8
|
gem "net-smtp"
|
9
9
|
gem "pry"
|
10
10
|
gem "pry-byebug"
|
11
|
-
gem "rails", "~> 7.
|
11
|
+
gem "rails", "~> 7.2.0"
|
12
12
|
gem "responders"
|
13
13
|
gem "rspec"
|
14
|
+
gem "rspec-its"
|
14
15
|
gem "rubocop"
|
15
16
|
gem "yard"
|
16
17
|
gem "mysql2", "~> 0.5"
|
18
|
+
gem "pg", "~> 1.1"
|
19
|
+
gem "sqlite3", "~> 1.4"
|
17
20
|
|
18
21
|
group :testapp do
|
19
22
|
gem "bootsnap", ">= 1.1.0", require: false
|
@@ -41,14 +41,8 @@ module DeclareSchema
|
|
41
41
|
file.puts ["gem '#{gem}', '>= #{version}'", (gemfile_options.inspect unless gemfile_options.empty?)].compact.join(', ')
|
42
42
|
end
|
43
43
|
puts "Generating Rails infrastructure..."
|
44
|
-
|
45
|
-
|
46
|
-
require 'mysql2'
|
47
|
-
' -d mysql'
|
48
|
-
rescue LoadError
|
49
|
-
end
|
50
|
-
puts("rails new #{app_name} #{args * ' '} -m #{template_path}#{database_option}")
|
51
|
-
system("rails new #{app_name} #{args * ' '} -m #{template_path}#{database_option}")
|
44
|
+
puts("rails new #{app_name} #{args * ' '} -m #{template_path}")
|
45
|
+
system("rails new #{app_name} #{args * ' '} -m #{template_path}")
|
52
46
|
File.delete(template_path)
|
53
47
|
|
54
48
|
when /^(g|generate|destroy)$/
|
@@ -52,7 +52,7 @@ module DeclareSchema
|
|
52
52
|
|
53
53
|
def deserialize_default_value(column, type, default_value)
|
54
54
|
type or raise ArgumentError, "must pass type; got #{type.inspect}"
|
55
|
-
cast_type = ActiveRecord::Base.connection.
|
55
|
+
cast_type = ActiveRecord::Base.connection.lookup_cast_type_from_column(column) or
|
56
56
|
raise "cast_type not found for #{type}"
|
57
57
|
cast_type.deserialize(default_value)
|
58
58
|
end
|
@@ -31,19 +31,16 @@ module DeclareSchema
|
|
31
31
|
end
|
32
32
|
|
33
33
|
class << self
|
34
|
+
# TODO: I think we might just be able to start using the AR built in moving forward
|
34
35
|
def for_table(child_table_name, connection, dependent: nil)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
constraint_name: constraint_name,
|
44
|
-
child_table_name: child_table_name,
|
45
|
-
parent_table_name: parent_table_name,
|
46
|
-
dependent: dependent_value)
|
36
|
+
connection.foreign_keys(child_table_name).map do |fkc|
|
37
|
+
new(
|
38
|
+
fkc.column,
|
39
|
+
constraint_name: fkc.name,
|
40
|
+
child_table_name: fkc.from_table,
|
41
|
+
parent_table_name: fkc.to_table,
|
42
|
+
dependent: dependent || fkc.on_delete == :cascade ? :delete : nil
|
43
|
+
)
|
47
44
|
end
|
48
45
|
end
|
49
46
|
end
|
@@ -64,14 +64,11 @@ module DeclareSchema
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def default_index_name(table_name, columns)
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end
|
73
|
-
end or raise IndexNameTooLongError,
|
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."
|
67
|
+
if DeclareSchema.current_adapter == 'postgresql'
|
68
|
+
long_index_name(table_name, columns)
|
69
|
+
else
|
70
|
+
longest_valid_index_name(table_name, columns)
|
71
|
+
end
|
75
72
|
end
|
76
73
|
|
77
74
|
# This method normalizes the length option to be either nil or a Hash of Symbol column names to lengths,
|
@@ -108,6 +105,17 @@ module DeclareSchema
|
|
108
105
|
end
|
109
106
|
end
|
110
107
|
|
108
|
+
def longest_valid_index_name(table_name, columns)
|
109
|
+
index_name = nil
|
110
|
+
[:long_index_name, :short_index_name].find do |method_name|
|
111
|
+
index_name = send(method_name, table_name, columns)
|
112
|
+
if DeclareSchema.max_index_and_constraint_name_length.nil? || index_name.length <= DeclareSchema.max_index_and_constraint_name_length
|
113
|
+
break index_name
|
114
|
+
end
|
115
|
+
end or raise IndexNameTooLongError,
|
116
|
+
"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." # rubocop:disable Layout/LineLength
|
117
|
+
end
|
118
|
+
|
111
119
|
def long_index_name(table_name, columns)
|
112
120
|
"index_#{table_name}_on_#{Array(columns).join("_and_")}"
|
113
121
|
end
|
data/lib/declare_schema/model.rb
CHANGED
@@ -157,7 +157,7 @@ module DeclareSchema
|
|
157
157
|
column_options[:default] = options.delete(:default) if options.has_key?(:default)
|
158
158
|
if options.has_key?(:limit)
|
159
159
|
options.delete(:limit)
|
160
|
-
|
160
|
+
DeclareSchema.deprecator.warn("belongs_to #{name.inspect}, limit: is deprecated since it is now inferred")
|
161
161
|
end
|
162
162
|
|
163
163
|
# index: true means create an index on the foreign key
|
@@ -171,7 +171,7 @@ module DeclareSchema
|
|
171
171
|
index_options = {} # create an index
|
172
172
|
case index_value
|
173
173
|
when String, Symbol
|
174
|
-
|
174
|
+
DeclareSchema.deprecator.warn("[declare_schema] belongs_to #{name.inspect}, index: 'name' is deprecated; use index: { name: 'name' } instead (in #{self.name})")
|
175
175
|
index_options[:name] = index_value.to_s
|
176
176
|
when true
|
177
177
|
when nil
|
@@ -182,7 +182,7 @@ module DeclareSchema
|
|
182
182
|
end
|
183
183
|
|
184
184
|
if options.has_key?(:unique)
|
185
|
-
|
185
|
+
DeclareSchema.deprecator.warn("[declare_schema] belongs_to #{name.inspect}, unique: true|false is deprecated; use index: { unique: true|false } instead (in #{self.name})")
|
186
186
|
index_options[:unique] = options.delete(:unique)
|
187
187
|
end
|
188
188
|
|
@@ -358,24 +358,20 @@ module DeclareSchema
|
|
358
358
|
end
|
359
359
|
|
360
360
|
def _add_formatting_for_field(name, type)
|
361
|
-
if (type_class = DeclareSchema.to_class(type))
|
362
|
-
|
363
|
-
|
364
|
-
record.send("#{name}=", record.send(name)&.format)
|
365
|
-
end
|
361
|
+
if (type_class = DeclareSchema.to_class(type)) && "format".in?(type_class.instance_methods)
|
362
|
+
before_validation do |record|
|
363
|
+
record.send("#{name}=", record.send(name)&.format)
|
366
364
|
end
|
367
365
|
end
|
368
366
|
end
|
369
367
|
|
370
368
|
def _add_index_for_field(column_name, args, **options)
|
371
|
-
if (
|
372
|
-
index_opts =
|
373
|
-
|
374
|
-
unique: args.include?(:unique) || !!options.delete(:unique)
|
375
|
-
}
|
369
|
+
if (index_config = options.delete(:index))
|
370
|
+
index_opts = index_config.is_a?(Hash) ? index_config : {}
|
371
|
+
index_opts[:unique] ||= args.include?(:unique) || !!options.delete(:unique)
|
376
372
|
|
377
373
|
# support index: true declaration
|
378
|
-
index_opts[:name] =
|
374
|
+
index_opts[:name] = index_config unless index_config == true || index_config.is_a?(Hash)
|
379
375
|
index([column_name], **index_opts)
|
380
376
|
end
|
381
377
|
end
|
@@ -5,7 +5,7 @@ require_relative 'base'
|
|
5
5
|
module DeclareSchema
|
6
6
|
module SchemaChange
|
7
7
|
class PrimaryKeyChange < Base
|
8
|
-
def initialize(table_name, old_column_names, new_column_names)
|
8
|
+
def initialize(table_name, old_column_names, new_column_names) # rubocop:disable Lint/MissingSuper
|
9
9
|
@table_name = table_name
|
10
10
|
@old_column_names = old_column_names.presence
|
11
11
|
@new_column_names = new_column_names.presence
|
@@ -22,11 +22,24 @@ module DeclareSchema
|
|
22
22
|
private
|
23
23
|
|
24
24
|
def alter_primary_key(old_col_names, new_col_names)
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
if current_adapter == 'postgresql'
|
26
|
+
[].tap do |commands|
|
27
|
+
if old_col_names
|
28
|
+
drop_command = "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(@table_name)} DROP CONSTRAINT #{@table_name}_pkey;"
|
29
|
+
commands << "execute #{drop_command.inspect}"
|
30
|
+
end
|
31
|
+
if new_col_names
|
32
|
+
add_command = "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(@table_name)} ADD PRIMARY KEY (#{new_col_names.join(', ')});"
|
33
|
+
commands << "execute #{add_command.inspect}"
|
34
|
+
end
|
35
|
+
end.join("\n")
|
36
|
+
else
|
37
|
+
drop_command = "DROP PRIMARY KEY" if old_col_names
|
38
|
+
add_command = "ADD PRIMARY KEY (#{new_col_names.join(', ')})" if new_col_names
|
39
|
+
commands = [drop_command, add_command].compact.join(', ')
|
40
|
+
statement = "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(@table_name)} #{commands}"
|
41
|
+
"execute #{statement.inspect}"
|
42
|
+
end
|
30
43
|
end
|
31
44
|
end
|
32
45
|
end
|
data/lib/declare_schema.rb
CHANGED
@@ -24,14 +24,14 @@ module DeclareSchema
|
|
24
24
|
|
25
25
|
SEMVER_8 = Gem::Version.new('8.0.0').freeze
|
26
26
|
|
27
|
-
@default_charset
|
28
|
-
@default_collation
|
29
|
-
@default_text_limit
|
30
|
-
@default_string_limit
|
31
|
-
@default_null
|
32
|
-
@default_generate_foreign_keys
|
33
|
-
@default_generate_indexing
|
34
|
-
@db_migrate_command
|
27
|
+
@default_charset = "utf8mb4"
|
28
|
+
@default_collation = "utf8mb4_bin"
|
29
|
+
@default_text_limit = 0xffff_ffff
|
30
|
+
@default_string_limit = nil
|
31
|
+
@default_null = false
|
32
|
+
@default_generate_foreign_keys = true
|
33
|
+
@default_generate_indexing = true
|
34
|
+
@db_migrate_command = "bundle exec rails db:migrate"
|
35
35
|
@max_index_and_constraint_name_length = 64 # limit for MySQL
|
36
36
|
|
37
37
|
class << self
|
@@ -146,6 +146,18 @@ module DeclareSchema
|
|
146
146
|
length.is_a?(Integer) || length.nil? or raise ArgumentError, "max_index_and_constraint_name_length must be an Integer or nil (meaning unlimited)"
|
147
147
|
@max_index_and_constraint_name_length = length
|
148
148
|
end
|
149
|
+
|
150
|
+
def deprecator
|
151
|
+
@deprecator ||= ActiveSupport::Deprecation.new('3.0', 'DeclareSchema')
|
152
|
+
end
|
153
|
+
|
154
|
+
def current_adapter(model_class = ActiveRecord::Base)
|
155
|
+
if Rails::VERSION::MAJOR >= 7
|
156
|
+
model_class.connection_db_config.adapter
|
157
|
+
else
|
158
|
+
model_class.connection_config[:adapter]
|
159
|
+
end
|
160
|
+
end
|
149
161
|
end
|
150
162
|
end
|
151
163
|
|
@@ -107,32 +107,41 @@ module DeclareSchema
|
|
107
107
|
end
|
108
108
|
|
109
109
|
def load_migrations
|
110
|
-
if ActiveSupport.version >= Gem::Version.new('7.
|
110
|
+
if ActiveSupport.version >= Gem::Version.new('7.2.0')
|
111
|
+
ActiveRecord::MigrationContext.new(
|
112
|
+
ActiveRecord::Migrator.migrations_paths,
|
113
|
+
ActiveRecord::SchemaMigration.new(ActiveRecord::Base.connection_pool),
|
114
|
+
ActiveRecord::InternalMetadata.new(ActiveRecord::Base.connection_pool)
|
115
|
+
).migrations
|
116
|
+
elsif ActiveSupport.version >= Gem::Version.new('7.1.0')
|
111
117
|
ActiveRecord::MigrationContext.new(
|
112
118
|
ActiveRecord::Migrator.migrations_paths,
|
113
119
|
ActiveRecord::Base.connection.schema_migration,
|
114
120
|
ActiveRecord::Base.connection.internal_metadata
|
115
121
|
).migrations
|
116
|
-
|
122
|
+
else
|
117
123
|
ActiveRecord::MigrationContext.new(ActiveRecord::Migrator.migrations_paths, ActiveRecord::SchemaMigration).migrations
|
118
|
-
else # Rails 5.2 and earlier
|
119
|
-
ActiveRecord::MigrationContext.new(ActiveRecord::Migrator.migrations_paths).migrations
|
120
124
|
end
|
121
125
|
end
|
122
126
|
|
123
127
|
def load_pending_migrations
|
124
128
|
migrations = load_migrations
|
125
|
-
if ActiveSupport.version >= Gem::Version.new('7.
|
129
|
+
if ActiveSupport.version >= Gem::Version.new('7.2.0')
|
130
|
+
ActiveRecord::Migrator.new(
|
131
|
+
:up,
|
132
|
+
migrations,
|
133
|
+
ActiveRecord::SchemaMigration.new(ActiveRecord::Base.connection_pool),
|
134
|
+
ActiveRecord::InternalMetadata.new(ActiveRecord::Base.connection_pool)
|
135
|
+
).pending_migrations
|
136
|
+
elsif ActiveSupport.version >= Gem::Version.new('7.1.0')
|
126
137
|
ActiveRecord::Migrator.new(
|
127
138
|
:up,
|
128
139
|
migrations,
|
129
140
|
ActiveRecord::Base.connection.schema_migration,
|
130
141
|
ActiveRecord::Base.connection.internal_metadata
|
131
142
|
).pending_migrations
|
132
|
-
|
143
|
+
else
|
133
144
|
ActiveRecord::Migrator.new(:up, migrations, ActiveRecord::SchemaMigration).pending_migrations
|
134
|
-
else # Rails 5.2 and earlier
|
135
|
-
ActiveRecord::Migrator.new(:up, migrations).pending_migrations
|
136
145
|
end
|
137
146
|
end
|
138
147
|
|
@@ -303,7 +303,7 @@ module Generators
|
|
303
303
|
private
|
304
304
|
|
305
305
|
def up_and_down_migrations(migration_commands)
|
306
|
-
up = migration_commands.map(&:up
|
306
|
+
up = migration_commands.map(&:up).select(&:present?)
|
307
307
|
down = migration_commands.map(&:down).select(&:present?).reverse
|
308
308
|
|
309
309
|
[up * "\n", down * "\n"]
|
@@ -314,7 +314,7 @@ module Generators
|
|
314
314
|
if primary_key.blank? || disable_auto_increment
|
315
315
|
{ id: false }
|
316
316
|
elsif primary_key == "id"
|
317
|
-
{ id: :bigint }
|
317
|
+
{ id: current_adapter == 'postgresql' ? :bigserial : :bigint }
|
318
318
|
elsif primary_key.is_a?(Array)
|
319
319
|
{ primary_key: primary_key.map(&:to_sym) }
|
320
320
|
else
|
@@ -322,14 +322,18 @@ module Generators
|
|
322
322
|
end.merge(model._table_options)
|
323
323
|
end
|
324
324
|
|
325
|
+
def current_adapter(model_class = ActiveRecord::Base)
|
326
|
+
::DeclareSchema.current_adapter(model_class)
|
327
|
+
end
|
328
|
+
|
325
329
|
def table_options_for_model(model)
|
326
|
-
if
|
327
|
-
{}
|
328
|
-
else
|
330
|
+
if current_adapter == 'mysql2'
|
329
331
|
{
|
330
332
|
charset: model._table_options&.[](:charset) || ::DeclareSchema.default_charset,
|
331
333
|
collation: model._table_options&.[](:collation) || ::DeclareSchema.default_collation
|
332
334
|
}
|
335
|
+
else
|
336
|
+
{}
|
333
337
|
end
|
334
338
|
end
|
335
339
|
|
@@ -601,12 +605,15 @@ module Generators
|
|
601
605
|
end
|
602
606
|
end
|
603
607
|
|
604
|
-
SchemaDumper = ActiveRecord::ConnectionAdapters::SchemaDumper
|
605
|
-
|
606
|
-
|
607
608
|
def add_table_back(table)
|
608
609
|
dumped_schema_stream = StringIO.new
|
609
|
-
|
610
|
+
dumper = case current_adapter
|
611
|
+
when 'postgresql'
|
612
|
+
ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaDumper.create(ActiveRecord::Base.connection, {})
|
613
|
+
else
|
614
|
+
ActiveRecord::ConnectionAdapters::SchemaDumper.create(ActiveRecord::Base.connection, {})
|
615
|
+
end
|
616
|
+
dumper.send(:table, table, dumped_schema_stream)
|
610
617
|
|
611
618
|
dumped_schema = dumped_schema_stream.string.strip.gsub!("\n ", "\n")
|
612
619
|
if connection.class.name.match?(/mysql/i)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
create_table :categories, id: :bigint, options: "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin" do |t|
|
2
|
+
t.string :name, limit: 250, null: true, charset: "utf8mb4", collation: "utf8mb4_bin"
|
3
|
+
end
|
4
|
+
create_table :advertisers, id: :bigint, options: "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin" do |t|
|
5
|
+
t.string :name, limit: 250, null: true, charset: "utf8mb4", collation: "utf8mb4_bin"
|
6
|
+
t.integer :category_id, limit: 8, null: false
|
7
|
+
end
|
8
|
+
create_table :affiliates, id: :bigint, options: "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin" do |t|
|
9
|
+
t.string :name, limit: 250, null: true, charset: "utf8mb4", collation: "utf8mb4_bin"
|
10
|
+
t.integer :category_id, limit: 8, null: false
|
11
|
+
end
|
12
|
+
add_index :advertisers, [:category_id], name: :index_advertisers_on_category_id
|
13
|
+
add_index :affiliates, [:category_id], name: :index_affiliates_on_category_id
|
14
|
+
add_foreign_key :advertisers, :categories, column: :category_id, name: :index_advertisers_on_category_id
|
15
|
+
add_foreign_key :affiliates, :categories, column: :category_id, name: :index_affiliates_on_category_id
|
@@ -0,0 +1,15 @@
|
|
1
|
+
create_table :affiliates, id: :bigint, options: "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin" do |t|
|
2
|
+
t.string :name, limit: 250, null: true, charset: "utf8mb4", collation: "utf8mb4_bin"
|
3
|
+
t.integer :category_id, limit: 8, null: false
|
4
|
+
end
|
5
|
+
create_table :advertisers, id: :bigint, options: "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin" do |t|
|
6
|
+
t.string :name, limit: 250, null: true, charset: "utf8mb4", collation: "utf8mb4_bin"
|
7
|
+
t.integer :category_id, limit: 8, null: false
|
8
|
+
end
|
9
|
+
create_table :categories, id: :bigint, options: "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin" do |t|
|
10
|
+
t.string :name, limit: 250, null: true, charset: "utf8mb4", collation: "utf8mb4_bin"
|
11
|
+
end
|
12
|
+
add_index :affiliates, [:category_id], name: :index_affiliates_on_category_id
|
13
|
+
add_index :advertisers, [:category_id], name: :index_advertisers_on_category_id
|
14
|
+
add_foreign_key :affiliates, :categories, column: :category_id, name: :index_affiliates_on_category_id
|
15
|
+
add_foreign_key :advertisers, :categories, column: :category_id, name: :index_advertisers_on_category_id
|
@@ -0,0 +1,15 @@
|
|
1
|
+
create_table :categories, id: :bigserial do |t|
|
2
|
+
t.string :name, limit: 250, null: true
|
3
|
+
end
|
4
|
+
create_table :advertisers, id: :bigserial do |t|
|
5
|
+
t.string :name, limit: 250, null: true
|
6
|
+
t.integer :category_id, limit: 8, null: false
|
7
|
+
end
|
8
|
+
create_table :affiliates, id: :bigserial do |t|
|
9
|
+
t.string :name, limit: 250, null: true
|
10
|
+
t.integer :category_id, limit: 8, null: false
|
11
|
+
end
|
12
|
+
add_index :advertisers, [:category_id], name: :index_advertisers_on_category_id
|
13
|
+
add_index :affiliates, [:category_id], name: :index_affiliates_on_category_id
|
14
|
+
add_foreign_key :advertisers, :categories, column: :category_id, name: :index_advertisers_on_category_id
|
15
|
+
add_foreign_key :affiliates, :categories, column: :category_id, name: :index_affiliates_on_category_id
|