declare_schema 0.8.0.pre.3 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -1
- data/Gemfile.lock +1 -1
- data/README.md +90 -12
- data/lib/declare_schema.rb +46 -0
- data/lib/declare_schema/extensions/active_record/fields_declaration.rb +1 -1
- data/lib/declare_schema/model.rb +52 -37
- data/lib/declare_schema/model/column.rb +2 -0
- data/lib/declare_schema/model/field_spec.rb +13 -12
- data/lib/declare_schema/model/foreign_key_definition.rb +5 -3
- data/lib/declare_schema/model/habtm_model_shim.rb +75 -0
- data/lib/declare_schema/model/index_definition.rb +5 -1
- data/lib/declare_schema/version.rb +1 -1
- data/lib/generators/declare_schema/migration/migrator.rb +23 -97
- data/spec/lib/declare_schema/field_spec_spec.rb +73 -22
- data/spec/lib/declare_schema/interactive_primary_key_spec.rb +3 -3
- data/spec/lib/declare_schema/migration_generator_spec.rb +28 -28
- data/spec/lib/declare_schema/model/habtm_model_shim_spec.rb +148 -0
- data/spec/lib/declare_schema/model/index_definition_spec.rb +11 -1
- data/spec/lib/declare_schema_spec.rb +101 -0
- data/spec/lib/generators/declare_schema/migration/migrator_spec.rb +12 -2
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 531940d48f1fe38830944576136ff76e2b29dc69a323a2a8e3f60f431731f104
|
4
|
+
data.tar.gz: 3afe0ce91fa631f4fa07d10ef1039f788589d440e87245c859db9bbe0179972d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 67270e128e00f45b8ed8393b85ffe65ab4bc7ae197296adacf22c37629bdf5e768d6e204987b67e2d63b3ac0cc79aadb0b02c2c4f60e7e4b85e01f4d24ef55e2
|
7
|
+
data.tar.gz: c9763a6eacd9d1d1deef9e829072e974ea53a5575f0938a8b4202e4210aa704186022a12e8704c08edde24bc73c2da7f95b467b77f3c81e422df54eddaf2352a
|
data/CHANGELOG.md
CHANGED
@@ -4,8 +4,18 @@ 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.
|
7
|
+
## [0.9.0] - 2021-03-01
|
8
|
+
### Added
|
9
|
+
- Added configurable default settings for `default_text_limit`, `default_string_limit`, `default_null`,
|
10
|
+
`default_generate_foreign_keys` and `default_generate_indexing` to allow developers to adhere to project conventions.
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
- Moved and deprecated default settings for `default_charset` and `default_collation` from
|
14
|
+
`Generators::DeclareSchema::Migration::Migrator` to `::DeclareSchema`
|
15
|
+
|
16
|
+
## [0.8.0] - 2021-02-22
|
8
17
|
### Removed
|
18
|
+
- Removed assumption that primary key is named 'id'.
|
9
19
|
- Removed `sql_type` that was confusing because it was actually the same as `type` (ex: :string) and not
|
10
20
|
in fact the SQL type (ex: ``varchar(255)'`).
|
11
21
|
|
@@ -130,6 +140,7 @@ using the appropriate Rails configuration attributes.
|
|
130
140
|
### Added
|
131
141
|
- Initial version from https://github.com/Invoca/hobo_fields v4.1.0.
|
132
142
|
|
143
|
+
[0.9.0]: https://github.com/Invoca/declare_schema/compare/v0.8.0...v0.9.0
|
133
144
|
[0.8.0]: https://github.com/Invoca/declare_schema/compare/v0.7.1...v0.8.0
|
134
145
|
[0.7.1]: https://github.com/Invoca/declare_schema/compare/v0.7.0...v0.7.1
|
135
146
|
[0.7.0]: https://github.com/Invoca/declare_schema/compare/v0.6.3...v0.7.0
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -70,18 +70,85 @@ DeclareSchema::Migration::Migrator.before_generating_migration do
|
|
70
70
|
end
|
71
71
|
```
|
72
72
|
|
73
|
-
|
74
|
-
|
73
|
+
### Global Configuration
|
74
|
+
Configurations can be set at the global level to customize default declaration for the following values:
|
75
75
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
tables and columns in the database. With `declare_schema` this can be configured
|
82
|
-
at three separate levels
|
76
|
+
#### Text Limit
|
77
|
+
The default text limit can be set using the `DeclareSchema.default_text_limit=` method.
|
78
|
+
Note that a `nil` default means that there is no default-- so every declaration must be explicit.
|
79
|
+
This will `raise` a `limit: must be provided for field :text...` error when the default value is `nil` and there is no explicit
|
80
|
+
declaration.
|
83
81
|
|
84
|
-
|
82
|
+
For example, adding the following to your `config/initializers` directory will
|
83
|
+
set the default `text limit` value to `0xffff`:
|
84
|
+
|
85
|
+
**declare_schema.rb**
|
86
|
+
```ruby
|
87
|
+
# frozen_string_literal: true
|
88
|
+
|
89
|
+
DeclareSchema.default_text_limit = 0xffff
|
90
|
+
```
|
91
|
+
|
92
|
+
#### String Limit
|
93
|
+
The default string limit can be set using the `DeclareSchema.default_string_limit=` method.
|
94
|
+
Note that a `nil` default means that there is no default-- so every declaration must be explicit.
|
95
|
+
This will `raise` a `limit: must be provided for field :string...` error when the default value is `nil` and there is no explicit
|
96
|
+
declaration.
|
97
|
+
|
98
|
+
For example, adding the following to your `config/initializers` directory will
|
99
|
+
set the default `string limit` value to `255`:
|
100
|
+
|
101
|
+
**declare_schema.rb**
|
102
|
+
```ruby
|
103
|
+
# frozen_string_literal: true
|
104
|
+
|
105
|
+
DeclareSchema.default_string_limit = 255
|
106
|
+
```
|
107
|
+
|
108
|
+
#### Null
|
109
|
+
The default null value can be set using the `DeclareSchema.default_null=` method.
|
110
|
+
Note that a `nil` default means that there is no default-- so every declaration must be explicit.
|
111
|
+
This will `raise` a `null: must be provided for field...` error when the default value is `nil` and there is no explicit
|
112
|
+
declaration.
|
113
|
+
|
114
|
+
For example, adding the following to your `config/initializers` directory will
|
115
|
+
set the default `null` value to `true`:
|
116
|
+
|
117
|
+
**declare_schema.rb**
|
118
|
+
```ruby
|
119
|
+
# frozen_string_literal: true
|
120
|
+
|
121
|
+
DeclareSchema.default_null = true
|
122
|
+
```
|
123
|
+
|
124
|
+
#### Generate Foreign Keys
|
125
|
+
The default value for generate foreign keys can be set using the `DeclareSchema.default_generate_foreign_keys=` method.
|
126
|
+
This value defaults to `true` and can only be set at the global level.
|
127
|
+
|
128
|
+
For example, adding the following to your `config/initializers` directory will set
|
129
|
+
the default `generate foreign keys` value to `false`:
|
130
|
+
|
131
|
+
**declare_schema.rb**
|
132
|
+
```ruby
|
133
|
+
# frozen_string_literal: true
|
134
|
+
|
135
|
+
DeclareSchema.default_generate_foreign_keys = false
|
136
|
+
```
|
137
|
+
|
138
|
+
#### Generate Indexing
|
139
|
+
The default value for generate indexing can be set using the `DeclareSchema.default_generate_indexing=` method.
|
140
|
+
This value defaults to `true` and can only be set at the global level.
|
141
|
+
|
142
|
+
For example, adding the following to your `config/initializers` directory will
|
143
|
+
set the default `generate indexing` value to `false`:
|
144
|
+
|
145
|
+
**declare_schema.rb**
|
146
|
+
```ruby
|
147
|
+
# frozen_string_literal: true
|
148
|
+
|
149
|
+
DeclareSchema.default_generate_indexing = false
|
150
|
+
```
|
151
|
+
#### Character Set and Collation
|
85
152
|
The character set and collation for all tables and fields can be set at the global level
|
86
153
|
using the `Generators::DeclareSchema::Migrator.default_charset=` and
|
87
154
|
`Generators::DeclareSchema::Migrator.default_collation=` configuration methods.
|
@@ -93,10 +160,21 @@ turn all tables into `utf8mb4` supporting tables:
|
|
93
160
|
```ruby
|
94
161
|
# frozen_string_literal: true
|
95
162
|
|
96
|
-
|
97
|
-
|
163
|
+
DeclareSchema.default_charset = "utf8mb4"
|
164
|
+
DeclareSchema.default_collation = "utf8mb4_bin"
|
98
165
|
```
|
99
166
|
|
167
|
+
## Declaring Character Set and Collation
|
168
|
+
_Note: This feature currently only works for MySQL database configurations._
|
169
|
+
|
170
|
+
MySQL originally supported UTF-8 in the range of 1-3 bytes (`mb3` or "multi-byte 3")
|
171
|
+
which covered the full set of Unicode code points at the time: U+0000 - U+FFFF.
|
172
|
+
But later, Unicode was extended beyond U+FFFF to make room for emojis, and with that
|
173
|
+
UTF-8 require 1-4 bytes (`mb4` or "multi-byte 4"). With this addition, there has
|
174
|
+
come a need to dynamically define the character set and collation for individual
|
175
|
+
tables and columns in the database. With `declare_schema` this can be configured
|
176
|
+
at three separate levels
|
177
|
+
|
100
178
|
### Table Configuration
|
101
179
|
In order to configure a table's default character set and collation, the `charset` and
|
102
180
|
`collation` arguments can be added to the `fields` block.
|
data/lib/declare_schema.rb
CHANGED
@@ -21,7 +21,18 @@ module DeclareSchema
|
|
21
21
|
text: String
|
22
22
|
}.freeze
|
23
23
|
|
24
|
+
@default_charset = "utf8mb4"
|
25
|
+
@default_collation = "utf8mb4_bin"
|
26
|
+
@default_text_limit = 0xffff_ffff
|
27
|
+
@default_string_limit = nil
|
28
|
+
@default_null = false
|
29
|
+
@default_generate_foreign_keys = true
|
30
|
+
@default_generate_indexing = true
|
31
|
+
|
24
32
|
class << self
|
33
|
+
attr_reader :default_charset, :default_collation, :default_text_limit, :default_string_limit, :default_null,
|
34
|
+
:default_generate_foreign_keys, :default_generate_indexing
|
35
|
+
|
25
36
|
def to_class(type)
|
26
37
|
case type
|
27
38
|
when Class
|
@@ -32,6 +43,41 @@ module DeclareSchema
|
|
32
43
|
raise ArgumentError, "expected Class or Symbol or String: got #{type.inspect}"
|
33
44
|
end
|
34
45
|
end
|
46
|
+
|
47
|
+
def default_charset=(charset)
|
48
|
+
charset.is_a?(String) or raise ArgumentError, "charset must be a string (got #{charset.inspect})"
|
49
|
+
@default_charset = charset
|
50
|
+
end
|
51
|
+
|
52
|
+
def default_collation=(collation)
|
53
|
+
collation.is_a?(String) or raise ArgumentError, "collation must be a string (got #{collation.inspect})"
|
54
|
+
@default_collation = collation
|
55
|
+
end
|
56
|
+
|
57
|
+
def default_text_limit=(text_limit)
|
58
|
+
text_limit.nil? or text_limit.is_a?(Integer) or raise ArgumentError, "text limit must be an integer or nil (got #{text_limit.inspect})"
|
59
|
+
@default_text_limit = text_limit
|
60
|
+
end
|
61
|
+
|
62
|
+
def default_string_limit=(string_limit)
|
63
|
+
string_limit.nil? or string_limit.is_a?(Integer) or raise ArgumentError, "string limit must be an integer or nil (got #{string_limit.inspect})"
|
64
|
+
@default_string_limit = string_limit
|
65
|
+
end
|
66
|
+
|
67
|
+
def default_null=(null)
|
68
|
+
null.in?([true, false, nil]) or raise ArgumentError, "null must be either true, false, or nil (got #{null.inspect})"
|
69
|
+
@default_null = null
|
70
|
+
end
|
71
|
+
|
72
|
+
def default_generate_foreign_keys=(generate_foreign_keys)
|
73
|
+
generate_foreign_keys.in?([true, false]) or raise ArgumentError, "generate_foreign_keys must be either true or false (got #{generate_foreign_keys.inspect})"
|
74
|
+
@default_generate_foreign_keys = generate_foreign_keys
|
75
|
+
end
|
76
|
+
|
77
|
+
def default_generate_indexing=(generate_indexing)
|
78
|
+
generate_indexing.in?([true, false]) or raise ArgumentError, "generate_indexing must be either true or false (got #{generate_indexing.inspect})"
|
79
|
+
@default_generate_indexing = generate_indexing
|
80
|
+
end
|
35
81
|
end
|
36
82
|
end
|
37
83
|
|
data/lib/declare_schema/model.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'rails'
|
4
|
+
|
3
5
|
require 'declare_schema/extensions/module'
|
4
6
|
|
5
7
|
module DeclareSchema
|
@@ -36,7 +38,7 @@ module DeclareSchema
|
|
36
38
|
|
37
39
|
# eval avoids the ruby 1.9.2 "super from singleton method ..." error
|
38
40
|
|
39
|
-
eval
|
41
|
+
eval <<~EOS
|
40
42
|
def self.inherited(klass)
|
41
43
|
unless klass.field_specs.has_key?(inheritance_column)
|
42
44
|
fields do |f|
|
@@ -46,14 +48,14 @@ module DeclareSchema
|
|
46
48
|
end
|
47
49
|
super
|
48
50
|
end
|
49
|
-
|
51
|
+
EOS
|
50
52
|
end
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
54
56
|
module ClassMethods
|
55
57
|
def index(fields, options = {})
|
56
|
-
#
|
58
|
+
# make index idempotent
|
57
59
|
index_fields_s = Array.wrap(fields).map(&:to_s)
|
58
60
|
unless index_definitions.any? { |index_spec| index_spec.fields == index_fields_s }
|
59
61
|
index_definitions << ::DeclareSchema::Model::IndexDefinition.new(self, fields, options)
|
@@ -72,7 +74,7 @@ module DeclareSchema
|
|
72
74
|
end
|
73
75
|
|
74
76
|
# tell the migration generator to ignore the named index. Useful for existing indexes, or for indexes
|
75
|
-
# that can't be automatically generated (for example:
|
77
|
+
# that can't be automatically generated (for example: a prefix index in MySQL)
|
76
78
|
def ignore_index(index_name)
|
77
79
|
ignore_indexes << index_name.to_s
|
78
80
|
end
|
@@ -83,10 +85,10 @@ module DeclareSchema
|
|
83
85
|
# declarations.
|
84
86
|
def declare_field(name, type, *args, **options)
|
85
87
|
try(:field_added, name, type, args, options)
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
88
|
+
_add_serialize_for_field(name, type, options)
|
89
|
+
_add_formatting_for_field(name, type)
|
90
|
+
_add_validations_for_field(name, type, args, options)
|
91
|
+
_add_index_for_field(name, args, options)
|
90
92
|
field_specs[name] = ::DeclareSchema::Model::FieldSpec.new(self, name, type, position: field_specs.size, **options)
|
91
93
|
attr_order << name unless attr_order.include?(name)
|
92
94
|
end
|
@@ -95,20 +97,10 @@ module DeclareSchema
|
|
95
97
|
if index_definitions.any?(&:primary_key?)
|
96
98
|
index_definitions
|
97
99
|
else
|
98
|
-
index_definitions + [
|
100
|
+
index_definitions + [_rails_default_primary_key]
|
99
101
|
end
|
100
102
|
end
|
101
103
|
|
102
|
-
def primary_key
|
103
|
-
super || 'id'
|
104
|
-
end
|
105
|
-
|
106
|
-
private
|
107
|
-
|
108
|
-
def rails_default_primary_key
|
109
|
-
::DeclareSchema::Model::IndexDefinition.new(self, [primary_key.to_sym], unique: true, name: DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME)
|
110
|
-
end
|
111
|
-
|
112
104
|
# Extend belongs_to so that it creates a FieldSpec for the foreign key
|
113
105
|
def belongs_to(name, scope = nil, **options)
|
114
106
|
column_options = {}
|
@@ -149,7 +141,7 @@ module DeclareSchema
|
|
149
141
|
declare_field(fkey.to_sym, :integer, column_options)
|
150
142
|
if refl.options[:polymorphic]
|
151
143
|
foreign_type = options[:foreign_type] || "#{name}_type"
|
152
|
-
|
144
|
+
_declare_polymorphic_type_field(foreign_type, column_options)
|
153
145
|
index([foreign_type, fkey], index_options) if index_options[:name] != false
|
154
146
|
else
|
155
147
|
index(fkey, index_options) if index_options[:name] != false
|
@@ -157,26 +149,49 @@ module DeclareSchema
|
|
157
149
|
end
|
158
150
|
end
|
159
151
|
|
152
|
+
if ::Rails::VERSION::MAJOR < 5
|
153
|
+
def primary_key
|
154
|
+
super || 'id'
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# returns the primary key (String) as declared with primary_key =
|
159
|
+
# unlike the `primary_key` method, DOES NOT query the database to find the actual primary key in use right now
|
160
|
+
# if no explicit primary key set, returns the default_defined_primary_key
|
161
|
+
def _defined_primary_key
|
162
|
+
if defined?(@primary_key)
|
163
|
+
@primary_key&.to_s
|
164
|
+
end || _default_defined_primary_key
|
165
|
+
end
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
# if this is a derived class, returns the base class's _defined_primary_key
|
170
|
+
# otherwise, returns 'id'
|
171
|
+
def _default_defined_primary_key
|
172
|
+
if self == base_class
|
173
|
+
'id'
|
174
|
+
else
|
175
|
+
base_class._defined_primary_key
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def _rails_default_primary_key
|
180
|
+
::DeclareSchema::Model::IndexDefinition.new(self, [_defined_primary_key.to_sym], unique: true, name: DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME)
|
181
|
+
end
|
182
|
+
|
160
183
|
# Declares the "foo_type" field that accompanies the "foo_id"
|
161
184
|
# field for a polymorphic belongs_to
|
162
|
-
def
|
185
|
+
def _declare_polymorphic_type_field(foreign_type, column_options)
|
163
186
|
declare_field(foreign_type, :string, column_options.merge(limit: 255))
|
164
187
|
# FIXME: Before declare_schema was extracted, this used to now do:
|
165
188
|
# never_show(type_col)
|
166
189
|
# That needs doing somewhere
|
167
190
|
end
|
168
191
|
|
169
|
-
# Declare a rich-type for any attribute (i.e. getter method). This
|
170
|
-
# does not effect the attribute in any way - it just records the
|
171
|
-
# metadata.
|
172
|
-
def declare_attr_type(name, type, options = {})
|
173
|
-
attr_types[name] = klass = DeclareSchema.to_class(type)
|
174
|
-
klass.try(:declared, self, name, options)
|
175
|
-
end
|
176
|
-
|
177
192
|
# Add field validations according to arguments in the
|
178
193
|
# field declaration
|
179
|
-
def
|
194
|
+
def _add_validations_for_field(name, type, args, options)
|
180
195
|
validates_presence_of name if :required.in?(args)
|
181
196
|
validates_uniqueness_of name, allow_nil: !:required.in?(args) if :unique.in?(args)
|
182
197
|
|
@@ -195,18 +210,18 @@ module DeclareSchema
|
|
195
210
|
end
|
196
211
|
end
|
197
212
|
|
198
|
-
def
|
213
|
+
def _add_serialize_for_field(name, type, options)
|
199
214
|
if (serialize_class = options.delete(:serialize))
|
200
215
|
type == :string || type == :text or raise ArgumentError, "serialize field type must be :string or :text"
|
201
216
|
serialize_args = Array((serialize_class unless serialize_class == true))
|
202
217
|
serialize(name, *serialize_args)
|
203
218
|
if options.has_key?(:default)
|
204
|
-
options[:default] =
|
219
|
+
options[:default] = _serialized_default(name, serialize_class == true ? Object : serialize_class, options[:default])
|
205
220
|
end
|
206
221
|
end
|
207
222
|
end
|
208
223
|
|
209
|
-
def
|
224
|
+
def _serialized_default(attr_name, class_name_or_coder, default)
|
210
225
|
# copied from https://github.com/rails/rails/blob/7d6cb950e7c0e31c2faaed08c81743439156c9f5/activerecord/lib/active_record/attribute_methods/serialization.rb#L70-L76
|
211
226
|
coder = if class_name_or_coder == ::JSON
|
212
227
|
ActiveRecord::Coders::JSON
|
@@ -225,7 +240,7 @@ module DeclareSchema
|
|
225
240
|
end
|
226
241
|
end
|
227
242
|
|
228
|
-
def
|
243
|
+
def _add_formatting_for_field(name, type)
|
229
244
|
if (type_class = DeclareSchema.to_class(type))
|
230
245
|
if "format".in?(type_class.instance_methods)
|
231
246
|
before_validation do |record|
|
@@ -235,7 +250,7 @@ module DeclareSchema
|
|
235
250
|
end
|
236
251
|
end
|
237
252
|
|
238
|
-
def
|
253
|
+
def _add_index_for_field(name, args, options)
|
239
254
|
if (to_name = options.delete(:index))
|
240
255
|
index_opts =
|
241
256
|
{
|
@@ -264,13 +279,13 @@ module DeclareSchema
|
|
264
279
|
refl
|
265
280
|
end
|
266
281
|
end ||
|
267
|
-
if (col =
|
282
|
+
if (col = _column(name.to_s))
|
268
283
|
DeclareSchema::PLAIN_TYPES[col.type] || col.klass
|
269
284
|
end
|
270
285
|
end
|
271
286
|
|
272
287
|
# Return the entry from #columns for the named column
|
273
|
-
def
|
288
|
+
def _column(name)
|
274
289
|
defined?(@table_exists) or @table_exists = table_exists?
|
275
290
|
if @table_exists
|
276
291
|
columns_hash[name.to_s]
|
@@ -44,6 +44,8 @@ module DeclareSchema
|
|
44
44
|
if ActiveRecord::Base.connection.class.name.match?(/mysql/i)
|
45
45
|
types[:text][:limit] ||= 0xffff
|
46
46
|
types[:binary][:limit] ||= 0xffff
|
47
|
+
|
48
|
+
types[:varbinary] ||= { name: "varbinary" } # TODO: :varbinary is an Invoca addition to Rails; make it a configurable option
|
47
49
|
end
|
48
50
|
end
|
49
51
|
end
|