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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c00506fa5c0018170d2c1b919b76f0a334cd1216
4
- data.tar.gz: 6d8631cb529bbbeb52e53211f83dc27b9be94084
3
+ metadata.gz: 337cf9c69d98f33651b5a008c20f0d24648151ad
4
+ data.tar.gz: 3d604b4f812af63b98c0cc0de1820a0b61147eae
5
5
  SHA512:
6
- metadata.gz: 8fddcc3dcfee81c8dd7c9d2c812de2567307e30176ffc52b503218b30a0779d8a5668bce442990335f9d60e1817b28eb2ddfba8a164adb7828019cc1afbcf919
7
- data.tar.gz: 4a41f0963cc799e802518fc21a97b1583bc7978136e4ec28c49f6a7d5b1be642e6f4ea3e86cbfcdb9a6873bf04168b95111dfba4ce724ec1e5bf34c7fd6d5021
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.to_s
19
- string = string.encode(options[:encoding]) if requires_transcoding?(string)
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
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module DatabaseValidations
3
- VERSION = "0.2.2"
3
+ VERSION = "0.2.3"
4
4
  end
5
5
  end
@@ -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
- column_type = column.sql_type.sub(/\(.*\z/, '').gsub(/\s/, '_').to_sym
53
- type_limit = TYPE_LIMITS.fetch(column_type, {})
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
- unsigned = column.sql_type =~ / unsigned\z/
68
- case column.type
69
- when :decimal
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
- limit = StringTruncator.mysql_textual_column_limit(column)
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 column.type
17
- when :string
18
- value = self[field].to_s
19
- if limit && value.length > limit
20
- self[field] = value.slice(0, limit)
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.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-27 00:00:00.000000000 Z
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