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
@@ -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
|