declare_schema 0.4.0 → 0.5.0.pre.3
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/.github/dependabot.yml +14 -0
- data/CHANGELOG.md +19 -3
- data/Gemfile +1 -0
- data/Gemfile.lock +5 -3
- 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 +4 -0
- data/lib/declare_schema/model/field_spec.rb +82 -26
- data/lib/declare_schema/model/index_definition.rb +29 -11
- 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 +95 -38
- data/spec/lib/declare_schema/api_spec.rb +1 -1
- data/spec/lib/declare_schema/migration_generator_spec.rb +8 -3
- data/spec/lib/declare_schema/model/index_definition_spec.rb +52 -12
- 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 +8 -6
- data/.dependabot/config.yml +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0fc9a936c7e14850eb00ae650edd879d4da0971c90d54ccc573293e8a2a690a
|
4
|
+
data.tar.gz: 5ce7613f80340733d6775f5416fb2d27d34c1497032b5c87adef9ff9988c6057
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 589b1f1de17bad311a9182fb30fac5e05b1ed151c4e430b449600efb4ed95f795d25f9cf7357298ffd803e40bb720ec01d6536923dc880c9ea7648d25876a369
|
7
|
+
data.tar.gz: 80b8d072e0ecb96302df946e529bdf32f590f3acfc7cbc0dd47bd0ecb4b22be87dd0a9a22defbd1b1e7cf3746e8542c3b233254234eafaf0592724b331bddcfc
|
@@ -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
|
data/CHANGELOG.md
CHANGED
@@ -4,10 +4,24 @@ 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.5.0] - Unreleased
|
8
|
+
### Added
|
9
|
+
- Added support for configuring the character set and collation for MySQL databases
|
10
|
+
at the global, table, and field level
|
11
|
+
|
12
|
+
## [0.4.2] - 2020-12-05
|
13
|
+
### Fixed
|
14
|
+
- Generalize the fix below to sqlite || Rails 4.
|
15
|
+
|
16
|
+
## [0.4.1] - 2020-12-04
|
17
|
+
### Fixed
|
18
|
+
- Fixed a bug detecting compound primary keys in Rails 4.
|
19
|
+
|
7
20
|
## [0.4.0] - 2020-11-20
|
8
21
|
### Added
|
9
|
-
- Fields may be declared with `serialize: true`
|
10
|
-
|
22
|
+
- Fields may be declared with `serialize: true` (any value with a valid `.to_yaml` stored as YAML),
|
23
|
+
or `serialize: <serializeable-class>`, where `<serializeable-class>`
|
24
|
+
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)
|
11
25
|
or any custom serializable class.
|
12
26
|
This invokes `ActiveSupport`'s `serialize` macro for that field, passing the serializable class, if given.
|
13
27
|
|
@@ -55,11 +69,13 @@ using the appropriate Rails configuration attributes.
|
|
55
69
|
### Changed
|
56
70
|
- Added travis support and created 2 specs as a starting point.
|
57
71
|
|
58
|
-
|
59
72
|
## [0.1.1] - 2020-09-24
|
60
73
|
### Added
|
61
74
|
- Initial version from https://github.com/Invoca/hobo_fields v4.1.0.
|
62
75
|
|
76
|
+
[0.5.0]: https://github.com/Invoca/declare_schema/compare/v0.4.2...v0.5.0
|
77
|
+
[0.4.2]: https://github.com/Invoca/declare_schema/compare/v0.4.1...v0.4.2
|
78
|
+
[0.4.1]: https://github.com/Invoca/declare_schema/compare/v0.4.0...v0.4.1
|
63
79
|
[0.4.0]: https://github.com/Invoca/declare_schema/compare/v0.3.1...v0.4.0
|
64
80
|
[0.3.1]: https://github.com/Invoca/declare_schema/compare/v0.3.0...v0.3.1
|
65
81
|
[0.3.0]: https://github.com/Invoca/declare_schema/compare/v0.2.0...v0.3.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.5.0.pre.3)
|
5
5
|
rails (>= 4.2)
|
6
6
|
|
7
7
|
GEM
|
@@ -54,7 +54,7 @@ GEM
|
|
54
54
|
thor (>= 0.14.0)
|
55
55
|
arel (9.0.0)
|
56
56
|
ast (2.4.1)
|
57
|
-
bootsnap (1.5.
|
57
|
+
bootsnap (1.5.1)
|
58
58
|
msgpack (~> 1.0)
|
59
59
|
builder (3.2.4)
|
60
60
|
byebug (11.1.3)
|
@@ -69,7 +69,7 @@ GEM
|
|
69
69
|
activesupport (>= 4.2.0)
|
70
70
|
i18n (1.8.5)
|
71
71
|
concurrent-ruby (~> 1.0)
|
72
|
-
listen (3.
|
72
|
+
listen (3.3.1)
|
73
73
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
74
74
|
rb-inotify (~> 0.9, >= 0.9.10)
|
75
75
|
loofah (2.7.0)
|
@@ -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::Migrator.default_charset = "utf8mb4"
|
97
|
+
Generators::DeclareSchema::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 %(
|
@@ -54,6 +54,9 @@ 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
|
+
else
|
58
|
+
@options[:collation] and raise "collation may only given for :string and :text fields"
|
59
|
+
@options[:charset] and raise "charset may only given for :string and :text fields"
|
57
60
|
end
|
58
61
|
@position = position_option || model.field_specs.length
|
59
62
|
end
|
@@ -102,6 +105,18 @@ module DeclareSchema
|
|
102
105
|
@options[:default]
|
103
106
|
end
|
104
107
|
|
108
|
+
def collation
|
109
|
+
if ActiveRecord::Base.connection.class.name.match?(/mysql/i)
|
110
|
+
(@options[:collation] || model.table_options[:collation] || Generators::DeclareSchema::Migration::Migrator.default_collation).to_s
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def charset
|
115
|
+
if ActiveRecord::Base.connection.class.name.match?(/mysql/i)
|
116
|
+
(@options[:charset] || model.table_options[:charset] || Generators::DeclareSchema::Migration::Migrator.default_charset).to_s
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
105
120
|
def same_type?(col_spec)
|
106
121
|
type = sql_type
|
107
122
|
normalized_type = TYPE_SYNONYMS[type] || type
|
@@ -109,36 +124,77 @@ module DeclareSchema
|
|
109
124
|
normalized_type == normalized_col_spec_type
|
110
125
|
end
|
111
126
|
|
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
|
-
|
127
|
+
def different_to?(table_name, col_spec)
|
128
|
+
!same_as(table_name, col_spec)
|
129
|
+
end
|
130
|
+
|
131
|
+
def same_as(table_name, col_spec)
|
132
|
+
same_type?(col_spec) &&
|
133
|
+
same_attributes?(col_spec) &&
|
134
|
+
(!type.in?([:text, :string]) || same_charset_and_collation?(table_name, col_spec))
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
def same_attributes?(col_spec)
|
140
|
+
native_type = native_types[type]
|
141
|
+
check_attributes = [:null, :default]
|
142
|
+
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
|
143
|
+
check_attributes -= [:default] if sql_type == :text && col_spec.class.name =~ /mysql/i
|
144
|
+
check_attributes << :limit if sql_type.in?([:string, :binary, :varbinary, :integer, :enum]) ||
|
145
|
+
(sql_type == :text && self.class.mysql_text_limits?)
|
146
|
+
check_attributes.all? do |k|
|
147
|
+
if k == :default
|
148
|
+
case Rails::VERSION::MAJOR
|
149
|
+
when 4
|
150
|
+
col_spec.type_cast_from_database(col_spec.default) == col_spec.type_cast_from_database(default)
|
151
|
+
else
|
152
|
+
cast_type = ActiveRecord::Base.connection.lookup_cast_type_from_column(col_spec) or raise "cast_type not found for #{col_spec.inspect}"
|
153
|
+
cast_type.deserialize(col_spec.default) == cast_type.deserialize(default)
|
154
|
+
end
|
155
|
+
else
|
156
|
+
col_value = col_spec.send(k)
|
157
|
+
if col_value.nil? && native_type
|
158
|
+
col_value = native_type[k]
|
137
159
|
end
|
160
|
+
col_value == send(k)
|
138
161
|
end
|
162
|
+
end
|
139
163
|
end
|
140
164
|
|
141
|
-
|
165
|
+
def same_charset_and_collation?(table_name, col_spec)
|
166
|
+
current_collation_and_charset = collation_and_charset_for_column(table_name, col_spec)
|
167
|
+
|
168
|
+
collation == current_collation_and_charset[:collation] &&
|
169
|
+
charset == current_collation_and_charset[:charset]
|
170
|
+
end
|
171
|
+
|
172
|
+
def collation_and_charset_for_column(table_name, col_spec)
|
173
|
+
column_name = col_spec.name
|
174
|
+
connection = ActiveRecord::Base.connection
|
175
|
+
|
176
|
+
if connection.class.name.match?(/mysql/i)
|
177
|
+
database_name = connection.current_database
|
178
|
+
|
179
|
+
defaults = connection.select_one(<<~EOS)
|
180
|
+
SELECT C.character_set_name, C.collation_name
|
181
|
+
FROM information_schema.`COLUMNS` C
|
182
|
+
WHERE C.table_schema = '#{connection.quote_string(database_name)}' AND
|
183
|
+
C.table_name = '#{connection.quote_string(table_name)}' AND
|
184
|
+
C.column_name = '#{connection.quote_string(column_name)}';
|
185
|
+
EOS
|
186
|
+
|
187
|
+
defaults["character_set_name"] or raise "character_set_name missing from #{defaults.inspect}"
|
188
|
+
defaults["collation_name"] or raise "collation_name missing from #{defaults.inspect}"
|
189
|
+
|
190
|
+
{
|
191
|
+
charset: defaults["character_set_name"],
|
192
|
+
collation: defaults["collation_name"]
|
193
|
+
}
|
194
|
+
else
|
195
|
+
{}
|
196
|
+
end
|
197
|
+
end
|
142
198
|
|
143
199
|
def native_type?(type)
|
144
200
|
type.to_sym != :primary_key && native_types.has_key?(type)
|
@@ -37,16 +37,9 @@ module DeclareSchema
|
|
37
37
|
def for_model(model, old_table_name = nil)
|
38
38
|
t = old_table_name || model.table_name
|
39
39
|
|
40
|
-
primary_key_columns = Array(model.connection.primary_key(t)).presence ||
|
41
|
-
|
42
|
-
|
43
|
-
if cols.any? { |col| col.name == 'id' }
|
44
|
-
'id'
|
45
|
-
else
|
46
|
-
cols.find { |col| col.type.to_s.include?('int') }&.name or raise "could not guess primary key for #{t} in #{cols.inspect}"
|
47
|
-
end
|
48
|
-
)
|
49
|
-
end
|
40
|
+
primary_key_columns = Array(model.connection.primary_key(t)).presence || sqlite_compound_primary_key(model, t) or
|
41
|
+
raise "could not find primary key for table #{t} in #{model.connection.columns(t).inspect}"
|
42
|
+
|
50
43
|
primary_key_found = false
|
51
44
|
index_definitions = model.connection.indexes(t).map do |i|
|
52
45
|
model.ignore_indexes.include?(i.name) and next
|
@@ -55,7 +48,8 @@ module DeclareSchema
|
|
55
48
|
raise "primary key on #{t} was not unique on #{primary_key_columns} (was unique=#{i.unique} on #{i.columns})"
|
56
49
|
primary_key_found = true
|
57
50
|
elsif i.columns == primary_key_columns && i.unique
|
58
|
-
|
51
|
+
# skip this primary key index since we'll create it below, with PRIMARY_KEY_NAME
|
52
|
+
next
|
59
53
|
end
|
60
54
|
new(model, i.columns, name: i.name, unique: i.unique, where: i.where, table_name: old_table_name)
|
61
55
|
end.compact
|
@@ -65,6 +59,30 @@ module DeclareSchema
|
|
65
59
|
end
|
66
60
|
index_definitions
|
67
61
|
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# This is the old approach which is still needed for MySQL in Rails 4 and SQLite
|
66
|
+
def sqlite_compound_primary_key(model, table)
|
67
|
+
ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/) || Rails::VERSION::MAJOR < 5 or return nil
|
68
|
+
|
69
|
+
connection = model.connection.dup
|
70
|
+
|
71
|
+
class << connection # defeat Rails MySQL driver code that skips the primary key by changing its name to a symbol
|
72
|
+
def each_hash(result)
|
73
|
+
super do |hash|
|
74
|
+
if hash[:Key_name] == PRIMARY_KEY_NAME
|
75
|
+
hash[:Key_name] = PRIMARY_KEY_NAME.to_sym
|
76
|
+
end
|
77
|
+
yield hash
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
pk_index = connection.indexes(table).find { |index| index.name.to_s == PRIMARY_KEY_NAME } or return nil
|
83
|
+
|
84
|
+
Array(pk_index.columns)
|
85
|
+
end
|
68
86
|
end
|
69
87
|
|
70
88
|
def primary_key?
|
@@ -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
|
@@ -69,13 +73,19 @@ module Generators
|
|
69
73
|
class Migrator
|
70
74
|
class Error < RuntimeError; end
|
71
75
|
|
72
|
-
|
73
|
-
|
76
|
+
DEFAULT_CHARSET = :utf8mb4
|
77
|
+
DEFAULT_COLLATION = :utf8mb4_general
|
78
|
+
|
79
|
+
@ignore_models = []
|
80
|
+
@ignore_tables = []
|
74
81
|
@before_generating_migration_callback = nil
|
75
|
-
@active_record_class
|
82
|
+
@active_record_class = ActiveRecord::Base
|
83
|
+
@default_charset = DEFAULT_CHARSET
|
84
|
+
@default_collation = DEFAULT_COLLATION
|
76
85
|
|
77
86
|
class << self
|
78
|
-
attr_accessor :ignore_models, :ignore_tables, :disable_indexing, :disable_constraints,
|
87
|
+
attr_accessor :ignore_models, :ignore_tables, :disable_indexing, :disable_constraints,
|
88
|
+
:active_record_class, :default_charset, :default_collation
|
79
89
|
attr_reader :before_generating_migration_callback
|
80
90
|
|
81
91
|
def active_record_class
|
@@ -292,52 +302,76 @@ module Generators
|
|
292
302
|
"drop_table :#{t}"
|
293
303
|
end * "\n"
|
294
304
|
|
295
|
-
changes
|
296
|
-
undo_changes
|
297
|
-
index_changes
|
298
|
-
undo_index_changes
|
299
|
-
fk_changes
|
300
|
-
undo_fk_changes
|
305
|
+
changes = []
|
306
|
+
undo_changes = []
|
307
|
+
index_changes = []
|
308
|
+
undo_index_changes = []
|
309
|
+
fk_changes = []
|
310
|
+
undo_fk_changes = []
|
311
|
+
table_options_changes = []
|
312
|
+
undo_table_options_changes = []
|
313
|
+
|
301
314
|
to_change.each do |t|
|
302
315
|
model = models_by_table_name[t]
|
303
316
|
table = to_rename.key(t) || model.table_name
|
304
317
|
if table.in?(db_tables)
|
305
|
-
change, undo, index_change, undo_index, fk_change, undo_fk = change_table(model, table)
|
318
|
+
change, undo, index_change, undo_index, fk_change, undo_fk, table_options_change, undo_table_options_change = change_table(model, table)
|
306
319
|
changes << change
|
307
320
|
undo_changes << undo
|
308
321
|
index_changes << index_change
|
309
322
|
undo_index_changes << undo_index
|
310
323
|
fk_changes << fk_change
|
311
324
|
undo_fk_changes << undo_fk
|
325
|
+
table_options_changes << table_options_change
|
326
|
+
undo_table_options_changes << undo_table_options_change
|
312
327
|
end
|
313
328
|
end
|
314
329
|
|
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"
|
330
|
+
up = [renames, drops, creates, changes, index_changes, fk_changes, table_options_changes].flatten.reject(&:blank?) * "\n\n"
|
331
|
+
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
332
|
|
318
333
|
[up, down]
|
319
334
|
end
|
320
335
|
|
321
336
|
def create_table(model)
|
322
|
-
longest_field_name
|
323
|
-
disable_auto_increment
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
337
|
+
longest_field_name = model.field_specs.values.map { |f| f.sql_type.to_s.length }.max
|
338
|
+
disable_auto_increment = model.respond_to?(:disable_auto_increment) && model.disable_auto_increment
|
339
|
+
table_options_definition = ::DeclareSchema::Model::TableOptionsDefinition.new(model.table_name, table_options_for_model(model))
|
340
|
+
field_definitions = [
|
341
|
+
disable_auto_increment ? "t.integer :id, limit: 8, auto_increment: false, primary_key: true" : nil,
|
342
|
+
*(model.field_specs.values.sort_by(&:position).map { |f| create_field(f, longest_field_name) })
|
343
|
+
].compact
|
344
|
+
|
345
|
+
<<~EOS.strip
|
346
|
+
create_table :#{model.table_name}, #{create_table_options(model, disable_auto_increment)} do |t|
|
347
|
+
#{field_definitions.join("\n")}
|
331
348
|
end
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
349
|
+
|
350
|
+
#{table_options_definition.alter_table_statement unless ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/)}
|
351
|
+
#{create_indexes(model).join("\n") unless Migrator.disable_indexing}
|
352
|
+
#{create_constraints(model).join("\n") unless Migrator.disable_indexing}
|
353
|
+
EOS
|
354
|
+
end
|
355
|
+
|
356
|
+
def create_table_options(model, disable_auto_increment)
|
357
|
+
if model.primary_key.blank? || disable_auto_increment
|
358
|
+
"id: false"
|
359
|
+
elsif model.primary_key == "id"
|
360
|
+
"id: :bigint"
|
361
|
+
else
|
362
|
+
"primary_key: :#{model.primary_key}"
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
def table_options_for_model(model)
|
367
|
+
if ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/)
|
368
|
+
{}
|
369
|
+
else
|
370
|
+
{
|
371
|
+
charset: model.table_options[:charset] || Migrator.default_charset,
|
372
|
+
collation: model.table_options[:collation] || Migrator.default_collation
|
373
|
+
}
|
374
|
+
end
|
341
375
|
end
|
342
376
|
|
343
377
|
def create_indexes(model)
|
@@ -351,7 +385,7 @@ module Generators
|
|
351
385
|
def create_field(field_spec, field_name_width)
|
352
386
|
options = fk_field_options(field_spec.model, field_spec.name).merge(field_spec.sql_options)
|
353
387
|
args = [field_spec.name.inspect] + format_options(options, field_spec.sql_type)
|
354
|
-
format("
|
388
|
+
format("t.%-*s %s", field_name_width, field_spec.sql_type, args.join(', '))
|
355
389
|
end
|
356
390
|
|
357
391
|
def change_table(model, current_table_name)
|
@@ -413,15 +447,17 @@ module Generators
|
|
413
447
|
col_name = old_names[c] || c
|
414
448
|
col = db_columns[col_name]
|
415
449
|
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
|
450
|
+
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
451
|
change_spec = fk_field_options(model, c)
|
418
452
|
change_spec[:limit] ||= spec.limit if (spec.sql_type != :text ||
|
419
453
|
::DeclareSchema::Model::FieldSpec.mysql_text_limits?) &&
|
420
454
|
(spec.limit || col.limit)
|
421
|
-
change_spec[:precision]
|
422
|
-
change_spec[:scale]
|
423
|
-
change_spec[:null]
|
424
|
-
change_spec[:default]
|
455
|
+
change_spec[:precision] = spec.precision unless spec.precision.nil?
|
456
|
+
change_spec[:scale] = spec.scale unless spec.scale.nil?
|
457
|
+
change_spec[:null] = spec.null unless spec.null && col.null
|
458
|
+
change_spec[:default] = spec.default unless spec.default.nil? && col.default.nil?
|
459
|
+
change_spec[:collation] = spec.collation unless spec.collation.nil?
|
460
|
+
change_spec[:charset] = spec.charset unless spec.charset.nil?
|
425
461
|
|
426
462
|
changes << "change_column :#{new_table_name}, :#{c}, " +
|
427
463
|
([":#{spec.sql_type}"] + format_options(change_spec, spec.sql_type, changing: true)).join(", ")
|
@@ -436,13 +472,20 @@ module Generators
|
|
436
472
|
else
|
437
473
|
change_foreign_key_constraints(model, current_table_name)
|
438
474
|
end
|
475
|
+
table_options_changes, undo_table_options_changes = if ActiveRecord::Base.connection.class.name.match?(/mysql/i)
|
476
|
+
change_table_options(model, current_table_name)
|
477
|
+
else
|
478
|
+
[[], []]
|
479
|
+
end
|
439
480
|
|
440
481
|
[(renames + adds + removes + changes) * "\n",
|
441
482
|
(undo_renames + undo_adds + undo_removes + undo_changes) * "\n",
|
442
483
|
index_changes * "\n",
|
443
484
|
undo_index_changes * "\n",
|
444
485
|
fk_changes * "\n",
|
445
|
-
undo_fk_changes * "\n"
|
486
|
+
undo_fk_changes * "\n",
|
487
|
+
table_options_changes * "\n",
|
488
|
+
undo_table_options_changes * "\n"]
|
446
489
|
end
|
447
490
|
|
448
491
|
def change_indexes(model, old_table_name)
|
@@ -552,6 +595,20 @@ module Generators
|
|
552
595
|
end
|
553
596
|
end
|
554
597
|
|
598
|
+
def change_table_options(model, current_table_name)
|
599
|
+
old_options_definition = ::DeclareSchema::Model::TableOptionsDefinition.for_model(model, current_table_name)
|
600
|
+
new_options_definition = ::DeclareSchema::Model::TableOptionsDefinition.new(model.table_name, table_options_for_model(model))
|
601
|
+
|
602
|
+
if old_options_definition.equivalent?(new_options_definition)
|
603
|
+
[[], []]
|
604
|
+
else
|
605
|
+
[
|
606
|
+
[new_options_definition.alter_table_statement],
|
607
|
+
[old_options_definition.alter_table_statement]
|
608
|
+
]
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
555
612
|
def revert_table(table)
|
556
613
|
res = StringIO.new
|
557
614
|
schema_dumper_klass = case Rails::VERSION::MAJOR
|
@@ -43,6 +43,12 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
43
43
|
ActiveRecord::Migration.class_eval(up)
|
44
44
|
expect(Advert.columns.map(&:name)).to eq(["id", "name"])
|
45
45
|
|
46
|
+
if Rails::VERSION::MAJOR < 5
|
47
|
+
# Rails 4 sqlite driver doesn't create PK properly. Fix that by dropping and recreating.
|
48
|
+
ActiveRecord::Base.connection.execute("drop table adverts")
|
49
|
+
ActiveRecord::Base.connection.execute('CREATE TABLE "adverts" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255))')
|
50
|
+
end
|
51
|
+
|
46
52
|
class Advert < ActiveRecord::Base
|
47
53
|
fields do
|
48
54
|
name :string, limit: 255, null: true
|
@@ -610,9 +616,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
610
616
|
# Dropping tables is where the automatic down-migration really comes in handy:
|
611
617
|
|
612
618
|
rails4_table_create = <<~EOS.strip
|
613
|
-
create_table "adverts",
|
614
|
-
t.
|
615
|
-
t.string "name", limit: 255
|
619
|
+
create_table "adverts", force: :cascade do |t|
|
620
|
+
t.string "name", limit: 255
|
616
621
|
end
|
617
622
|
EOS
|
618
623
|
|
@@ -13,16 +13,25 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
|
|
13
13
|
before do
|
14
14
|
load File.expand_path('../prepare_testapp.rb', __dir__)
|
15
15
|
|
16
|
-
class
|
16
|
+
class IndexDefinitionTestModel < ActiveRecord::Base
|
17
17
|
fields do
|
18
18
|
name :string, limit: 127, index: true
|
19
19
|
|
20
20
|
timestamps
|
21
21
|
end
|
22
22
|
end
|
23
|
+
|
24
|
+
class IndexDefinitionCompoundIndexModel < ActiveRecord::Base
|
25
|
+
fields do
|
26
|
+
fk1_id :integer
|
27
|
+
fk2_id :integer
|
28
|
+
|
29
|
+
timestamps
|
30
|
+
end
|
31
|
+
end
|
23
32
|
end
|
24
33
|
|
25
|
-
let(:model_class) {
|
34
|
+
let(:model_class) { IndexDefinitionTestModel }
|
26
35
|
|
27
36
|
describe 'instance methods' do
|
28
37
|
let(:model) { model_class.new }
|
@@ -53,13 +62,20 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
|
|
53
62
|
context 'with a migrated database' do
|
54
63
|
before do
|
55
64
|
ActiveRecord::Base.connection.execute <<~EOS
|
56
|
-
CREATE TABLE
|
65
|
+
CREATE TABLE index_definition_test_models (
|
57
66
|
id INTEGER NOT NULL PRIMARY KEY,
|
58
67
|
name TEXT NOT NULL
|
59
68
|
)
|
60
69
|
EOS
|
61
70
|
ActiveRecord::Base.connection.execute <<~EOS
|
62
|
-
CREATE UNIQUE INDEX
|
71
|
+
CREATE UNIQUE INDEX index_definition_test_models_on_name ON index_definition_test_models(name)
|
72
|
+
EOS
|
73
|
+
ActiveRecord::Base.connection.execute <<~EOS
|
74
|
+
CREATE TABLE index_definition_compound_index_models (
|
75
|
+
fk1_id INTEGER NULL,
|
76
|
+
fk2_id INTEGER NULL,
|
77
|
+
PRIMARY KEY (fk1_id, fk2_id)
|
78
|
+
)
|
63
79
|
EOS
|
64
80
|
ActiveRecord::Base.connection.schema_cache.clear!
|
65
81
|
end
|
@@ -67,14 +83,38 @@ RSpec.describe DeclareSchema::Model::IndexDefinition do
|
|
67
83
|
describe 'for_model' do
|
68
84
|
subject { described_class.for_model(model_class) }
|
69
85
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
[
|
74
|
-
|
75
|
-
|
76
|
-
[
|
77
|
-
|
86
|
+
context 'with single-column PK' do
|
87
|
+
it 'returns the indexes for the model' do
|
88
|
+
expect(subject.size).to eq(2), subject.inspect
|
89
|
+
expect([:name, :columns, :unique].map { |attr| subject[0].send(attr) }).to eq(
|
90
|
+
['index_definition_test_models_on_name', ['name'], true]
|
91
|
+
)
|
92
|
+
expect([:name, :columns, :unique].map { |attr| subject[1].send(attr) }).to eq(
|
93
|
+
['PRIMARY', ['id'], true]
|
94
|
+
)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'with compound-column PK' do
|
99
|
+
let(:model_class) { IndexDefinitionCompoundIndexModel }
|
100
|
+
|
101
|
+
it 'returns the indexes for the model' do
|
102
|
+
# Simulate MySQL for Rails 4 work-around
|
103
|
+
if Rails::VERSION::MAJOR < 5
|
104
|
+
expect(model_class.connection).to receive(:primary_key).with('index_definition_compound_index_models').and_return(nil)
|
105
|
+
connection_stub = instance_double(ActiveRecord::ConnectionAdapters::SQLite3Adapter, "connection")
|
106
|
+
expect(connection_stub).to receive(:indexes).
|
107
|
+
with('index_definition_compound_index_models').
|
108
|
+
and_return([DeclareSchema::Model::IndexDefinition.new(model_class, ['fk1_id', 'fk2_id'], name: 'PRIMARY')])
|
109
|
+
|
110
|
+
expect(model_class.connection).to receive(:dup).and_return(connection_stub)
|
111
|
+
end
|
112
|
+
|
113
|
+
expect(subject.size).to eq(1), subject.inspect
|
114
|
+
expect([:name, :columns, :unique].map { |attr| subject[0].send(attr) }).to eq(
|
115
|
+
['PRIMARY', ['fk1_id', 'fk2_id'], true]
|
116
|
+
)
|
117
|
+
end
|
78
118
|
end
|
79
119
|
end
|
80
120
|
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.5.0.pre.3
|
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-
|
11
|
+
date: 2020-12-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -32,7 +32,7 @@ executables:
|
|
32
32
|
extensions: []
|
33
33
|
extra_rdoc_files: []
|
34
34
|
files:
|
35
|
-
- ".dependabot
|
35
|
+
- ".github/dependabot.yml"
|
36
36
|
- ".github/workflows/gem_release.yml"
|
37
37
|
- ".gitignore"
|
38
38
|
- ".rspec"
|
@@ -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
|
@@ -78,6 +79,7 @@ files:
|
|
78
79
|
- spec/lib/declare_schema/interactive_primary_key_spec.rb
|
79
80
|
- spec/lib/declare_schema/migration_generator_spec.rb
|
80
81
|
- spec/lib/declare_schema/model/index_definition_spec.rb
|
82
|
+
- spec/lib/declare_schema/model/table_options_definition_spec.rb
|
81
83
|
- spec/lib/declare_schema/prepare_testapp.rb
|
82
84
|
- spec/lib/generators/declare_schema/migration/migrator_spec.rb
|
83
85
|
- spec/spec_helper.rb
|
@@ -87,7 +89,7 @@ homepage: https://github.com/Invoca/declare_schema
|
|
87
89
|
licenses: []
|
88
90
|
metadata:
|
89
91
|
allowed_push_host: https://rubygems.org
|
90
|
-
post_install_message:
|
92
|
+
post_install_message:
|
91
93
|
rdoc_options: []
|
92
94
|
require_paths:
|
93
95
|
- lib
|
@@ -103,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
105
|
version: 1.3.6
|
104
106
|
requirements: []
|
105
107
|
rubygems_version: 3.0.3
|
106
|
-
signing_key:
|
108
|
+
signing_key:
|
107
109
|
specification_version: 4
|
108
110
|
summary: Database migration generator for Rails
|
109
111
|
test_files: []
|