declare_schema 0.4.2 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +14 -0
- data/.github/workflows/declare_schema_build.yml +44 -0
- data/.gitignore +1 -0
- data/CHANGELOG.md +19 -1
- data/Gemfile +1 -0
- data/Gemfile.lock +3 -1
- data/README.md +66 -0
- data/gemfiles/rails_4.gemfile +1 -0
- data/gemfiles/rails_5.gemfile +1 -0
- data/gemfiles/rails_6.gemfile +1 -0
- data/lib/declare_schema.rb +1 -0
- data/lib/declare_schema/extensions/active_record/fields_declaration.rb +2 -1
- data/lib/declare_schema/model.rb +5 -1
- data/lib/declare_schema/model/field_spec.rb +90 -30
- data/lib/declare_schema/model/table_options_definition.rb +83 -0
- data/lib/declare_schema/version.rb +1 -1
- data/lib/generators/declare_schema/migration/migrator.rb +97 -42
- data/spec/lib/declare_schema/field_spec_spec.rb +69 -0
- data/spec/lib/declare_schema/generator_spec.rb +22 -9
- data/spec/lib/declare_schema/migration_generator_spec.rb +25 -0
- data/spec/lib/declare_schema/model/table_options_definition_spec.rb +84 -0
- data/spec/lib/generators/declare_schema/migration/migrator_spec.rb +28 -0
- metadata +10 -7
- data/.dependabot/config.yml +0 -10
- data/.travis.yml +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0c13793a5b263a2f1134ee19168a42ceb57c472cf8fb656b8e86fcdd9500b79
|
4
|
+
data.tar.gz: ee92620b0a091538958957205ba940d97cd9f67bc606751cb52a8ae44370e541
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3bc0e18dd1b3a998640cc2e983c86182e19b855d8ce42b3f7233b3471a7849d0dc28fa2653d110b42d0544c513267f8066386496b1469085bb507541c068b87a
|
7
|
+
data.tar.gz: 34b9e31862291b85becf7eeb908c4ef1da5b29178b1ab387b35c6678680299cd921fa65b49e0ba89604518a66515092d3e7d6c38678267301c9399b4572d372e
|
@@ -0,0 +1,14 @@
|
|
1
|
+
version: 2
|
2
|
+
updates:
|
3
|
+
- package-ecosystem: bundler
|
4
|
+
directory: "/"
|
5
|
+
schedule:
|
6
|
+
interval: weekly
|
7
|
+
day: friday
|
8
|
+
time: "22:00"
|
9
|
+
timezone: PST8PDT
|
10
|
+
open-pull-requests-limit: 99
|
11
|
+
versioning-strategy: lockfile-only
|
12
|
+
commit-message:
|
13
|
+
prefix: No-Jira
|
14
|
+
include: scope
|
@@ -0,0 +1,44 @@
|
|
1
|
+
---
|
2
|
+
on: [push]
|
3
|
+
|
4
|
+
name: DeclareSchema Build
|
5
|
+
|
6
|
+
jobs:
|
7
|
+
build:
|
8
|
+
name: DeclareSchema Build
|
9
|
+
runs-on: ubuntu-latest
|
10
|
+
strategy:
|
11
|
+
matrix:
|
12
|
+
ruby: [ 2.4.5, 2.5.8, 2.6.5, 2.7.1 ]
|
13
|
+
gemfile: [ gemfiles/rails_4.gemfile, gemfiles/rails_5.gemfile, gemfiles/rails_6.gemfile ]
|
14
|
+
exclude:
|
15
|
+
- { gemfile: gemfiles/rails_4.gemfile, ruby: 2.7.1 }
|
16
|
+
- { gemfile: gemfiles/rails_5.gemfile, ruby: 2.4.5 }
|
17
|
+
- { gemfile: gemfiles/rails_6.gemfile, ruby: 2.4.5 }
|
18
|
+
env:
|
19
|
+
BUNDLE_GEMFILE: "${{ matrix.gemfile }}"
|
20
|
+
steps:
|
21
|
+
- name: Checkout Branch
|
22
|
+
id: checkout_branch
|
23
|
+
uses: actions/checkout@v2
|
24
|
+
- name: Setup Ruby
|
25
|
+
id: setup_ruby
|
26
|
+
uses: ruby/setup-ruby@v1
|
27
|
+
with:
|
28
|
+
bundler: 1.17.3
|
29
|
+
ruby-version: ${{matrix.ruby}}
|
30
|
+
- name: Remove Bundler 2
|
31
|
+
id: remove_bundler_2
|
32
|
+
if: ${{ matrix.ruby >= '2.6.5' }}
|
33
|
+
run: |
|
34
|
+
rm -f /opt/hostedtoolcache/Ruby/2.*/x64/lib/ruby/gems/2.*/specifications/default/bundler-2.*.gemspec
|
35
|
+
gem install bundler:1.17.3 --force --default
|
36
|
+
gem install bundler -v 1.17.3
|
37
|
+
- name: Appraisals
|
38
|
+
id: appraisals
|
39
|
+
run: |
|
40
|
+
bundle install --jobs=3 --retry=3 --path=${BUNDLE_PATH:-vendor/bundle} --gemfile=${{ matrix.gemfile }}
|
41
|
+
git config --global user.email "dummy@example.com"
|
42
|
+
git config --global user.name "dummy"
|
43
|
+
bundle exec rake test:prepare_testapp[force]
|
44
|
+
bundle exec rake test:all < test_responses.txt
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,22 @@ 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
|
+
## [0.6.0] - 2020-12-23
|
8
|
+
### Added
|
9
|
+
- Fields may now be declared with `:bigint` type which is identical to `:integer, limit 8`
|
10
|
+
- FieldSpec#initialize interface now includes `position` keyword argument and `**options` hash.
|
11
|
+
|
12
|
+
### Fixed
|
13
|
+
- Fixed cycle in which FieldSpec#initialize was calling `model.field_specs`
|
14
|
+
|
15
|
+
### Changed
|
16
|
+
- Changed ci support from Travis to Github Workflow
|
17
|
+
|
18
|
+
## [0.5.0] - 2020-12-21
|
19
|
+
### Added
|
20
|
+
- Added support for configuring the character set and collation for MySQL databases
|
21
|
+
at the global, table, and field level
|
22
|
+
|
7
23
|
## [0.4.2] - 2020-12-05
|
8
24
|
### Fixed
|
9
25
|
- Generalize the fix below to sqlite || Rails 4.
|
@@ -15,7 +31,7 @@ Note: this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0
|
|
15
31
|
## [0.4.0] - 2020-11-20
|
16
32
|
### Added
|
17
33
|
- Fields may be declared with `serialize: true` (any value with a valid `.to_yaml` stored as YAML),
|
18
|
-
or `serialize: <serializeable-class>`, where `<serializeable-class>`
|
34
|
+
or `serialize: <serializeable-class>`, where `<serializeable-class>`
|
19
35
|
may be `Array` (`Array` stored as YAML) or `Hash` (`Hash` stored as YAML) or `JSON` (any value with a valid `.to_json`, stored as JSON)
|
20
36
|
or any custom serializable class.
|
21
37
|
This invokes `ActiveSupport`'s `serialize` macro for that field, passing the serializable class, if given.
|
@@ -68,6 +84,8 @@ using the appropriate Rails configuration attributes.
|
|
68
84
|
### Added
|
69
85
|
- Initial version from https://github.com/Invoca/hobo_fields v4.1.0.
|
70
86
|
|
87
|
+
[0.6.0]: https://github.com/Invoca/declare_schema/compare/v0.5.0...v0.6.0
|
88
|
+
[0.5.0]: https://github.com/Invoca/declare_schema/compare/v0.4.2...v0.5.0
|
71
89
|
[0.4.2]: https://github.com/Invoca/declare_schema/compare/v0.4.1...v0.4.2
|
72
90
|
[0.4.1]: https://github.com/Invoca/declare_schema/compare/v0.4.0...v0.4.1
|
73
91
|
[0.4.0]: https://github.com/Invoca/declare_schema/compare/v0.3.1...v0.4.0
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
declare_schema (0.
|
4
|
+
declare_schema (0.6.0)
|
5
5
|
rails (>= 4.2)
|
6
6
|
|
7
7
|
GEM
|
@@ -85,6 +85,7 @@ GEM
|
|
85
85
|
mini_portile2 (2.4.0)
|
86
86
|
minitest (5.14.2)
|
87
87
|
msgpack (1.3.3)
|
88
|
+
mysql2 (0.5.3)
|
88
89
|
nio4r (2.5.4)
|
89
90
|
nokogiri (1.10.10)
|
90
91
|
mini_portile2 (~> 2.4.0)
|
@@ -187,6 +188,7 @@ DEPENDENCIES
|
|
187
188
|
climate_control (~> 0.2)
|
188
189
|
declare_schema!
|
189
190
|
listen
|
191
|
+
mysql2
|
190
192
|
pry
|
191
193
|
pry-byebug
|
192
194
|
rails (~> 5.2, >= 5.2.4.3)
|
data/README.md
CHANGED
@@ -70,6 +70,72 @@ DeclareSchema::Migration::Migrator.before_generating_migration do
|
|
70
70
|
end
|
71
71
|
```
|
72
72
|
|
73
|
+
## Declaring Character Set and Collation
|
74
|
+
_Note: This feature currently only works for MySQL database configurations._
|
75
|
+
|
76
|
+
MySQL originally supported UTF-8 in the range of 1-3 bytes (`mb3` or "multi-byte 3")
|
77
|
+
which covered the full set of Unicode code points at the time: U+0000 - U+FFFF.
|
78
|
+
But later, Unicode was extended beyond U+FFFF to make room for emojis, and with that
|
79
|
+
UTF-8 require 1-4 bytes (`mb4` or "multi-byte 4"). With this addition, there has
|
80
|
+
come a need to dynamically define the character set and collation for individual
|
81
|
+
tables and columns in the database. With `declare_schema` this can be configured
|
82
|
+
at three separate levels
|
83
|
+
|
84
|
+
### Global Configuration
|
85
|
+
The character set and collation for all tables and fields can be set at the global level
|
86
|
+
using the `Generators::DeclareSchema::Migrator.default_charset=` and
|
87
|
+
`Generators::DeclareSchema::Migrator.default_collation=` configuration methods.
|
88
|
+
|
89
|
+
For example, adding the following to your `config/initializers` directory will
|
90
|
+
turn all tables into `utf8mb4` supporting tables:
|
91
|
+
|
92
|
+
**declare_schema.rb**
|
93
|
+
```ruby
|
94
|
+
# frozen_string_literal: true
|
95
|
+
|
96
|
+
Generators::DeclareSchema::Migration::Migrator.default_charset = "utf8mb4"
|
97
|
+
Generators::DeclareSchema::Migration::Migrator.default_collation = "utf8mb4_general"
|
98
|
+
```
|
99
|
+
|
100
|
+
### Table Configuration
|
101
|
+
In order to configure a table's default character set and collation, the `charset` and
|
102
|
+
`collation` arguments can be added to the `fields` block.
|
103
|
+
|
104
|
+
For example, if you have a comments model that needs `utf8mb4` support, it would look
|
105
|
+
like the following:
|
106
|
+
|
107
|
+
**app/models/comment.rb**
|
108
|
+
```ruby
|
109
|
+
# frozen_string_literal: true
|
110
|
+
|
111
|
+
class Comment < ActiveRecord::Base
|
112
|
+
fields charset: "utf8mb4", collation: "utf8mb4_general" do
|
113
|
+
subject :string, limit: 255
|
114
|
+
content :text, limit: 0xffff_ffff
|
115
|
+
end
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
### Field Configuration
|
120
|
+
If you're looking to only change the character set and collation for a single field
|
121
|
+
in the table, simply set the `charset` and `collation` configuration options on the
|
122
|
+
field definition itself.
|
123
|
+
|
124
|
+
For example, if you only want to support `utf8mb4` for the content of a comment, it would
|
125
|
+
look like the following:
|
126
|
+
|
127
|
+
**app/models/comment.rb**
|
128
|
+
```ruby
|
129
|
+
# frozen_string_literal: true
|
130
|
+
|
131
|
+
class Comment < ActiveRecord::Base
|
132
|
+
fields do
|
133
|
+
subject :string, limit: 255
|
134
|
+
context :text, limit: 0xffff_ffff, charset: "utf8mb4", collation: "utf8mb4_general"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
73
139
|
## Installing
|
74
140
|
|
75
141
|
Install the `DeclareSchema` gem directly:
|
data/gemfiles/rails_4.gemfile
CHANGED
data/gemfiles/rails_5.gemfile
CHANGED
data/gemfiles/rails_6.gemfile
CHANGED
data/lib/declare_schema.rb
CHANGED
@@ -41,5 +41,6 @@ require 'declare_schema/model'
|
|
41
41
|
require 'declare_schema/model/field_spec'
|
42
42
|
require 'declare_schema/model/index_definition'
|
43
43
|
require 'declare_schema/model/foreign_key_definition'
|
44
|
+
require 'declare_schema/model/table_options_definition'
|
44
45
|
|
45
46
|
require 'declare_schema/railtie' if defined?(Rails)
|
@@ -6,12 +6,13 @@ require 'declare_schema/field_declaration_dsl'
|
|
6
6
|
|
7
7
|
module DeclareSchema
|
8
8
|
module FieldsDsl
|
9
|
-
def fields(&block)
|
9
|
+
def fields(table_options = {}, &block)
|
10
10
|
# Any model that calls 'fields' gets DeclareSchema::Model behavior
|
11
11
|
DeclareSchema::Model.mix_in(self)
|
12
12
|
|
13
13
|
# @include_in_migration = false #||= options.fetch(:include_in_migration, true); options.delete(:include_in_migration)
|
14
14
|
@include_in_migration = true
|
15
|
+
@table_options = table_options
|
15
16
|
|
16
17
|
if block
|
17
18
|
dsl = DeclareSchema::FieldDeclarationDsl.new(self, null: false)
|
data/lib/declare_schema/model.rb
CHANGED
@@ -30,6 +30,10 @@ module DeclareSchema
|
|
30
30
|
inheriting_cattr_reader ignore_indexes: []
|
31
31
|
inheriting_cattr_reader constraint_specs: []
|
32
32
|
|
33
|
+
# table_options holds optional configuration for the create_table statement
|
34
|
+
# supported options include :charset and :collation
|
35
|
+
inheriting_cattr_reader table_options: HashWithIndifferentAccess.new
|
36
|
+
|
33
37
|
# eval avoids the ruby 1.9.2 "super from singleton method ..." error
|
34
38
|
|
35
39
|
eval %(
|
@@ -84,7 +88,7 @@ module DeclareSchema
|
|
84
88
|
add_formatting_for_field(name, type)
|
85
89
|
add_validations_for_field(name, type, args, options)
|
86
90
|
add_index_for_field(name, args, options)
|
87
|
-
field_specs[name] = ::DeclareSchema::Model::FieldSpec.new(self, name, type, options)
|
91
|
+
field_specs[name] = ::DeclareSchema::Model::FieldSpec.new(self, name, type, position: field_specs.size, **options)
|
88
92
|
attr_order << name unless attr_order.include?(name)
|
89
93
|
end
|
90
94
|
|
@@ -33,7 +33,8 @@ module DeclareSchema
|
|
33
33
|
|
34
34
|
attr_reader :model, :name, :type, :position, :options
|
35
35
|
|
36
|
-
def initialize(model, name, type,
|
36
|
+
def initialize(model, name, type, position: 0, **options)
|
37
|
+
# TODO: TECH-5116
|
37
38
|
# Invoca change - searching for the primary key was causing an additional database read on every model load. Assume
|
38
39
|
# "id" which works for invoca.
|
39
40
|
# raise ArgumentError, "you cannot provide a field spec for the primary key" if name == model.primary_key
|
@@ -43,9 +44,8 @@ module DeclareSchema
|
|
43
44
|
@name = name.to_sym
|
44
45
|
type.is_a?(Symbol) or raise ArgumentError, "type must be a Symbol; got #{type.inspect}"
|
45
46
|
@type = type
|
46
|
-
|
47
|
+
@position = position
|
47
48
|
@options = options
|
48
|
-
|
49
49
|
case type
|
50
50
|
when :text
|
51
51
|
@options[:default] and raise "default may not be given for :text field #{model}##{@name}"
|
@@ -54,8 +54,15 @@ module DeclareSchema
|
|
54
54
|
end
|
55
55
|
when :string
|
56
56
|
@options[:limit] or raise "limit must be given for :string field #{model}##{@name}: #{@options.inspect}; do you want `limit: 255`?"
|
57
|
+
when :bigint
|
58
|
+
@type = :integer
|
59
|
+
@options = options.merge(limit: 8)
|
60
|
+
end
|
61
|
+
|
62
|
+
unless type.in?([:text, :string])
|
63
|
+
@options[:collation] and raise "collation may only given for :string and :text fields"
|
64
|
+
@options[:charset] and raise "charset may only given for :string and :text fields"
|
57
65
|
end
|
58
|
-
@position = position_option || model.field_specs.length
|
59
66
|
end
|
60
67
|
|
61
68
|
TYPE_SYNONYMS = { timestamp: :datetime }.freeze
|
@@ -102,6 +109,18 @@ module DeclareSchema
|
|
102
109
|
@options[:default]
|
103
110
|
end
|
104
111
|
|
112
|
+
def collation
|
113
|
+
if ActiveRecord::Base.connection.class.name.match?(/mysql/i)
|
114
|
+
(@options[:collation] || model.table_options[:collation] || Generators::DeclareSchema::Migration::Migrator.default_collation).to_s
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def charset
|
119
|
+
if ActiveRecord::Base.connection.class.name.match?(/mysql/i)
|
120
|
+
(@options[:charset] || model.table_options[:charset] || Generators::DeclareSchema::Migration::Migrator.default_charset).to_s
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
105
124
|
def same_type?(col_spec)
|
106
125
|
type = sql_type
|
107
126
|
normalized_type = TYPE_SYNONYMS[type] || type
|
@@ -109,36 +128,77 @@ module DeclareSchema
|
|
109
128
|
normalized_type == normalized_col_spec_type
|
110
129
|
end
|
111
130
|
|
112
|
-
def different_to?(col_spec)
|
113
|
-
!
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
131
|
+
def different_to?(table_name, col_spec)
|
132
|
+
!same_as(table_name, col_spec)
|
133
|
+
end
|
134
|
+
|
135
|
+
def same_as(table_name, col_spec)
|
136
|
+
same_type?(col_spec) &&
|
137
|
+
same_attributes?(col_spec) &&
|
138
|
+
(!type.in?([:text, :string]) || same_charset_and_collation?(table_name, col_spec))
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def same_attributes?(col_spec)
|
144
|
+
native_type = native_types[type]
|
145
|
+
check_attributes = [:null, :default]
|
146
|
+
check_attributes += [:precision, :scale] if sql_type == :decimal && !col_spec.is_a?(SQLITE_COLUMN_CLASS) # remove when rails fixes https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/2872
|
147
|
+
check_attributes -= [:default] if sql_type == :text && col_spec.class.name =~ /mysql/i
|
148
|
+
check_attributes << :limit if sql_type.in?([:string, :binary, :varbinary, :integer, :enum]) ||
|
149
|
+
(sql_type == :text && self.class.mysql_text_limits?)
|
150
|
+
check_attributes.all? do |k|
|
151
|
+
if k == :default
|
152
|
+
case Rails::VERSION::MAJOR
|
153
|
+
when 4
|
154
|
+
col_spec.type_cast_from_database(col_spec.default) == col_spec.type_cast_from_database(default)
|
155
|
+
else
|
156
|
+
cast_type = ActiveRecord::Base.connection.lookup_cast_type_from_column(col_spec) or raise "cast_type not found for #{col_spec.inspect}"
|
157
|
+
cast_type.deserialize(col_spec.default) == cast_type.deserialize(default)
|
137
158
|
end
|
159
|
+
else
|
160
|
+
col_value = col_spec.send(k)
|
161
|
+
if col_value.nil? && native_type
|
162
|
+
col_value = native_type[k]
|
163
|
+
end
|
164
|
+
col_value == send(k)
|
138
165
|
end
|
166
|
+
end
|
139
167
|
end
|
140
168
|
|
141
|
-
|
169
|
+
def same_charset_and_collation?(table_name, col_spec)
|
170
|
+
current_collation_and_charset = collation_and_charset_for_column(table_name, col_spec)
|
171
|
+
|
172
|
+
collation == current_collation_and_charset[:collation] &&
|
173
|
+
charset == current_collation_and_charset[:charset]
|
174
|
+
end
|
175
|
+
|
176
|
+
def collation_and_charset_for_column(table_name, col_spec)
|
177
|
+
column_name = col_spec.name
|
178
|
+
connection = ActiveRecord::Base.connection
|
179
|
+
|
180
|
+
if connection.class.name.match?(/mysql/i)
|
181
|
+
database_name = connection.current_database
|
182
|
+
|
183
|
+
defaults = connection.select_one(<<~EOS)
|
184
|
+
SELECT C.character_set_name, C.collation_name
|
185
|
+
FROM information_schema.`COLUMNS` C
|
186
|
+
WHERE C.table_schema = '#{connection.quote_string(database_name)}' AND
|
187
|
+
C.table_name = '#{connection.quote_string(table_name)}' AND
|
188
|
+
C.column_name = '#{connection.quote_string(column_name)}';
|
189
|
+
EOS
|
190
|
+
|
191
|
+
defaults["character_set_name"] or raise "character_set_name missing from #{defaults.inspect}"
|
192
|
+
defaults["collation_name"] or raise "collation_name missing from #{defaults.inspect}"
|
193
|
+
|
194
|
+
{
|
195
|
+
charset: defaults["character_set_name"],
|
196
|
+
collation: defaults["collation_name"]
|
197
|
+
}
|
198
|
+
else
|
199
|
+
{}
|
200
|
+
end
|
201
|
+
end
|
142
202
|
|
143
203
|
def native_type?(type)
|
144
204
|
type.to_sym != :primary_key && native_types.has_key?(type)
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeclareSchema
|
4
|
+
module Model
|
5
|
+
class TableOptionsDefinition
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
TABLE_OPTIONS_TO_SQL_MAPPINGS = {
|
9
|
+
charset: 'CHARACTER SET',
|
10
|
+
collation: 'COLLATE'
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def for_model(model, old_table_name = nil)
|
15
|
+
table_name = old_table_name || model.table_name
|
16
|
+
table_options = if model.connection.class.name.match?(/mysql/i)
|
17
|
+
mysql_table_options(model.connection, table_name)
|
18
|
+
else
|
19
|
+
{}
|
20
|
+
end
|
21
|
+
|
22
|
+
new(table_name, table_options)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def mysql_table_options(connection, table_name)
|
28
|
+
database = connection.current_database
|
29
|
+
defaults = connection.select_one(<<~EOS)
|
30
|
+
SELECT CCSA.character_set_name, CCSA.collation_name
|
31
|
+
FROM information_schema.`TABLES` T, information_schema.`COLLATION_CHARACTER_SET_APPLICABILITY` CCSA
|
32
|
+
WHERE CCSA.collation_name = T.table_collation AND
|
33
|
+
T.table_schema = '#{connection.quote_string(database)}' AND
|
34
|
+
T.table_name = '#{connection.quote_string(table_name)}';
|
35
|
+
EOS
|
36
|
+
|
37
|
+
defaults["character_set_name"] or raise "character_set_name missing from #{defaults.inspect}"
|
38
|
+
defaults["collation_name"] or raise "collation_name missing from #{defaults.inspect}"
|
39
|
+
|
40
|
+
{
|
41
|
+
charset: defaults["character_set_name"],
|
42
|
+
collation: defaults["collation_name"]
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
attr_reader :table_name, :table_options
|
48
|
+
|
49
|
+
def initialize(table_name, table_options = {})
|
50
|
+
@table_name = table_name
|
51
|
+
@table_options = table_options
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_key
|
55
|
+
@key ||= [table_name, table_options].map(&:to_s)
|
56
|
+
end
|
57
|
+
|
58
|
+
def settings
|
59
|
+
@settings ||= table_options.map { |name, value| "#{TABLE_OPTIONS_TO_SQL_MAPPINGS[name]} #{value}" if value }.compact.join(" ")
|
60
|
+
end
|
61
|
+
|
62
|
+
def hash
|
63
|
+
to_key.hash
|
64
|
+
end
|
65
|
+
|
66
|
+
def <=>(rhs)
|
67
|
+
to_key <=> rhs.to_key
|
68
|
+
end
|
69
|
+
|
70
|
+
def equivalent?(rhs)
|
71
|
+
settings == rhs.settings
|
72
|
+
end
|
73
|
+
|
74
|
+
alias eql? ==
|
75
|
+
alias to_s settings
|
76
|
+
|
77
|
+
def alter_table_statement
|
78
|
+
statement = "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(table_name)} #{to_s};"
|
79
|
+
"execute #{statement.inspect}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -23,6 +23,10 @@ module Generators
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
+
def table_options
|
27
|
+
{}
|
28
|
+
end
|
29
|
+
|
26
30
|
def table_name
|
27
31
|
join_table
|
28
32
|
end
|
@@ -33,11 +37,9 @@ module Generators
|
|
33
37
|
|
34
38
|
def field_specs
|
35
39
|
i = 0
|
36
|
-
foreign_keys.
|
37
|
-
|
38
|
-
h[v] = ::DeclareSchema::Model::FieldSpec.new(self, v, :integer, position: i, null: false)
|
40
|
+
foreign_keys.each_with_object({}) do |v, result|
|
41
|
+
result[v] = ::DeclareSchema::Model::FieldSpec.new(self, v, :integer, position: i, null: false)
|
39
42
|
i += 1
|
40
|
-
h
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
@@ -69,13 +71,19 @@ module Generators
|
|
69
71
|
class Migrator
|
70
72
|
class Error < RuntimeError; end
|
71
73
|
|
72
|
-
|
73
|
-
|
74
|
+
DEFAULT_CHARSET = :utf8mb4
|
75
|
+
DEFAULT_COLLATION = :utf8mb4_general
|
76
|
+
|
77
|
+
@ignore_models = []
|
78
|
+
@ignore_tables = []
|
74
79
|
@before_generating_migration_callback = nil
|
75
|
-
@active_record_class
|
80
|
+
@active_record_class = ActiveRecord::Base
|
81
|
+
@default_charset = DEFAULT_CHARSET
|
82
|
+
@default_collation = DEFAULT_COLLATION
|
76
83
|
|
77
84
|
class << self
|
78
|
-
attr_accessor :ignore_models, :ignore_tables, :disable_indexing, :disable_constraints,
|
85
|
+
attr_accessor :ignore_models, :ignore_tables, :disable_indexing, :disable_constraints,
|
86
|
+
:active_record_class, :default_charset, :default_collation
|
79
87
|
attr_reader :before_generating_migration_callback
|
80
88
|
|
81
89
|
def active_record_class
|
@@ -292,52 +300,76 @@ module Generators
|
|
292
300
|
"drop_table :#{t}"
|
293
301
|
end * "\n"
|
294
302
|
|
295
|
-
changes
|
296
|
-
undo_changes
|
297
|
-
index_changes
|
298
|
-
undo_index_changes
|
299
|
-
fk_changes
|
300
|
-
undo_fk_changes
|
303
|
+
changes = []
|
304
|
+
undo_changes = []
|
305
|
+
index_changes = []
|
306
|
+
undo_index_changes = []
|
307
|
+
fk_changes = []
|
308
|
+
undo_fk_changes = []
|
309
|
+
table_options_changes = []
|
310
|
+
undo_table_options_changes = []
|
311
|
+
|
301
312
|
to_change.each do |t|
|
302
313
|
model = models_by_table_name[t]
|
303
314
|
table = to_rename.key(t) || model.table_name
|
304
315
|
if table.in?(db_tables)
|
305
|
-
change, undo, index_change, undo_index, fk_change, undo_fk = change_table(model, table)
|
316
|
+
change, undo, index_change, undo_index, fk_change, undo_fk, table_options_change, undo_table_options_change = change_table(model, table)
|
306
317
|
changes << change
|
307
318
|
undo_changes << undo
|
308
319
|
index_changes << index_change
|
309
320
|
undo_index_changes << undo_index
|
310
321
|
fk_changes << fk_change
|
311
322
|
undo_fk_changes << undo_fk
|
323
|
+
table_options_changes << table_options_change
|
324
|
+
undo_table_options_changes << undo_table_options_change
|
312
325
|
end
|
313
326
|
end
|
314
327
|
|
315
|
-
up = [renames, drops, creates, changes, index_changes, fk_changes].flatten.reject(&:blank?) * "\n\n"
|
316
|
-
down = [undo_changes, undo_renames, undo_drops, undo_creates, undo_index_changes, undo_fk_changes].flatten.reject(&:blank?) * "\n\n"
|
328
|
+
up = [renames, drops, creates, changes, index_changes, fk_changes, table_options_changes].flatten.reject(&:blank?) * "\n\n"
|
329
|
+
down = [undo_changes, undo_renames, undo_drops, undo_creates, undo_index_changes, undo_fk_changes, undo_table_options_changes].flatten.reject(&:blank?) * "\n\n"
|
317
330
|
|
318
331
|
[up, down]
|
319
332
|
end
|
320
333
|
|
321
334
|
def create_table(model)
|
322
|
-
longest_field_name
|
323
|
-
disable_auto_increment
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
335
|
+
longest_field_name = model.field_specs.values.map { |f| f.sql_type.to_s.length }.max
|
336
|
+
disable_auto_increment = model.respond_to?(:disable_auto_increment) && model.disable_auto_increment
|
337
|
+
table_options_definition = ::DeclareSchema::Model::TableOptionsDefinition.new(model.table_name, table_options_for_model(model))
|
338
|
+
field_definitions = [
|
339
|
+
disable_auto_increment ? "t.integer :id, limit: 8, auto_increment: false, primary_key: true" : nil,
|
340
|
+
*(model.field_specs.values.sort_by(&:position).map { |f| create_field(f, longest_field_name) })
|
341
|
+
].compact
|
342
|
+
|
343
|
+
<<~EOS.strip
|
344
|
+
create_table :#{model.table_name}, #{create_table_options(model, disable_auto_increment)} do |t|
|
345
|
+
#{field_definitions.join("\n")}
|
331
346
|
end
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
347
|
+
|
348
|
+
#{table_options_definition.alter_table_statement unless ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/)}
|
349
|
+
#{create_indexes(model).join("\n") unless Migrator.disable_indexing}
|
350
|
+
#{create_constraints(model).join("\n") unless Migrator.disable_indexing}
|
351
|
+
EOS
|
352
|
+
end
|
353
|
+
|
354
|
+
def create_table_options(model, disable_auto_increment)
|
355
|
+
if model.primary_key.blank? || disable_auto_increment
|
356
|
+
"id: false"
|
357
|
+
elsif model.primary_key == "id"
|
358
|
+
"id: :bigint"
|
359
|
+
else
|
360
|
+
"primary_key: :#{model.primary_key}"
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
def table_options_for_model(model)
|
365
|
+
if ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/)
|
366
|
+
{}
|
367
|
+
else
|
368
|
+
{
|
369
|
+
charset: model.table_options[:charset] || Migrator.default_charset,
|
370
|
+
collation: model.table_options[:collation] || Migrator.default_collation
|
371
|
+
}
|
372
|
+
end
|
341
373
|
end
|
342
374
|
|
343
375
|
def create_indexes(model)
|
@@ -351,7 +383,7 @@ module Generators
|
|
351
383
|
def create_field(field_spec, field_name_width)
|
352
384
|
options = fk_field_options(field_spec.model, field_spec.name).merge(field_spec.sql_options)
|
353
385
|
args = [field_spec.name.inspect] + format_options(options, field_spec.sql_type)
|
354
|
-
format("
|
386
|
+
format("t.%-*s %s", field_name_width, field_spec.sql_type, args.join(', '))
|
355
387
|
end
|
356
388
|
|
357
389
|
def change_table(model, current_table_name)
|
@@ -413,15 +445,17 @@ module Generators
|
|
413
445
|
col_name = old_names[c] || c
|
414
446
|
col = db_columns[col_name]
|
415
447
|
spec = model.field_specs[c]
|
416
|
-
if spec.different_to?(col) # TODO: TECH-4814 DRY this up to a diff function that returns the differences. It's different if it has differences. -Colin
|
448
|
+
if spec.different_to?(current_table_name, col) # TODO: TECH-4814 DRY this up to a diff function that returns the differences. It's different if it has differences. -Colin
|
417
449
|
change_spec = fk_field_options(model, c)
|
418
450
|
change_spec[:limit] ||= spec.limit if (spec.sql_type != :text ||
|
419
451
|
::DeclareSchema::Model::FieldSpec.mysql_text_limits?) &&
|
420
452
|
(spec.limit || col.limit)
|
421
|
-
change_spec[:precision]
|
422
|
-
change_spec[:scale]
|
423
|
-
change_spec[:null]
|
424
|
-
change_spec[:default]
|
453
|
+
change_spec[:precision] = spec.precision unless spec.precision.nil?
|
454
|
+
change_spec[:scale] = spec.scale unless spec.scale.nil?
|
455
|
+
change_spec[:null] = spec.null unless spec.null && col.null
|
456
|
+
change_spec[:default] = spec.default unless spec.default.nil? && col.default.nil?
|
457
|
+
change_spec[:collation] = spec.collation unless spec.collation.nil?
|
458
|
+
change_spec[:charset] = spec.charset unless spec.charset.nil?
|
425
459
|
|
426
460
|
changes << "change_column :#{new_table_name}, :#{c}, " +
|
427
461
|
([":#{spec.sql_type}"] + format_options(change_spec, spec.sql_type, changing: true)).join(", ")
|
@@ -436,13 +470,20 @@ module Generators
|
|
436
470
|
else
|
437
471
|
change_foreign_key_constraints(model, current_table_name)
|
438
472
|
end
|
473
|
+
table_options_changes, undo_table_options_changes = if ActiveRecord::Base.connection.class.name.match?(/mysql/i)
|
474
|
+
change_table_options(model, current_table_name)
|
475
|
+
else
|
476
|
+
[[], []]
|
477
|
+
end
|
439
478
|
|
440
479
|
[(renames + adds + removes + changes) * "\n",
|
441
480
|
(undo_renames + undo_adds + undo_removes + undo_changes) * "\n",
|
442
481
|
index_changes * "\n",
|
443
482
|
undo_index_changes * "\n",
|
444
483
|
fk_changes * "\n",
|
445
|
-
undo_fk_changes * "\n"
|
484
|
+
undo_fk_changes * "\n",
|
485
|
+
table_options_changes * "\n",
|
486
|
+
undo_table_options_changes * "\n"]
|
446
487
|
end
|
447
488
|
|
448
489
|
def change_indexes(model, old_table_name)
|
@@ -552,6 +593,20 @@ module Generators
|
|
552
593
|
end
|
553
594
|
end
|
554
595
|
|
596
|
+
def change_table_options(model, current_table_name)
|
597
|
+
old_options_definition = ::DeclareSchema::Model::TableOptionsDefinition.for_model(model, current_table_name)
|
598
|
+
new_options_definition = ::DeclareSchema::Model::TableOptionsDefinition.new(model.table_name, table_options_for_model(model))
|
599
|
+
|
600
|
+
if old_options_definition.equivalent?(new_options_definition)
|
601
|
+
[[], []]
|
602
|
+
else
|
603
|
+
[
|
604
|
+
[new_options_definition.alter_table_statement],
|
605
|
+
[old_options_definition.alter_table_statement]
|
606
|
+
]
|
607
|
+
end
|
608
|
+
end
|
609
|
+
|
555
610
|
def revert_table(table)
|
556
611
|
res = StringIO.new
|
557
612
|
schema_dumper_klass = case Rails::VERSION::MAJOR
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe 'DeclareSchema Model FieldSpec' do
|
4
|
+
before do
|
5
|
+
load File.expand_path('prepare_testapp.rb', __dir__)
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'There are no model columns to change' do
|
9
|
+
it '#different_to should return false for int8 == int8' do
|
10
|
+
subject = DeclareSchema::Model::FieldSpec.new(Object, :price, :integer, limit: 8, null: false, position: 0)
|
11
|
+
|
12
|
+
case Rails::VERSION::MAJOR
|
13
|
+
when 4
|
14
|
+
cast_type = ActiveRecord::Type::Integer.new(limit: 8)
|
15
|
+
col = ActiveRecord::ConnectionAdapters::Column.new("price", nil, cast_type, "integer(8)", false)
|
16
|
+
else
|
17
|
+
sql_type_metadata = ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(sql_type: "integer(8)", type: :integer, limit: 8)
|
18
|
+
col = ActiveRecord::ConnectionAdapters::Column.new("price", nil, sql_type_metadata, false, "adverts")
|
19
|
+
end
|
20
|
+
|
21
|
+
expect(subject.different_to?(subject.name, col)).to eq(false)
|
22
|
+
end
|
23
|
+
|
24
|
+
it '#different_to should return false for bigint == bigint' do
|
25
|
+
subject = DeclareSchema::Model::FieldSpec.new(Object, :price, :bigint, null: false, position: 0)
|
26
|
+
|
27
|
+
case Rails::VERSION::MAJOR
|
28
|
+
when 4
|
29
|
+
cast_type = ActiveRecord::Type::BigInteger.new(limit: 8)
|
30
|
+
col = ActiveRecord::ConnectionAdapters::Column.new("price", nil, cast_type, "bigint(20)", false)
|
31
|
+
else
|
32
|
+
sql_type_metadata = ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(sql_type: "bigint(20)", type: :integer, limit: 8)
|
33
|
+
col = ActiveRecord::ConnectionAdapters::Column.new("price", nil, sql_type_metadata, false, "adverts")
|
34
|
+
end
|
35
|
+
|
36
|
+
expect(subject.different_to?(subject.name, col)).to eq(false)
|
37
|
+
end
|
38
|
+
|
39
|
+
it '#different_to should return false for int8 == bigint' do
|
40
|
+
subject = DeclareSchema::Model::FieldSpec.new(Object, :price, :integer, limit: 8, null: false, position: 0)
|
41
|
+
|
42
|
+
case Rails::VERSION::MAJOR
|
43
|
+
when 4
|
44
|
+
cast_type = ActiveRecord::Type::BigInteger.new(limit: 8)
|
45
|
+
col = ActiveRecord::ConnectionAdapters::Column.new("price", nil, cast_type, "bigint(20)", false)
|
46
|
+
else
|
47
|
+
sql_type_metadata = ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(sql_type: "bigint(20)", type: :integer, limit: 8)
|
48
|
+
col = ActiveRecord::ConnectionAdapters::Column.new("price", nil, sql_type_metadata, false, "adverts")
|
49
|
+
end
|
50
|
+
|
51
|
+
expect(subject.different_to?(subject.name, col)).to eq(false)
|
52
|
+
end
|
53
|
+
|
54
|
+
it '#different_to should return false for bigint == int8' do
|
55
|
+
subject = DeclareSchema::Model::FieldSpec.new(Object, :price, :bigint, null: false, position: 0)
|
56
|
+
|
57
|
+
case Rails::VERSION::MAJOR
|
58
|
+
when 4
|
59
|
+
cast_type = ActiveRecord::Type::Integer.new(limit: 8)
|
60
|
+
col = ActiveRecord::ConnectionAdapters::Column.new("price", nil, cast_type, "integer(8)", false)
|
61
|
+
else
|
62
|
+
sql_type_metadata = ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(sql_type: "integer(8)", type: :integer, limit: 8)
|
63
|
+
col = ActiveRecord::ConnectionAdapters::Column.new("price", nil, sql_type_metadata, false, "adverts")
|
64
|
+
end
|
65
|
+
|
66
|
+
expect(subject.different_to?(subject.name, col)).to eq(false)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -27,15 +27,28 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
27
27
|
end
|
28
28
|
EOS
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
30
|
+
case Rails::VERSION::MAJOR
|
31
|
+
when 4, 5
|
32
|
+
expect_test_definition_to_eq('alpha/beta', <<~EOS)
|
33
|
+
require 'test_helper'
|
34
|
+
|
35
|
+
class Alpha::BetaTest < ActiveSupport::TestCase
|
36
|
+
# test "the truth" do
|
37
|
+
# assert true
|
38
|
+
# end
|
39
|
+
end
|
40
|
+
EOS
|
41
|
+
else
|
42
|
+
expect_test_definition_to_eq('alpha/beta', <<~EOS)
|
43
|
+
require "test_helper"
|
44
|
+
|
45
|
+
class Alpha::BetaTest < ActiveSupport::TestCase
|
46
|
+
# test "the truth" do
|
47
|
+
# assert true
|
48
|
+
# end
|
49
|
+
end
|
50
|
+
EOS
|
51
|
+
end
|
39
52
|
|
40
53
|
case Rails::VERSION::MAJOR
|
41
54
|
when 4
|
@@ -1093,4 +1093,29 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
1093
1093
|
expect(base_class).to eq("(Rails::VERSION::MAJOR >= 5 ? ActiveRecord::Migration[4.2] : ActiveRecord::Migration)")
|
1094
1094
|
end
|
1095
1095
|
end
|
1096
|
+
|
1097
|
+
context 'Does not generate migrations' do
|
1098
|
+
it 'for aliased fields bigint -> integer limit 8' do
|
1099
|
+
if Rails::VERSION::MAJOR >= 5 || !ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/)
|
1100
|
+
class Advert < active_record_base_class.constantize
|
1101
|
+
fields do
|
1102
|
+
price :bigint
|
1103
|
+
end
|
1104
|
+
end
|
1105
|
+
|
1106
|
+
generate_migrations '-n', '-m'
|
1107
|
+
|
1108
|
+
migrations = Dir.glob('db/migrate/*declare_schema_migration*.rb')
|
1109
|
+
expect(migrations.size).to eq(1), migrations.inspect
|
1110
|
+
|
1111
|
+
class Advert < active_record_base_class.constantize
|
1112
|
+
fields do
|
1113
|
+
price :integer, limit: 8
|
1114
|
+
end
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
expect { generate_migrations '-n', '-g' }.to output("Database and models match -- nothing to change\n").to_stdout
|
1118
|
+
end
|
1119
|
+
end
|
1120
|
+
end
|
1096
1121
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_record/connection_adapters/mysql2_adapter'
|
4
|
+
require_relative '../../../../lib/declare_schema/model/table_options_definition'
|
5
|
+
|
6
|
+
RSpec.describe DeclareSchema::Model::TableOptionsDefinition do
|
7
|
+
before do
|
8
|
+
load File.expand_path('../prepare_testapp.rb', __dir__)
|
9
|
+
|
10
|
+
class TableOptionsDefinitionTestModel < ActiveRecord::Base
|
11
|
+
fields do
|
12
|
+
name :string, limit: 127, index: true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:model_class) { TableOptionsDefinitionTestModel }
|
18
|
+
|
19
|
+
context 'instance methods' do
|
20
|
+
let(:table_options) { { charset: "utf8", collation: "utf8_general"} }
|
21
|
+
let(:model) { described_class.new('table_options_definition_test_models', table_options) }
|
22
|
+
|
23
|
+
describe '#to_key' do
|
24
|
+
subject { model.to_key }
|
25
|
+
it { should eq(["table_options_definition_test_models", "{:charset=>\"utf8\", :collation=>\"utf8_general\"}"]) }
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#settings' do
|
29
|
+
subject { model.settings }
|
30
|
+
it { should eq("CHARACTER SET utf8 COLLATE utf8_general") }
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#hash' do
|
34
|
+
subject { model.hash }
|
35
|
+
it { should eq(["table_options_definition_test_models", "{:charset=>\"utf8\", :collation=>\"utf8_general\"}"].hash) }
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#to_s' do
|
39
|
+
subject { model.to_s }
|
40
|
+
it { should eq("CHARACTER SET utf8 COLLATE utf8_general") }
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#alter_table_statement' do
|
44
|
+
subject { model.alter_table_statement }
|
45
|
+
it { should eq('execute "ALTER TABLE \"table_options_definition_test_models\" CHARACTER SET utf8 COLLATE utf8_general;"') }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
context 'class << self' do
|
51
|
+
describe '#for_model' do
|
52
|
+
context 'when using a SQLite connection' do
|
53
|
+
subject { described_class.for_model(model_class) }
|
54
|
+
it { should eq(described_class.new(model_class.table_name, {})) }
|
55
|
+
end
|
56
|
+
# TODO: Convert these tests to run against a MySQL database so that we can
|
57
|
+
# perform them without mocking out so much
|
58
|
+
context 'when using a MySQL connection' do
|
59
|
+
before do
|
60
|
+
double(ActiveRecord::ConnectionAdapters::Mysql2Adapter).tap do |stub_connection|
|
61
|
+
expect(stub_connection).to receive(:class).and_return(ActiveRecord::ConnectionAdapters::Mysql2Adapter)
|
62
|
+
expect(stub_connection).to receive(:current_database).and_return('test_database')
|
63
|
+
expect(stub_connection).to receive(:quote_string).with('test_database').and_return('test_database')
|
64
|
+
expect(stub_connection).to receive(:quote_string).with(model_class.table_name).and_return(model_class.table_name)
|
65
|
+
expect(stub_connection).to(
|
66
|
+
receive(:select_one).with(<<~EOS)
|
67
|
+
SELECT CCSA.character_set_name, CCSA.collation_name
|
68
|
+
FROM information_schema.`TABLES` T, information_schema.`COLLATION_CHARACTER_SET_APPLICABILITY` CCSA
|
69
|
+
WHERE CCSA.collation_name = T.table_collation AND
|
70
|
+
T.table_schema = 'test_database' AND
|
71
|
+
T.table_name = '#{model_class.table_name}';
|
72
|
+
EOS
|
73
|
+
.and_return({ "character_set_name" => "utf8", "collation_name" => "utf8_general" })
|
74
|
+
)
|
75
|
+
allow(model_class).to receive(:connection).and_return(stub_connection)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
subject { described_class.for_model(model_class) }
|
80
|
+
it { should eq(described_class.new(model_class.table_name, { charset: "utf8", collation: "utf8_general" })) }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -43,6 +43,34 @@ module Generators
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
+
describe '#default_charset' do
|
47
|
+
subject { described_class.default_charset }
|
48
|
+
|
49
|
+
context 'when not explicitly set' do
|
50
|
+
it { should eq(:utf8mb4) }
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'when explicitly set' do
|
54
|
+
before { described_class.default_charset = :utf8 }
|
55
|
+
after { described_class.default_charset = described_class::DEFAULT_CHARSET }
|
56
|
+
it { should eq(:utf8) }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#default_collation' do
|
61
|
+
subject { described_class.default_collation }
|
62
|
+
|
63
|
+
context 'when not explicitly set' do
|
64
|
+
it { should eq(:utf8mb4_general) }
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'when explicitly set' do
|
68
|
+
before { described_class.default_collation = :utf8mb4_general_ci }
|
69
|
+
after { described_class.default_collation = described_class::DEFAULT_COLLATION }
|
70
|
+
it { should eq(:utf8mb4_general_ci) }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
46
74
|
describe 'load_rails_models' do
|
47
75
|
before do
|
48
76
|
expect(Rails.application).to receive(:eager_load!)
|
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: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Invoca Development adapted from hobo_fields by Tom Locke
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-12-
|
11
|
+
date: 2020-12-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -32,13 +32,13 @@ executables:
|
|
32
32
|
extensions: []
|
33
33
|
extra_rdoc_files: []
|
34
34
|
files:
|
35
|
-
- ".dependabot
|
35
|
+
- ".github/dependabot.yml"
|
36
|
+
- ".github/workflows/declare_schema_build.yml"
|
36
37
|
- ".github/workflows/gem_release.yml"
|
37
38
|
- ".gitignore"
|
38
39
|
- ".rspec"
|
39
40
|
- ".rubocop.yml"
|
40
41
|
- ".ruby-version"
|
41
|
-
- ".travis.yml"
|
42
42
|
- Appraisals
|
43
43
|
- CHANGELOG.md
|
44
44
|
- Gemfile
|
@@ -61,6 +61,7 @@ files:
|
|
61
61
|
- lib/declare_schema/model/field_spec.rb
|
62
62
|
- lib/declare_schema/model/foreign_key_definition.rb
|
63
63
|
- lib/declare_schema/model/index_definition.rb
|
64
|
+
- lib/declare_schema/model/table_options_definition.rb
|
64
65
|
- lib/declare_schema/railtie.rb
|
65
66
|
- lib/declare_schema/version.rb
|
66
67
|
- lib/generators/declare_schema/migration/USAGE
|
@@ -74,10 +75,12 @@ files:
|
|
74
75
|
- lib/generators/declare_schema/support/thor_shell.rb
|
75
76
|
- spec/lib/declare_schema/api_spec.rb
|
76
77
|
- spec/lib/declare_schema/field_declaration_dsl_spec.rb
|
78
|
+
- spec/lib/declare_schema/field_spec_spec.rb
|
77
79
|
- spec/lib/declare_schema/generator_spec.rb
|
78
80
|
- spec/lib/declare_schema/interactive_primary_key_spec.rb
|
79
81
|
- spec/lib/declare_schema/migration_generator_spec.rb
|
80
82
|
- spec/lib/declare_schema/model/index_definition_spec.rb
|
83
|
+
- spec/lib/declare_schema/model/table_options_definition_spec.rb
|
81
84
|
- spec/lib/declare_schema/prepare_testapp.rb
|
82
85
|
- spec/lib/generators/declare_schema/migration/migrator_spec.rb
|
83
86
|
- spec/spec_helper.rb
|
@@ -87,7 +90,7 @@ homepage: https://github.com/Invoca/declare_schema
|
|
87
90
|
licenses: []
|
88
91
|
metadata:
|
89
92
|
allowed_push_host: https://rubygems.org
|
90
|
-
post_install_message:
|
93
|
+
post_install_message:
|
91
94
|
rdoc_options: []
|
92
95
|
require_paths:
|
93
96
|
- lib
|
@@ -103,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
106
|
version: 1.3.6
|
104
107
|
requirements: []
|
105
108
|
rubygems_version: 3.0.3
|
106
|
-
signing_key:
|
109
|
+
signing_key:
|
107
110
|
specification_version: 4
|
108
111
|
summary: Database migration generator for Rails
|
109
112
|
test_files: []
|
data/.dependabot/config.yml
DELETED
data/.travis.yml
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
---
|
2
|
-
dist: trusty
|
3
|
-
os: linux
|
4
|
-
language: ruby
|
5
|
-
cache: bundler
|
6
|
-
rvm:
|
7
|
-
- 2.4.5
|
8
|
-
- 2.5.8
|
9
|
-
- 2.6.5
|
10
|
-
- 2.7.1
|
11
|
-
- ruby-head
|
12
|
-
gemfile:
|
13
|
-
- gemfiles/rails_4.gemfile
|
14
|
-
- gemfiles/rails_5.gemfile
|
15
|
-
- gemfiles/rails_6.gemfile
|
16
|
-
jobs:
|
17
|
-
fast_finish: false
|
18
|
-
exclude:
|
19
|
-
- gemfile: gemfiles/rails_4.gemfile
|
20
|
-
rvm: 2.7.1
|
21
|
-
- gemfile: gemfiles/rails_5.gemfile
|
22
|
-
rvm: 2.4.5
|
23
|
-
- gemfile: gemfiles/rails_6.gemfile
|
24
|
-
rvm: 2.4.5
|
25
|
-
allow_failures:
|
26
|
-
- rvm: ruby-head
|
27
|
-
before_install:
|
28
|
-
- rm -f /home/travis/.rvm/rubies/ruby-2.*/lib/ruby/gems/2.*/specifications/default/bundler-2.*.gemspec
|
29
|
-
- echo y | rvm @global do gem install bundler -v 1.17.3 --force --default
|
30
|
-
- gem install bundler -v 1.17.3 --force --default
|
31
|
-
- gem install bundler -v 1.17.3
|
32
|
-
install:
|
33
|
-
- bundle --version
|
34
|
-
- bundle install --jobs=3 --retry=3 --path=${BUNDLE_PATH:-vendor/bundle}
|
35
|
-
script:
|
36
|
-
- bundle exec rake test:prepare_testapp[force]
|
37
|
-
- bundle exec rake test:all < test_responses.txt
|