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
@@ -47,11 +47,9 @@ module DeclareSchema
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def initialize(model, name, type, position: 0, **options)
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
# raise ArgumentError, "you cannot provide a field spec for the primary key" if name == model.primary_key
|
54
|
-
name == "id" and raise ArgumentError, "you cannot provide a field spec for the primary key"
|
50
|
+
_defined_primary_key = model._defined_primary_key
|
51
|
+
|
52
|
+
name.to_s == _defined_primary_key and raise ArgumentError, "you may not provide a field spec for the primary key #{name.inspect}"
|
55
53
|
|
56
54
|
@model = model
|
57
55
|
@name = name.to_sym
|
@@ -60,18 +58,21 @@ module DeclareSchema
|
|
60
58
|
@position = position
|
61
59
|
@options = options.dup
|
62
60
|
|
63
|
-
@options.has_key?(:null) or @options[:null] =
|
61
|
+
@options.has_key?(:null) or @options[:null] = ::DeclareSchema.default_null
|
62
|
+
@options[:null].nil? and raise "null: must be provided for field #{model}##{@name}: #{@options.inspect} since ::DeclareSchema#default_null is set to 'nil'; do you want `null: false`?"
|
64
63
|
|
65
64
|
case @type
|
66
65
|
when :text
|
67
66
|
if self.class.mysql_text_limits?
|
68
67
|
@options[:default].nil? or raise MysqlTextMayNotHaveDefault, "when using MySQL, non-nil default may not be given for :text field #{model}##{@name}"
|
69
|
-
@options[:limit]
|
68
|
+
@options[:limit] ||= ::DeclareSchema.default_text_limit or
|
69
|
+
raise("limit: must be provided for :text field #{model}##{@name}: #{@options.inspect} since ::DeclareSchema#default_text_limit is set to 'nil'; do you want `limit: 0xffff_ffff`?")
|
70
|
+
@options[:limit] = self.class.round_up_mysql_text_limit(@options[:limit])
|
70
71
|
else
|
71
72
|
@options.delete(:limit)
|
72
73
|
end
|
73
74
|
when :string
|
74
|
-
@options[:limit] or raise "limit: must be
|
75
|
+
@options[:limit] ||= ::DeclareSchema.default_string_limit or raise "limit: must be provided for :string field #{model}##{@name}: #{@options.inspect} since ::DeclareSchema#default_string_limit is set to 'nil'; do you want `limit: 255`?"
|
75
76
|
when :bigint
|
76
77
|
@type = :integer
|
77
78
|
@options[:limit] = 8
|
@@ -80,7 +81,7 @@ module DeclareSchema
|
|
80
81
|
Column.native_type?(@type) or raise UnknownTypeError, "#{@type.inspect} not found in #{Column.native_types.inspect} for adapter #{ActiveRecord::Base.connection.class.name}"
|
81
82
|
|
82
83
|
if @type.in?([:string, :text, :binary, :varbinary, :integer, :enum])
|
83
|
-
@options[:limit] ||= Column.native_types
|
84
|
+
@options[:limit] ||= Column.native_types.dig(@type, :limit)
|
84
85
|
else
|
85
86
|
@type != :decimal && @options.has_key?(:limit) and warn("unsupported limit: for SQL type #{@type} in field #{model}##{@name}")
|
86
87
|
@options.delete(:limit)
|
@@ -98,15 +99,15 @@ module DeclareSchema
|
|
98
99
|
|
99
100
|
if @type.in?([:text, :string])
|
100
101
|
if ActiveRecord::Base.connection.class.name.match?(/mysql/i)
|
101
|
-
@options[:charset] ||= model.table_options[:charset] ||
|
102
|
-
@options[:collation] ||= model.table_options[:collation] ||
|
102
|
+
@options[:charset] ||= model.table_options[:charset] || ::DeclareSchema.default_charset
|
103
|
+
@options[:collation] ||= model.table_options[:collation] || ::DeclareSchema.default_collation
|
103
104
|
else
|
104
105
|
@options.delete(:charset)
|
105
106
|
@options.delete(:collation)
|
106
107
|
end
|
107
108
|
else
|
108
109
|
@options[:charset] and warn("charset may only given for :string and :text fields for SQL type #{@type} in field #{model}##{@name}")
|
109
|
-
@options[:collation] and
|
110
|
+
@options[:collation] and warn("collation may only given for :string and :text fields for SQL type #{@type} in field #{model}##{@name}")
|
110
111
|
end
|
111
112
|
|
112
113
|
@options = Hash[@options.sort_by { |k, _v| OPTION_INDEXES[k] || 9999 }]
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'index_definition'
|
4
|
+
|
3
5
|
module DeclareSchema
|
4
6
|
module Model
|
5
7
|
class ForeignKeyDefinition
|
@@ -15,10 +17,10 @@ module DeclareSchema
|
|
15
17
|
@child_table = model.table_name # unless a table rename, which would happen when a class is renamed??
|
16
18
|
@parent_table_name = options[:parent_table]&.to_s
|
17
19
|
@foreign_key_name = options[:foreign_key]&.to_s || @foreign_key
|
18
|
-
@index_name = options[:index_name]&.to_s || model.connection.index_name(model.table_name, column: @foreign_key_name)
|
19
20
|
|
20
|
-
|
21
|
-
|
21
|
+
@constraint_name = options[:constraint_name]&.to_s ||
|
22
|
+
options[:index_name]&.to_s ||
|
23
|
+
IndexDefinition.index_name(@foreign_key_name)
|
22
24
|
@on_delete_cascade = options[:dependent] == :delete
|
23
25
|
end
|
24
26
|
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeclareSchema
|
4
|
+
module Model
|
5
|
+
class HabtmModelShim
|
6
|
+
class << self
|
7
|
+
def from_reflection(refl)
|
8
|
+
join_table = refl.join_table
|
9
|
+
foreign_keys_and_classes = [
|
10
|
+
[refl.foreign_key.to_s, refl.active_record],
|
11
|
+
[refl.association_foreign_key.to_s, refl.class_name.constantize]
|
12
|
+
].sort { |a, b| a.first <=> b.first }
|
13
|
+
foreign_keys = foreign_keys_and_classes.map(&:first)
|
14
|
+
foreign_key_classes = foreign_keys_and_classes.map(&:last)
|
15
|
+
# this may fail in weird ways if HABTM is running across two DB connections (assuming that's even supported)
|
16
|
+
# figure that anybody who sets THAT up can deal with their own migrations...
|
17
|
+
connection = refl.active_record.connection
|
18
|
+
|
19
|
+
new(join_table, foreign_keys, foreign_key_classes, connection)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :join_table, :foreign_keys, :foreign_key_classes, :connection
|
24
|
+
|
25
|
+
def initialize(join_table, foreign_keys, foreign_key_classes, connection)
|
26
|
+
@join_table = join_table
|
27
|
+
@foreign_keys = foreign_keys
|
28
|
+
@foreign_key_classes = foreign_key_classes
|
29
|
+
@connection = connection
|
30
|
+
end
|
31
|
+
|
32
|
+
def table_options
|
33
|
+
{}
|
34
|
+
end
|
35
|
+
|
36
|
+
def table_name
|
37
|
+
join_table
|
38
|
+
end
|
39
|
+
|
40
|
+
def field_specs
|
41
|
+
foreign_keys.each_with_index.each_with_object({}) do |(v, position), result|
|
42
|
+
result[v] = ::DeclareSchema::Model::FieldSpec.new(self, v, :integer, position: position, null: false)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def primary_key
|
47
|
+
false # no single-column primary key in database
|
48
|
+
end
|
49
|
+
|
50
|
+
def _defined_primary_key
|
51
|
+
false # no single-column primary key declared
|
52
|
+
end
|
53
|
+
|
54
|
+
def index_definitions_with_primary_key
|
55
|
+
[
|
56
|
+
IndexDefinition.new(self, foreign_keys, unique: true, name: ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME),
|
57
|
+
IndexDefinition.new(self, foreign_keys.last) # not unique by itself; combines with primary key to be unique
|
58
|
+
]
|
59
|
+
end
|
60
|
+
|
61
|
+
alias_method :index_definitions, :index_definitions_with_primary_key
|
62
|
+
|
63
|
+
def ignore_indexes
|
64
|
+
[]
|
65
|
+
end
|
66
|
+
|
67
|
+
def constraint_specs
|
68
|
+
[
|
69
|
+
ForeignKeyDefinition.new(self, foreign_keys.first, parent_table: foreign_key_classes.first.table_name, constraint_name: "#{join_table}_FK1", dependent: :delete),
|
70
|
+
ForeignKeyDefinition.new(self, foreign_keys.last, parent_table: foreign_key_classes.last.table_name, constraint_name: "#{join_table}_FK2", dependent: :delete)
|
71
|
+
]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -19,7 +19,7 @@ module DeclareSchema
|
|
19
19
|
@table = options.delete(:table_name) || model.table_name
|
20
20
|
@fields = Array.wrap(fields).map(&:to_s)
|
21
21
|
@explicit_name = options[:name] unless options.delete(:allow_equivalent)
|
22
|
-
@name = options.delete(:name) ||
|
22
|
+
@name = options.delete(:name) || self.class.index_name(@fields)
|
23
23
|
@unique = options.delete(:unique) || name == PRIMARY_KEY_NAME || false
|
24
24
|
|
25
25
|
if @name.length > MYSQL_INDEX_NAME_MAX_LENGTH
|
@@ -60,6 +60,10 @@ module DeclareSchema
|
|
60
60
|
index_definitions
|
61
61
|
end
|
62
62
|
|
63
|
+
def index_name(columns)
|
64
|
+
"on_#{Array(columns).join("_and_")}"
|
65
|
+
end
|
66
|
+
|
63
67
|
private
|
64
68
|
|
65
69
|
# This is the old approach which is still needed for MySQL in Rails 4 and SQLite
|
@@ -6,95 +6,17 @@ require 'active_record/connection_adapters/abstract_adapter'
|
|
6
6
|
module Generators
|
7
7
|
module DeclareSchema
|
8
8
|
module Migration
|
9
|
-
HabtmModelShim = Struct.new(:join_table, :foreign_keys, :foreign_key_classes, :connection) do
|
10
|
-
class << self
|
11
|
-
def from_reflection(refl)
|
12
|
-
join_table = refl.join_table
|
13
|
-
foreign_keys_and_classes = [
|
14
|
-
[refl.foreign_key.to_s, refl.active_record],
|
15
|
-
[refl.association_foreign_key.to_s, refl.class_name.constantize]
|
16
|
-
].sort { |a, b| a.first <=> b.first }
|
17
|
-
foreign_keys = foreign_keys_and_classes.map(&:first)
|
18
|
-
foreign_key_classes = foreign_keys_and_classes.map(&:last)
|
19
|
-
# this may fail in weird ways if HABTM is running across two DB connections (assuming that's even supported)
|
20
|
-
# figure that anybody who sets THAT up can deal with their own migrations...
|
21
|
-
connection = refl.active_record.connection
|
22
|
-
|
23
|
-
new(join_table, foreign_keys, foreign_key_classes, connection)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def table_options
|
28
|
-
{}
|
29
|
-
end
|
30
|
-
|
31
|
-
def table_name
|
32
|
-
join_table
|
33
|
-
end
|
34
|
-
|
35
|
-
def table_exists?
|
36
|
-
ActiveRecord::Migration.table_exists? table_name
|
37
|
-
end
|
38
|
-
|
39
|
-
def field_specs
|
40
|
-
i = 0
|
41
|
-
foreign_keys.each_with_object({}) do |v, result|
|
42
|
-
result[v] = ::DeclareSchema::Model::FieldSpec.new(self, v, :integer, position: i, null: false)
|
43
|
-
i += 1
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def primary_key
|
48
|
-
false # no single-column primary key
|
49
|
-
end
|
50
|
-
|
51
|
-
def index_definitions_with_primary_key
|
52
|
-
[
|
53
|
-
::DeclareSchema::Model::IndexDefinition.new(self, foreign_keys, unique: true, name: ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME),
|
54
|
-
::DeclareSchema::Model::IndexDefinition.new(self, foreign_keys.last) # not unique by itself; combines with primary key to be unique
|
55
|
-
]
|
56
|
-
end
|
57
|
-
|
58
|
-
alias_method :index_definitions, :index_definitions_with_primary_key
|
59
|
-
|
60
|
-
def ignore_indexes
|
61
|
-
[]
|
62
|
-
end
|
63
|
-
|
64
|
-
def constraint_specs
|
65
|
-
[
|
66
|
-
::DeclareSchema::Model::ForeignKeyDefinition.new(self, foreign_keys.first, parent_table: foreign_key_classes.first.table_name, constraint_name: "#{join_table}_FK1", dependent: :delete),
|
67
|
-
::DeclareSchema::Model::ForeignKeyDefinition.new(self, foreign_keys.last, parent_table: foreign_key_classes.last.table_name, constraint_name: "#{join_table}_FK2", dependent: :delete)
|
68
|
-
]
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
9
|
class Migrator
|
73
10
|
class Error < RuntimeError; end
|
74
11
|
|
75
|
-
DEFAULT_CHARSET = "utf8mb4"
|
76
|
-
DEFAULT_COLLATION = "utf8mb4_bin"
|
77
|
-
|
78
12
|
@ignore_models = []
|
79
13
|
@ignore_tables = []
|
80
14
|
@before_generating_migration_callback = nil
|
81
15
|
@active_record_class = ActiveRecord::Base
|
82
|
-
@default_charset = DEFAULT_CHARSET
|
83
|
-
@default_collation = DEFAULT_COLLATION
|
84
16
|
|
85
17
|
class << self
|
86
|
-
attr_accessor :ignore_models, :ignore_tables
|
87
|
-
attr_reader :active_record_class, :
|
88
|
-
|
89
|
-
def default_charset=(charset)
|
90
|
-
charset.is_a?(String) or raise ArgumentError, "charset must be a string (got #{charset.inspect})"
|
91
|
-
@default_charset = charset
|
92
|
-
end
|
93
|
-
|
94
|
-
def default_collation=(collation)
|
95
|
-
collation.is_a?(String) or raise ArgumentError, "collation must be a string (got #{collation.inspect})"
|
96
|
-
@default_collation = collation
|
97
|
-
end
|
18
|
+
attr_accessor :ignore_models, :ignore_tables
|
19
|
+
attr_reader :active_record_class, :before_generating_migration_callback
|
98
20
|
|
99
21
|
def active_record_class
|
100
22
|
@active_record_class.is_a?(Class) or @active_record_class = @active_record_class.to_s.constantize
|
@@ -121,6 +43,9 @@ module Generators
|
|
121
43
|
block or raise ArgumentError, 'A block is required when setting the before_generating_migration callback'
|
122
44
|
@before_generating_migration_callback = block
|
123
45
|
end
|
46
|
+
|
47
|
+
delegate :default_charset=, :default_collation=, :default_charset, :default_collation, to: ::DeclareSchema
|
48
|
+
deprecate :default_charset=, :default_collation=, :default_charset, :default_collation, deprecator: ActiveSupport::Deprecation.new('1.0', 'declare_schema')
|
124
49
|
end
|
125
50
|
|
126
51
|
def initialize(ambiguity_resolver = {})
|
@@ -266,7 +191,7 @@ module Generators
|
|
266
191
|
end
|
267
192
|
# generate shims for HABTM models
|
268
193
|
habtm_tables.each do |name, refls|
|
269
|
-
models_by_table_name[name] = HabtmModelShim.from_reflection(refls.first)
|
194
|
+
models_by_table_name[name] = ::DeclareSchema::Model::HabtmModelShim.from_reflection(refls.first)
|
270
195
|
end
|
271
196
|
model_table_names = models_by_table_name.keys
|
272
197
|
|
@@ -342,18 +267,19 @@ module Generators
|
|
342
267
|
end
|
343
268
|
|
344
269
|
#{table_options_definition.alter_table_statement unless ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/)}
|
345
|
-
#{create_indexes(model).join("\n")
|
346
|
-
#{create_constraints(model).join("\n")
|
270
|
+
#{create_indexes(model).join("\n") if ::DeclareSchema.default_generate_indexing}
|
271
|
+
#{create_constraints(model).join("\n") if ::DeclareSchema.default_generate_foreign_keys}
|
347
272
|
EOS
|
348
273
|
end
|
349
274
|
|
350
275
|
def create_table_options(model, disable_auto_increment)
|
351
|
-
|
276
|
+
primary_key = model._defined_primary_key
|
277
|
+
if primary_key.blank? || disable_auto_increment
|
352
278
|
"id: false"
|
353
|
-
elsif
|
279
|
+
elsif primary_key == "id"
|
354
280
|
"id: :bigint"
|
355
281
|
else
|
356
|
-
"primary_key: :#{
|
282
|
+
"primary_key: :#{primary_key}"
|
357
283
|
end
|
358
284
|
end
|
359
285
|
|
@@ -362,8 +288,8 @@ module Generators
|
|
362
288
|
{}
|
363
289
|
else
|
364
290
|
{
|
365
|
-
charset: model.table_options[:charset] ||
|
366
|
-
collation: model.table_options[:collation] ||
|
291
|
+
charset: model.table_options[:charset] || ::DeclareSchema.default_charset,
|
292
|
+
collation: model.table_options[:collation] || ::DeclareSchema.default_collation
|
367
293
|
}
|
368
294
|
end
|
369
295
|
end
|
@@ -386,18 +312,18 @@ module Generators
|
|
386
312
|
new_table_name = model.table_name
|
387
313
|
|
388
314
|
db_columns = model.connection.columns(current_table_name).index_by(&:name)
|
389
|
-
key_missing = db_columns[model.
|
390
|
-
if model.
|
391
|
-
db_columns.delete(model.
|
315
|
+
key_missing = db_columns[model._defined_primary_key].nil? && model._defined_primary_key.present?
|
316
|
+
if model._defined_primary_key.present?
|
317
|
+
db_columns.delete(model._defined_primary_key)
|
392
318
|
end
|
393
319
|
|
394
320
|
model_column_names = model.field_specs.keys.map(&:to_s)
|
395
321
|
db_column_names = db_columns.keys.map(&:to_s)
|
396
322
|
|
397
323
|
to_add = model_column_names - db_column_names
|
398
|
-
to_add += [model.
|
324
|
+
to_add += [model._defined_primary_key] if key_missing && model._defined_primary_key.present?
|
399
325
|
to_remove = db_column_names - model_column_names
|
400
|
-
to_remove -= [model.
|
326
|
+
to_remove -= [model._defined_primary_key.to_sym] if model._defined_primary_key.present?
|
401
327
|
|
402
328
|
to_rename = extract_column_renames!(to_add, to_remove, new_table_name)
|
403
329
|
|
@@ -477,7 +403,7 @@ module Generators
|
|
477
403
|
end
|
478
404
|
|
479
405
|
def change_indexes(model, old_table_name, to_remove)
|
480
|
-
|
406
|
+
::DeclareSchema.default_generate_indexing or return [[], []]
|
481
407
|
|
482
408
|
new_table_name = model.table_name
|
483
409
|
existing_indexes = ::DeclareSchema::Model::IndexDefinition.for_model(model, old_table_name)
|
@@ -518,7 +444,7 @@ module Generators
|
|
518
444
|
|
519
445
|
def change_foreign_key_constraints(model, old_table_name)
|
520
446
|
ActiveRecord::Base.connection.class.name.match?(/SQLite3Adapter/) and raise ArgumentError, 'SQLite does not support foreign keys'
|
521
|
-
|
447
|
+
::DeclareSchema.default_generate_foreign_keys or return [[], []]
|
522
448
|
|
523
449
|
new_table_name = model.table_name
|
524
450
|
existing_fks = ::DeclareSchema::Model::ForeignKeyDefinition.for_model(model, old_table_name)
|
@@ -647,8 +573,8 @@ module Generators
|
|
647
573
|
# TODO: rewrite this method to use charset and collation variables rather than manipulating strings. -Colin
|
648
574
|
def fix_mysql_charset_and_collation(dumped_schema)
|
649
575
|
if !dumped_schema['options: ']
|
650
|
-
dumped_schema.sub!('",', "\", options: \"DEFAULT CHARSET=#{
|
651
|
-
"COLLATE=#{
|
576
|
+
dumped_schema.sub!('",', "\", options: \"DEFAULT CHARSET=#{::DeclareSchema.default_charset} "+
|
577
|
+
"COLLATE=#{::DeclareSchema.default_collation}\",")
|
652
578
|
end
|
653
579
|
default_charset = dumped_schema[/CHARSET=(\w+)/, 1] or raise "unable to find charset in #{dumped_schema.inspect}"
|
654
580
|
default_collation = dumped_schema[/COLLATE=(\w+)/, 1] || default_collation_from_charset(default_charset) or
|
@@ -6,7 +6,7 @@ rescue LoadError
|
|
6
6
|
end
|
7
7
|
|
8
8
|
RSpec.describe DeclareSchema::Model::FieldSpec do
|
9
|
-
let(:model) { double('model', table_options: {}) }
|
9
|
+
let(:model) { double('model', table_options: {}, _defined_primary_key: 'id') }
|
10
10
|
let(:col_spec) { double('col_spec', type: :string) }
|
11
11
|
|
12
12
|
before do
|
@@ -61,6 +61,15 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
|
|
61
61
|
expect(subject.schema_attributes(col_spec)).to eq(type: :string, limit: 100, null: true)
|
62
62
|
end
|
63
63
|
end
|
64
|
+
|
65
|
+
it 'raises error when default_string_limit option is nil when not explicitly set in field spec' do
|
66
|
+
if defined?(Mysql2)
|
67
|
+
expect(::DeclareSchema).to receive(:default_string_limit) { nil }
|
68
|
+
expect do
|
69
|
+
described_class.new(model, :title, :string, null: true, charset: 'utf8mb4', position: 0)
|
70
|
+
end.to raise_error(/limit: must be provided for :string field/)
|
71
|
+
end
|
72
|
+
end
|
64
73
|
end
|
65
74
|
|
66
75
|
describe 'text' do
|
@@ -84,36 +93,66 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
|
|
84
93
|
end
|
85
94
|
end
|
86
95
|
|
87
|
-
describe '
|
88
|
-
it '
|
89
|
-
|
90
|
-
|
96
|
+
describe 'limit' do
|
97
|
+
it 'uses default_text_limit option when not explicitly set in field spec' do
|
98
|
+
allow(::DeclareSchema).to receive(:default_text_limit) { 100 }
|
99
|
+
subject = described_class.new(model, :title, :text, null: true, charset: 'utf8mb4', position: 2)
|
100
|
+
if defined?(Mysql2)
|
101
|
+
expect(subject.schema_attributes(col_spec)).to eq(type: :text, limit: 255, null: true, charset: 'utf8mb4', collation: 'utf8mb4_bin')
|
102
|
+
else
|
103
|
+
expect(subject.schema_attributes(col_spec)).to eq(type: :text, null: true)
|
104
|
+
end
|
91
105
|
end
|
92
106
|
|
93
|
-
it '
|
94
|
-
|
95
|
-
|
107
|
+
it 'raises error when default_text_limit option is nil when not explicitly set in field spec' do
|
108
|
+
if defined?(Mysql2)
|
109
|
+
expect(::DeclareSchema).to receive(:default_text_limit) { nil }
|
110
|
+
expect do
|
111
|
+
described_class.new(model, :title, :text, null: true, charset: 'utf8mb4', position: 2)
|
112
|
+
end.to raise_error(/limit: must be provided for :text field/)
|
113
|
+
end
|
96
114
|
end
|
115
|
+
end
|
116
|
+
end
|
97
117
|
|
98
|
-
|
99
|
-
|
100
|
-
|
118
|
+
if defined?(Mysql2)
|
119
|
+
describe 'varbinary' do # TODO: :varbinary is an Invoca addition to Rails; make it a configurable option
|
120
|
+
it 'is supported' do
|
121
|
+
subject = described_class.new(model, :binary_dump, :varbinary, limit: 200, null: false, position: 2)
|
122
|
+
expect(subject.schema_attributes(col_spec)).to eq(type: :varbinary, limit: 200, null: false)
|
101
123
|
end
|
102
124
|
end
|
125
|
+
end
|
103
126
|
|
104
|
-
|
105
|
-
|
106
|
-
|
127
|
+
describe 'decimal' do
|
128
|
+
it 'allows precision: and scale:' do
|
129
|
+
subject = described_class.new(model, :quantity, :decimal, precision: 8, scale: 10, null: true, position: 3)
|
130
|
+
expect(subject.schema_attributes(col_spec)).to eq(type: :decimal, precision: 8, scale: 10, null: true)
|
131
|
+
end
|
107
132
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
133
|
+
it 'requires precision:' do
|
134
|
+
expect_any_instance_of(described_class).to receive(:warn).with(/precision: required for :decimal type/)
|
135
|
+
described_class.new(model, :quantity, :decimal, scale: 10, null: true, position: 3)
|
136
|
+
end
|
112
137
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
138
|
+
it 'requires scale:' do
|
139
|
+
expect_any_instance_of(described_class).to receive(:warn).with(/scale: required for :decimal type/)
|
140
|
+
described_class.new(model, :quantity, :decimal, precision: 8, null: true, position: 3)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
[:integer, :bigint, :string, :text, :binary, :datetime, :date, :time, (:varbinary if defined?(Mysql2))].compact.each do |t|
|
145
|
+
describe t.to_s do
|
146
|
+
let(:extra) { t == :string ? { limit: 100 } : {} }
|
147
|
+
|
148
|
+
it 'does not allow precision:' do
|
149
|
+
expect_any_instance_of(described_class).to receive(:warn).with(/precision: only allowed for :decimal type/)
|
150
|
+
described_class.new(model, :quantity, t, { precision: 8, null: true, position: 3 }.merge(extra))
|
151
|
+
end unless t == :datetime
|
152
|
+
|
153
|
+
it 'does not allow scale:' do
|
154
|
+
expect_any_instance_of(described_class).to receive(:warn).with(/scale: only allowed for :decimal type/)
|
155
|
+
described_class.new(model, :quantity, t, { scale: 10, null: true, position: 3 }.merge(extra))
|
117
156
|
end
|
118
157
|
end
|
119
158
|
end
|
@@ -175,5 +214,17 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
|
|
175
214
|
it 'excludes non-sql options' do
|
176
215
|
expect(subject.sql_options).to eq(limit: 4, null: true, default: 0)
|
177
216
|
end
|
217
|
+
|
218
|
+
describe 'null' do
|
219
|
+
subject { described_class.new(model, :price, :integer, limit: 4, default: 0, position: 2, encrypt_using: ->(field) { field }) }
|
220
|
+
it 'uses default_null option when not explicitly set in field spec' do
|
221
|
+
expect(subject.sql_options).to eq(limit: 4, null: false, default: 0)
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'raises error if default_null is set to nil when not explicitly set in field spec' do
|
225
|
+
expect(::DeclareSchema).to receive(:default_null) { nil }
|
226
|
+
expect { subject.sql_options }.to raise_error(/null: must be provided for field/)
|
227
|
+
end
|
228
|
+
end
|
178
229
|
end
|
179
230
|
end
|