declare_schema 0.8.0.pre.3 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +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
|