activerecord-databasevalidations 0.1.3 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 090f45578b6802cdbdba1069b5d37a9a36936e53
4
- data.tar.gz: 8804b23a035aaa84fa334caa69fa862971bf0ffc
3
+ metadata.gz: e4aea1bc84aef567070fd2cb3febe2bdb6895daf
4
+ data.tar.gz: 2e8257b79b80d5b84d791b3ec7dfcffe3d115ea4
5
5
  SHA512:
6
- metadata.gz: 48f4ce81a05aa5798f73b24997eea4920027318e4c48cdae4824c806a3cee5c84688058f204af0bf074b29360037108939bca38c0389b52f18cd3d3fd52ab941
7
- data.tar.gz: 33774245ec628ff1b463559346e3e11699e302b4a41417676caf42ad08b5a8f07f3f613fb0d724c41edd883e7b51b90a660889e75cf4134d9e9526b909c8c438
6
+ metadata.gz: f7359255dd4cd2ad194d62aca4c6b7c19ffb26c999cb77a8d453b4fc28f7617f4bb4b30e520180a7a3cbf190fccccd08d31ca0d3e776d2d153ef6b4dcb9d41ff
7
+ data.tar.gz: 320f2a3744403023282bb9399fcc720a6592913142283dd8b3c3ec9e82146de6e7974e78b9678c4cc2e1ac924eb250f9acbe8adb70d9dcb56257b0d492bf96b8
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # ActiveRecord::DatabaseValidations
2
2
 
3
- Add validations to your ActiveRecord models based on your database constraints.
3
+ Add validations to your ActiveRecord models based on your database constraints.
4
4
 
5
- This gem is primarily intended for MySQL databases not running in strict mode,
6
- which can easily cause data loss. These problems are documented in
5
+ This gem is primarily intended for MySQL databases not running in strict mode,
6
+ which can easily cause data loss. These problems are documented in
7
7
  [DataLossTest](https://github.com/wvanbergen/activerecord-databasevalidations/blob/master/test/data_loss_test.rb)
8
8
 
9
9
  ## Installation
@@ -29,6 +29,7 @@ to validate based on the database constraints.
29
29
  class Foo < ActiveRecord::Base
30
30
  validates :boolean_field, database_constraints: :not_null
31
31
  validates :string_field, database_constraints: [:size, :basic_multilingual_plane]
32
+ validates :decimal_field, :integer_field, database_constraints: :range
32
33
  end
33
34
  ```
34
35
 
@@ -44,11 +45,12 @@ end
44
45
 
45
46
  You have to specify what conatrints you want to validate for. Valid values are:
46
47
 
47
- - `:size` to validate for the size of textual and binary columns. It will pick character
48
+ - `:range` to validate the numeric range of a column based on it's type.
49
+ - `:size` to validate for the size of textual and binary columns. It will pick character
48
50
  size or bytesize based on the column's type.
49
- - `:not_null` to validate a NOT NULL contraint.
50
- - `:basic_multilingual_plane` to validate that all characters for text fields are inside
51
+ - `:basic_multilingual_plane` to validate that all characters for text fields are inside
51
52
  the basic multilingual plane of unicode (unless you use the utf8mb4 character set).
53
+ - `:not_null` to validate a NOT NULL contraint.
52
54
 
53
55
  The validations will only be created if it makes sense for the column, e.g. a `:not_null`
54
56
  validation will only be added if the column has a NOT NULL constraint defined on it.
@@ -64,9 +66,31 @@ class Bar < ActiveRecord::Base
64
66
  end
65
67
  ```
66
68
 
67
- Note that this will create validations without inspecting the column to see if it
69
+ Note that this will create validations without inspecting the column to see if it
68
70
  actually makes sense.
69
71
 
72
+ ### Replicating MySQL's truncation behavior
73
+
74
+ Sometimes, truncated a string that goes over the column's limit is the best option, if
75
+ you don't want one field's value being too long prevent the record from saving.
76
+
77
+ You can use `truncate_string` to replicate MySQL's non-strict truncating behavior, so
78
+ you can prepare yourself for eventually turning on strict mode.
79
+
80
+
81
+ ``` ruby
82
+ class Unicorn < ActiveRecord::Base
83
+ include ActiveRecord::DatabaseValidations::StringTruncator
84
+
85
+ before_validation truncate_string(:string_field)
86
+ validates :string_field, database_constraints: [:size]
87
+ end
88
+ ```
89
+
90
+ In this example, it will truncate the string to a size that will fit before validation,
91
+ so the subsequent size validation will now always pass.
92
+
93
+
70
94
  ## Contributing
71
95
 
72
96
  1. Fork it (http://github.com/wvanbergen/activerecord-databasevalidations/fork)
@@ -3,5 +3,6 @@ require 'active_support/i18n'
3
3
  require 'active_record/database_validations/version'
4
4
 
5
5
  require 'active_record/validations/database_constraints'
6
+ require 'active_record/validations/string_truncator'
6
7
 
7
8
  I18n.load_path << File.dirname(__FILE__) + '/database_validations/locale/en.yml'
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module DatabaseValidations
3
- VERSION = "0.1.3"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -0,0 +1,49 @@
1
+ module ActiveRecord
2
+ module DatabaseValidations
3
+ module StringTruncator
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def truncate_string(field)
8
+ column = self.columns_hash[field.to_s]
9
+ case column.type
10
+ when :string
11
+ lambda do
12
+ return unless self.changes.key?(field.to_s)
13
+ return if self[field].nil?
14
+
15
+ limit = StringTruncator.mysql_textual_column_limit(column)
16
+ value = self[field].to_s
17
+ if value.length > limit
18
+ self[field] = value.slice(0, limit)
19
+ end
20
+ return true # to make sure the callback chain doesn't halt
21
+ end
22
+
23
+ when :text
24
+ lambda do
25
+ return unless self.changes.key?(field.to_s)
26
+ return if self[field].nil?
27
+
28
+ limit = StringTruncator.mysql_textual_column_limit(column)
29
+ value = self[field].to_s
30
+ value.encode!('utf-8') if value.encoding != Encoding::UTF_8
31
+ if value.bytesize > limit
32
+ self[field] = value.mb_chars.limit(limit).to_s
33
+ end
34
+ return true # to make sure the callback chain doesn't halt
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def self.mysql_textual_column_limit(column)
41
+ @mysql_textual_column_limits ||= {}
42
+ @mysql_textual_column_limits[column] ||= begin
43
+ raise ArgumentError, "Only UTF-8 textual columns are supported." unless column.text? && column.collation =~ /\Autf8_/
44
+ column.limit
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+ require 'test_helper'
3
+
4
+ ActiveRecord::Migration.suppress_messages do
5
+ ActiveRecord::Migration.create_table("magical_creatures", force: true, options: "CHARACTER SET utf8mb3") do |t|
6
+ t.string :string, limit: 255
7
+ t.text :tinytext, limit: 255
8
+ end
9
+ end
10
+
11
+ class MagicalCreature < ActiveRecord::Base
12
+ include ActiveRecord::DatabaseValidations::StringTruncator
13
+
14
+ before_validation truncate_string(:string)
15
+ before_validation truncate_string(:tinytext)
16
+
17
+ validates :string, :tinytext, database_constraints: :size
18
+ end
19
+
20
+ class StringTruncatorTest < Minitest::Test
21
+ def test_handles_nil_gracefully
22
+ u_nil = MagicalCreature.create!(string: 'present', tinytext: 'present')
23
+ u_nil.string, u_nil.tinytext = nil, nil
24
+ assert_equal ['string', 'tinytext'], u_nil.changed
25
+ assert u_nil.valid?
26
+ end
27
+
28
+ def test_truncate_varchar_field_using_characters
29
+ u1 = MagicalCreature.new(string: 'a' * 256)
30
+ assert u1.valid?
31
+ assert_equal 'a' * 255, u1.string
32
+
33
+ u2 = MagicalCreature.new(string: '漢' * 256)
34
+ assert u2.valid?
35
+ assert_equal '漢' * 255, u2.string
36
+ end
37
+
38
+ def test_truncate_text_fields_using_bytes
39
+ u1 = MagicalCreature.new(string: 'a' * 256)
40
+ assert u1.valid?
41
+ assert_equal 'a' * 255, u1.string
42
+
43
+ u2 = MagicalCreature.new(tinytext: '漢' * 86)
44
+ assert u2.valid?
45
+ assert_equal '漢' * 85, u2.tinytext
46
+
47
+ u3 = MagicalCreature.new(tinytext: 'ü' * 128) # note: field limit falls between the two bytes of the last character.
48
+ assert u3.valid?
49
+ assert_equal 'ü' * 127, u3.tinytext
50
+ end
51
+
52
+ def test_recoding_support_for_text_fields
53
+ u4 = MagicalCreature.new(tinytext: ('ü' * 128).encode('ISO-8859-15'))
54
+ assert u4.valid?
55
+ assert_equal 'ü' * 127, u4.tinytext
56
+ assert_equal Encoding::UTF_8, u4.tinytext.encoding
57
+ end
58
+ end
metadata CHANGED
@@ -1,97 +1,97 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-databasevalidations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
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-10-22 00:00:00.000000000 Z
11
+ date: 2014-11-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
19
  version: '4.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ~>
25
25
  - !ruby/object:Gem::Version
26
26
  version: '4.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rejectu
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - '>='
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - '>='
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ~>
46
46
  - !ruby/object:Gem::Version
47
47
  version: '1.5'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ~>
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.5'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - '>='
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: minitest
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - '>='
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - '>='
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: mysql2
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ">="
87
+ - - '>='
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">="
94
+ - - '>='
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  description: Opt-in validations for your ActiveRecord models based on your MySQL database
@@ -102,8 +102,8 @@ executables: []
102
102
  extensions: []
103
103
  extra_rdoc_files: []
104
104
  files:
105
- - ".gitignore"
106
- - ".travis.yml"
105
+ - .gitignore
106
+ - .travis.yml
107
107
  - Gemfile
108
108
  - Gemfile.activerecord40
109
109
  - Gemfile.activerecord41
@@ -118,6 +118,7 @@ files:
118
118
  - lib/active_record/database_validations/locale/en.yml
119
119
  - lib/active_record/database_validations/version.rb
120
120
  - lib/active_record/validations/database_constraints.rb
121
+ - lib/active_record/validations/string_truncator.rb
121
122
  - lib/activerecord-databasevalidations.rb
122
123
  - lib/activerecord/databasevalidations.rb
123
124
  - test/basic_multilingual_plane_validator_test.rb
@@ -126,6 +127,7 @@ files:
126
127
  - test/database.yml
127
128
  - test/database_constraints_validator_test.rb
128
129
  - test/not_null_validator_test.rb
130
+ - test/string_truncator_test.rb
129
131
  - test/test_helper.rb
130
132
  homepage: https://github.com/wvanbergen/activerecord-database_validations
131
133
  licenses:
@@ -137,17 +139,17 @@ require_paths:
137
139
  - lib
138
140
  required_ruby_version: !ruby/object:Gem::Requirement
139
141
  requirements:
140
- - - ">="
142
+ - - '>='
141
143
  - !ruby/object:Gem::Version
142
144
  version: '0'
143
145
  required_rubygems_version: !ruby/object:Gem::Requirement
144
146
  requirements:
145
- - - ">="
147
+ - - '>='
146
148
  - !ruby/object:Gem::Version
147
149
  version: '0'
148
150
  requirements: []
149
151
  rubyforge_project:
150
- rubygems_version: 2.2.2
152
+ rubygems_version: 2.0.14
151
153
  signing_key:
152
154
  specification_version: 4
153
155
  summary: Add validations to your ActiveRecord models based on MySQL database constraints.
@@ -158,4 +160,5 @@ test_files:
158
160
  - test/database.yml
159
161
  - test/database_constraints_validator_test.rb
160
162
  - test/not_null_validator_test.rb
163
+ - test/string_truncator_test.rb
161
164
  - test/test_helper.rb