activerecord-databasevalidations 0.2.2 → 0.2.3

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