activerecord-databasevalidations 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/active_model/validations/bytesize.rb +2 -6
- data/lib/active_record/database_validations.rb +1 -0
- data/lib/active_record/database_validations/mysql.rb +74 -0
- data/lib/active_record/database_validations/varchar_191.rb +1 -0
- data/lib/active_record/database_validations/varchar_255.rb +1 -0
- data/lib/active_record/database_validations/version.rb +1 -1
- data/lib/active_record/validations/database_constraints.rb +10 -47
- data/lib/active_record/validations/string_truncator.rb +7 -25
- data/test/varchar_default_size_test.rb +20 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 337cf9c69d98f33651b5a008c20f0d24648151ad
|
4
|
+
data.tar.gz: 3d604b4f812af63b98c0cc0de1820a0b61147eae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6700a0056bb6dd20e32b6c584b21509b1f2531bec7c7c26eb03d4eaff7f118ad2485b62657c97ac1409d80c0ad080a0389b0dc60344a17ff5e9614612d619d35
|
7
|
+
data.tar.gz: 09cc0d7b8c4dffd6f32700e2575e66d74c012f9215db3da75bfda9587f297c8578e5c3c9a38260bcf29123cc6b90726abe5c11c52213fc36a0432db5980ec37e
|
@@ -15,8 +15,8 @@ module ActiveModel
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def validate_each(record, attribute, value)
|
18
|
-
string = value
|
19
|
-
|
18
|
+
string = ActiveRecord::DatabaseValidations::MySQL.value_for_column(value, options[:encoding])
|
19
|
+
|
20
20
|
if string.bytesize > options[:maximum]
|
21
21
|
errors_options = options.except(:too_many_bytes, :maximum)
|
22
22
|
default_message = options[:too_many_bytes]
|
@@ -25,10 +25,6 @@ module ActiveModel
|
|
25
25
|
record.errors.add(attribute, :too_many_bytes, errors_options)
|
26
26
|
end
|
27
27
|
end
|
28
|
-
|
29
|
-
def requires_transcoding?(value)
|
30
|
-
encoding.present? && encoding != value.encoding
|
31
|
-
end
|
32
28
|
end
|
33
29
|
|
34
30
|
module HelperMethods
|
@@ -2,6 +2,7 @@ require 'active_record'
|
|
2
2
|
require 'active_support/i18n'
|
3
3
|
require 'active_record/database_validations/version'
|
4
4
|
|
5
|
+
require 'active_record/database_validations/mysql'
|
5
6
|
require 'active_record/validations/database_constraints'
|
6
7
|
require 'active_record/validations/string_truncator'
|
7
8
|
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module DatabaseValidations
|
3
|
+
module MySQL
|
4
|
+
TYPE_LIMITS = {
|
5
|
+
char: { type: :characters },
|
6
|
+
varchar: { type: :characters },
|
7
|
+
varbinary: { type: :bytes },
|
8
|
+
|
9
|
+
tinytext: { type: :bytes, maximum: 2 ** 8 - 1 },
|
10
|
+
text: { type: :bytes, maximum: 2 ** 16 - 1 },
|
11
|
+
mediumtext: { type: :bytes, maximum: 2 ** 24 - 1 },
|
12
|
+
longtext: { type: :bytes, maximum: 2 ** 32 - 1 },
|
13
|
+
|
14
|
+
tinyblob: { type: :bytes, maximum: 2 ** 8 - 1 },
|
15
|
+
blob: { type: :bytes, maximum: 2 ** 16 - 1 },
|
16
|
+
mediumblob: { type: :bytes, maximum: 2 ** 24 - 1 },
|
17
|
+
longblob: { type: :bytes, maximum: 2 ** 32 - 1 },
|
18
|
+
}
|
19
|
+
|
20
|
+
def self.column_size_limit(column)
|
21
|
+
@column_size_limit ||= {}
|
22
|
+
@column_size_limit[column] ||= begin
|
23
|
+
column_type = column.sql_type.sub(/\(.*\z/, '').gsub(/\s/, '_').to_sym
|
24
|
+
type_limit = TYPE_LIMITS.fetch(column_type, {})
|
25
|
+
|
26
|
+
[
|
27
|
+
column.limit || type_limit[:maximum],
|
28
|
+
type_limit[:type],
|
29
|
+
determine_encoding(column),
|
30
|
+
]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.column_range(column)
|
35
|
+
args = {}
|
36
|
+
unsigned = column.sql_type =~ / unsigned\z/
|
37
|
+
case column.type
|
38
|
+
when :decimal
|
39
|
+
args[:less_than] = maximum = 10 ** (column.precision - column.scale)
|
40
|
+
if unsigned
|
41
|
+
args[:greater_than_or_equal_to] = 0
|
42
|
+
else
|
43
|
+
args[:greater_than] = 0 - maximum
|
44
|
+
end
|
45
|
+
|
46
|
+
when :integer
|
47
|
+
args[:only_integer] = true
|
48
|
+
args[:less_than] = unsigned ? 1 << (column.limit * 8) : 1 << (column.limit * 8 - 1)
|
49
|
+
args[:greater_than_or_equal_to] = unsigned ? 0 : 0 - args[:less_than]
|
50
|
+
end
|
51
|
+
|
52
|
+
args
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.determine_encoding(column)
|
56
|
+
return nil unless column.text?
|
57
|
+
case column.collation
|
58
|
+
when /\Autf8/; Encoding::UTF_8
|
59
|
+
else raise NotImplementedError, "Don't know how to determine the Ruby encoding for MySQL's #{column.collation} collation."
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.requires_transcoding?(value, column_encoding = nil)
|
64
|
+
column_encoding.present? && column_encoding != value.encoding
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.value_for_column(value, column_encoding = nil)
|
68
|
+
value = value.to_s unless value.is_a?(String)
|
69
|
+
value.encode!('utf-8') if requires_transcoding?(value, column_encoding)
|
70
|
+
return value
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::NATIVE_DATABASE_TYPES[:string][:limit] = 191
|
@@ -0,0 +1 @@
|
|
1
|
+
ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::NATIVE_DATABASE_TYPES[:string][:limit] = 255
|
@@ -5,26 +5,15 @@ require 'active_model/validations/basic_multilingual_plane'
|
|
5
5
|
module ActiveRecord
|
6
6
|
module Validations
|
7
7
|
class DatabaseConstraintsValidator < ActiveModel::EachValidator
|
8
|
-
TYPE_LIMITS = {
|
9
|
-
char: { validator: ActiveModel::Validations::LengthValidator },
|
10
|
-
varchar: { validator: ActiveModel::Validations::LengthValidator },
|
11
|
-
varbinary: { validator: ActiveModel::Validations::BytesizeValidator },
|
12
|
-
|
13
|
-
tinytext: { validator: ActiveModel::Validations::BytesizeValidator, default_maximum: 2 ** 8 - 1 },
|
14
|
-
text: { validator: ActiveModel::Validations::BytesizeValidator, default_maximum: 2 ** 16 - 1 },
|
15
|
-
mediumtext: { validator: ActiveModel::Validations::BytesizeValidator, default_maximum: 2 ** 24 - 1 },
|
16
|
-
longtext: { validator: ActiveModel::Validations::BytesizeValidator, default_maximum: 2 ** 32 - 1 },
|
17
|
-
|
18
|
-
tinyblob: { validator: ActiveModel::Validations::BytesizeValidator, default_maximum: 2 ** 8 - 1 },
|
19
|
-
blob: { validator: ActiveModel::Validations::BytesizeValidator, default_maximum: 2 ** 16 - 1 },
|
20
|
-
mediumblob: { validator: ActiveModel::Validations::BytesizeValidator, default_maximum: 2 ** 24 - 1 },
|
21
|
-
longblob: { validator: ActiveModel::Validations::BytesizeValidator, default_maximum: 2 ** 32 - 1 },
|
22
|
-
}
|
23
|
-
|
24
8
|
attr_reader :constraints
|
25
9
|
|
26
10
|
VALID_CONSTRAINTS = Set[:size, :basic_multilingual_plane, :not_null, :range]
|
27
11
|
|
12
|
+
SIZE_VALIDATORS_FOR_TYPE = {
|
13
|
+
characters: ActiveModel::Validations::LengthValidator,
|
14
|
+
bytes: ActiveModel::Validations::BytesizeValidator,
|
15
|
+
}
|
16
|
+
|
28
17
|
def initialize(options = {})
|
29
18
|
@constraints = Set.new(Array.wrap(options[:in]) + Array.wrap(options[:with]))
|
30
19
|
@constraint_validators = {}
|
@@ -49,11 +38,8 @@ module ActiveRecord
|
|
49
38
|
return unless constraints.include?(:size)
|
50
39
|
return unless column.text? || column.binary?
|
51
40
|
|
52
|
-
|
53
|
-
|
54
|
-
validator_class = type_limit[:validator]
|
55
|
-
maximum = column.limit || type_limit[:default_maximum]
|
56
|
-
encoding = column.text? ? determine_encoding(column) : nil
|
41
|
+
maximum, type, encoding = ActiveRecord::DatabaseValidations::MySQL.column_size_limit(column)
|
42
|
+
validator_class = SIZE_VALIDATORS_FOR_TYPE[type]
|
57
43
|
|
58
44
|
if validator_class && maximum
|
59
45
|
validator_class.new(attributes: [column.name.to_sym], class: klass, maximum: maximum, encoding: encoding)
|
@@ -64,23 +50,9 @@ module ActiveRecord
|
|
64
50
|
return unless constraints.include?(:range)
|
65
51
|
return unless column.number?
|
66
52
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
args = { attributes: [column.name.to_sym], class: klass, allow_nil: true }
|
71
|
-
args[:less_than] = maximum = 10 ** (column.precision - column.scale)
|
72
|
-
if unsigned
|
73
|
-
args[:greater_than_or_equal_to] = 0
|
74
|
-
else
|
75
|
-
args[:greater_than] = 0 - maximum
|
76
|
-
end
|
77
|
-
ActiveModel::Validations::NumericalityValidator.new(args)
|
78
|
-
|
79
|
-
when :integer
|
80
|
-
maximum = unsigned ? 1 << (column.limit * 8) : 1 << (column.limit * 8 - 1)
|
81
|
-
minimum = unsigned ? 0 : 0 - maximum
|
82
|
-
ActiveModel::Validations::NumericalityValidator.new(attributes: [column.name.to_sym], class: klass, greater_than_or_equal_to: minimum, less_than: maximum, allow_nil: true, only_integer: true)
|
83
|
-
end
|
53
|
+
args = { attributes: [column.name.to_sym], class: klass, allow_nil: true }
|
54
|
+
args.merge!(ActiveRecord::DatabaseValidations::MySQL.column_range(column))
|
55
|
+
ActiveModel::Validations::NumericalityValidator.new(args)
|
84
56
|
end
|
85
57
|
|
86
58
|
def basic_multilingual_plane_validator(klass, column)
|
@@ -107,15 +79,6 @@ module ActiveRecord
|
|
107
79
|
validator.validate_each(record, attribute, value)
|
108
80
|
end
|
109
81
|
end
|
110
|
-
|
111
|
-
private
|
112
|
-
|
113
|
-
def determine_encoding(column)
|
114
|
-
case column.collation
|
115
|
-
when /\Autf8/; Encoding.find('utf-8')
|
116
|
-
else raise NotImplementedError, "Don't know how to determine the Ruby encoding for MySQL's #{column.collation} collation."
|
117
|
-
end
|
118
|
-
end
|
119
82
|
end
|
120
83
|
end
|
121
84
|
end
|
@@ -11,21 +11,14 @@ module ActiveRecord
|
|
11
11
|
return if self[field].nil?
|
12
12
|
|
13
13
|
column = self.class.columns_hash[field.to_s]
|
14
|
-
|
14
|
+
maximum, type, encoding = ActiveRecord::DatabaseValidations::MySQL.column_size_limit(column)
|
15
|
+
value = ActiveRecord::DatabaseValidations::MySQL.value_for_column(self[field], encoding)
|
15
16
|
|
16
|
-
case
|
17
|
-
when :
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
when :text
|
24
|
-
value = self[field].to_s
|
25
|
-
value.encode!('utf-8') if value.encoding != Encoding::UTF_8
|
26
|
-
if limit && value.bytesize > limit
|
27
|
-
self[field] = value.mb_chars.limit(limit).to_s
|
28
|
-
end
|
17
|
+
case type
|
18
|
+
when :characters
|
19
|
+
self[field] = value.slice(0, maximum) if maximum && value.length > maximum
|
20
|
+
when :bytes
|
21
|
+
self[field] = value.mb_chars.limit(maximum).to_s if maximum && value.bytesize > maximum
|
29
22
|
end
|
30
23
|
|
31
24
|
return # to make sure the callback chain doesn't halt
|
@@ -33,17 +26,6 @@ module ActiveRecord
|
|
33
26
|
return method_name
|
34
27
|
end
|
35
28
|
end
|
36
|
-
|
37
|
-
def self.mysql_textual_column_limit(column)
|
38
|
-
@mysql_textual_column_limits ||= {}
|
39
|
-
@mysql_textual_column_limits[column] ||= begin
|
40
|
-
raise ArgumentError, "Only UTF-8 textual columns are supported." unless column.text? && column.collation =~ /\Autf8_/
|
41
|
-
|
42
|
-
column_type = column.sql_type.sub(/\(.*\z/, '').gsub(/\s/, '_').to_sym
|
43
|
-
type_limit = ActiveRecord::Validations::DatabaseConstraintsValidator::TYPE_LIMITS.fetch(column_type, {})
|
44
|
-
column.limit || type_limit[:default_maximum]
|
45
|
-
end
|
46
|
-
end
|
47
29
|
end
|
48
30
|
end
|
49
31
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'active_record/database_validations/varchar_191'
|
3
|
+
|
4
|
+
class Varchar < ActiveRecord::Base; end
|
5
|
+
|
6
|
+
class VarcharDefaultSizeTest < Minitest::Test
|
7
|
+
def test_field_was_created_with_191_characters
|
8
|
+
ActiveRecord::Migration.suppress_messages do
|
9
|
+
ActiveRecord::Migration.create_table(:varchars, force: true, options: "CHARACTER SET utf8mb4") do |t|
|
10
|
+
t.string :string
|
11
|
+
end
|
12
|
+
|
13
|
+
# This will fail if the field is more than 767 bytes.
|
14
|
+
ActiveRecord::Migration.add_index(:varchars, :string, unique: true)
|
15
|
+
end
|
16
|
+
|
17
|
+
assert_match /\Autf8mb4_/, Varchar.columns_hash['string'].collation
|
18
|
+
assert_equal 191, Varchar.columns_hash['string'].limit
|
19
|
+
end
|
20
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-databasevalidations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Willem van Bergen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -116,6 +116,9 @@ files:
|
|
116
116
|
- lib/active_model/validations/not_null.rb
|
117
117
|
- lib/active_record/database_validations.rb
|
118
118
|
- lib/active_record/database_validations/locale/en.yml
|
119
|
+
- lib/active_record/database_validations/mysql.rb
|
120
|
+
- lib/active_record/database_validations/varchar_191.rb
|
121
|
+
- lib/active_record/database_validations/varchar_255.rb
|
119
122
|
- lib/active_record/database_validations/version.rb
|
120
123
|
- lib/active_record/validations/database_constraints.rb
|
121
124
|
- lib/active_record/validations/string_truncator.rb
|
@@ -129,6 +132,7 @@ files:
|
|
129
132
|
- test/not_null_validator_test.rb
|
130
133
|
- test/string_truncator_test.rb
|
131
134
|
- test/test_helper.rb
|
135
|
+
- test/varchar_default_size_test.rb
|
132
136
|
homepage: https://github.com/wvanbergen/activerecord-database_validations
|
133
137
|
licenses:
|
134
138
|
- MIT
|
@@ -162,3 +166,4 @@ test_files:
|
|
162
166
|
- test/not_null_validator_test.rb
|
163
167
|
- test/string_truncator_test.rb
|
164
168
|
- test/test_helper.rb
|
169
|
+
- test/varchar_default_size_test.rb
|