database_validations 0.8.5 → 0.9.1
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 +4 -4
- data/lib/database_validations.rb +14 -10
- data/lib/database_validations/lib/adapters.rb +20 -0
- data/lib/database_validations/{validations → lib}/adapters/base_adapter.rb +1 -17
- data/lib/database_validations/lib/adapters/mysql_adapter.rb +19 -0
- data/lib/database_validations/lib/adapters/postgresql_adapter.rb +21 -0
- data/lib/database_validations/lib/adapters/sqlite_adapter.rb +19 -0
- data/lib/database_validations/lib/attribute_validator.rb +3 -0
- data/lib/database_validations/lib/checkers/db_presence_validator.rb +36 -0
- data/lib/database_validations/lib/checkers/db_uniqueness_validator.rb +47 -0
- data/lib/database_validations/{validations → lib}/errors.rb +1 -12
- data/lib/database_validations/lib/injector.rb +13 -0
- data/lib/database_validations/lib/key_generator.rb +32 -0
- data/lib/database_validations/lib/presence_key_extractor.rb +18 -0
- data/lib/database_validations/lib/rescuer.rb +36 -0
- data/lib/database_validations/lib/storage.rb +28 -0
- data/lib/database_validations/lib/uniqueness_key_extractor.rb +38 -0
- data/lib/database_validations/lib/validations.rb +31 -0
- data/lib/database_validations/lib/validators/db_presence_validator.rb +63 -0
- data/lib/database_validations/lib/validators/db_uniqueness_validator.rb +48 -0
- data/lib/database_validations/rspec/uniqueness_validator_matcher.rb +19 -10
- data/lib/database_validations/version.rb +1 -1
- metadata +52 -41
- data/lib/database_validations/validations/adapters.rb +0 -20
- data/lib/database_validations/validations/adapters/mysql_adapter.rb +0 -20
- data/lib/database_validations/validations/adapters/postgresql_adapter.rb +0 -20
- data/lib/database_validations/validations/adapters/sqlite_adapter.rb +0 -22
- data/lib/database_validations/validations/belongs_to_handlers.rb +0 -58
- data/lib/database_validations/validations/belongs_to_options.rb +0 -44
- data/lib/database_validations/validations/belongs_to_presence_validator.rb +0 -25
- data/lib/database_validations/validations/helpers.rb +0 -69
- data/lib/database_validations/validations/options_storage.rb +0 -34
- data/lib/database_validations/validations/uniqueness_handlers.rb +0 -56
- data/lib/database_validations/validations/uniqueness_options.rb +0 -104
- data/lib/database_validations/validations/valid_without_database_validations.rb +0 -9
@@ -0,0 +1,31 @@
|
|
1
|
+
module DatabaseValidations
|
2
|
+
module Validations
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
alias_method :validate, :valid?
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :_database_validations_fallback
|
10
|
+
|
11
|
+
def valid?(context = nil)
|
12
|
+
self._database_validations_fallback = true
|
13
|
+
super(context)
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_or_update(*args, &block)
|
17
|
+
self._database_validations_fallback = false
|
18
|
+
ActiveRecord::Base.connection.transaction(requires_new: true) { super }
|
19
|
+
rescue ActiveRecord::InvalidForeignKey, ActiveRecord::RecordNotUnique => e
|
20
|
+
raise e unless Rescuer.handled?(self, e)
|
21
|
+
|
22
|
+
raise ActiveRecord::RecordInvalid, self
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def perform_validations(options = {})
|
28
|
+
options[:validate] == false || valid_without_database_validations?(options[:context])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module DatabaseValidations
|
2
|
+
class DbPresenceValidator < ActiveRecord::Validations::PresenceValidator
|
3
|
+
REFLECTION_MESSAGE = ActiveRecord::VERSION::MAJOR < 5 ? :blank : :required
|
4
|
+
|
5
|
+
attr_reader :klass
|
6
|
+
|
7
|
+
# Used to make 3rd party libraries work correctly
|
8
|
+
#
|
9
|
+
# @return [Symbol]
|
10
|
+
def self.kind
|
11
|
+
:presence
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param [Hash] options
|
15
|
+
def initialize(options)
|
16
|
+
@klass = options[:class]
|
17
|
+
|
18
|
+
super
|
19
|
+
|
20
|
+
Injector.inject(klass)
|
21
|
+
Checkers::DbPresenceValidator.validate!(self)
|
22
|
+
end
|
23
|
+
|
24
|
+
# TODO: add support of optional db_belongs_to
|
25
|
+
def validate(record)
|
26
|
+
if record._database_validations_fallback
|
27
|
+
super
|
28
|
+
else
|
29
|
+
attributes.each do |attribute|
|
30
|
+
reflection = record.class._reflect_on_association(attribute)
|
31
|
+
|
32
|
+
next if reflection && record.public_send(reflection.foreign_key).present?
|
33
|
+
|
34
|
+
validate_each(record, attribute, record.public_send(attribute))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def apply_error(instance, attribute)
|
40
|
+
# Helps to avoid querying the database when attribute is association
|
41
|
+
instance.send("#{attribute}=", nil)
|
42
|
+
instance.errors.add(attribute, :blank, message: REFLECTION_MESSAGE)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module ClassMethods
|
47
|
+
def validates_db_presence_of(*attr_names)
|
48
|
+
validates_with(DatabaseValidations::DbPresenceValidator, _merge_attributes(attr_names))
|
49
|
+
end
|
50
|
+
|
51
|
+
def db_belongs_to(name, scope = nil, **options)
|
52
|
+
if ActiveRecord::VERSION::MAJOR < 5
|
53
|
+
options[:required] = false
|
54
|
+
else
|
55
|
+
options[:optional] = true
|
56
|
+
end
|
57
|
+
|
58
|
+
belongs_to(name, scope, options)
|
59
|
+
|
60
|
+
validates_with DatabaseValidations::DbPresenceValidator, _merge_attributes([name, message: DatabaseValidations::DbPresenceValidator::REFLECTION_MESSAGE]) # rubocop:disable Metrics/LineLength
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module DatabaseValidations
|
2
|
+
class DbUniquenessValidator < ActiveRecord::Validations::UniquenessValidator
|
3
|
+
attr_reader :index_name, :where, :klass
|
4
|
+
|
5
|
+
# Used to make 3rd party libraries work correctly
|
6
|
+
#
|
7
|
+
# @return [Symbol]
|
8
|
+
def self.kind
|
9
|
+
:uniqueness
|
10
|
+
end
|
11
|
+
|
12
|
+
# @param [Hash] options
|
13
|
+
def initialize(options)
|
14
|
+
options[:allow_nil] = true
|
15
|
+
options[:allow_blank] = false
|
16
|
+
|
17
|
+
if options.key?(:where)
|
18
|
+
condition = options[:where]
|
19
|
+
options[:conditions] = -> { where(condition) }
|
20
|
+
end
|
21
|
+
|
22
|
+
@index_name = options.delete(:index_name) if options.key?(:index_name)
|
23
|
+
@where = options.delete(:where) if options.key?(:where)
|
24
|
+
|
25
|
+
super
|
26
|
+
|
27
|
+
Injector.inject(klass)
|
28
|
+
Checkers::DbUniquenessValidator.validate!(self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate(record)
|
32
|
+
super if record._database_validations_fallback
|
33
|
+
end
|
34
|
+
|
35
|
+
def apply_error(instance, attribute)
|
36
|
+
error_options = options.except(:case_sensitive, :scope, :conditions)
|
37
|
+
error_options[:value] = instance.public_send(attribute)
|
38
|
+
|
39
|
+
instance.errors.add(attribute, :taken, error_options)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module ClassMethods
|
44
|
+
def validates_db_uniqueness_of(*attr_names)
|
45
|
+
validates_with(DatabaseValidations::DbUniquenessValidator, _merge_attributes(attr_names))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -41,29 +41,37 @@ RSpec::Matchers.define :validate_db_uniqueness_of do |field| # rubocop:disable M
|
|
41
41
|
@case_sensitive = false
|
42
42
|
end
|
43
43
|
|
44
|
+
chain(:case_sensitive) do
|
45
|
+
@case_sensitive = true
|
46
|
+
end
|
47
|
+
|
44
48
|
match do |object|
|
45
49
|
@validators = []
|
46
50
|
|
47
51
|
model = object.is_a?(Class) ? object : object.class
|
48
52
|
|
49
|
-
DatabaseValidations::
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
53
|
+
model.validators.grep(DatabaseValidations::DbUniquenessValidator).each do |validator|
|
54
|
+
validator.attributes.each do |attribute|
|
55
|
+
@validators << {
|
56
|
+
field: attribute,
|
57
|
+
scope: Array.wrap(validator.options[:scope]),
|
58
|
+
where: validator.where,
|
59
|
+
message: validator.options[:message],
|
60
|
+
index_name: validator.index_name,
|
61
|
+
case_sensitive: validator.options[:case_sensitive]
|
62
|
+
}
|
63
|
+
end
|
58
64
|
end
|
59
65
|
|
66
|
+
case_sensitive_default = ActiveRecord::VERSION::MAJOR >= 6 ? nil : true
|
67
|
+
|
60
68
|
@validators.include?(
|
61
69
|
field: field,
|
62
70
|
scope: Array.wrap(@scope),
|
63
71
|
where: @where,
|
64
72
|
message: @message,
|
65
73
|
index_name: @index_name,
|
66
|
-
case_sensitive: @case_sensitive
|
74
|
+
case_sensitive: @case_sensitive.nil? ? case_sensitive_default : @case_sensitive
|
67
75
|
)
|
68
76
|
end
|
69
77
|
|
@@ -75,6 +83,7 @@ RSpec::Matchers.define :validate_db_uniqueness_of do |field| # rubocop:disable M
|
|
75
83
|
desc += "where: '#{@where}'; " if @where
|
76
84
|
desc += "index_name: '#{@index_name}'; " if @index_name
|
77
85
|
desc += 'be case insensitive.' unless @case_sensitive
|
86
|
+
desc += 'be case sensitive.' if @case_sensitive
|
78
87
|
desc
|
79
88
|
end
|
80
89
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: database_validations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evgeniy Demin
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-06-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,20 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
20
|
-
- - "<"
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: '6'
|
19
|
+
version: 4.2.0
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
24
|
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
30
|
-
- - "<"
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: '6'
|
26
|
+
version: 4.2.0
|
33
27
|
- !ruby/object:Gem::Dependency
|
34
28
|
name: benchmark-ips
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -50,56 +44,70 @@ dependencies:
|
|
50
44
|
requirements:
|
51
45
|
- - "~>"
|
52
46
|
- !ruby/object:Gem::Version
|
53
|
-
version: '
|
47
|
+
version: '2.0'
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
51
|
requirements:
|
58
52
|
- - "~>"
|
59
53
|
- !ruby/object:Gem::Version
|
60
|
-
version: '
|
54
|
+
version: '2.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: db-query-matchers
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.9'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.9'
|
61
69
|
- !ruby/object:Gem::Dependency
|
62
70
|
name: mysql2
|
63
71
|
requirement: !ruby/object:Gem::Requirement
|
64
72
|
requirements:
|
65
|
-
- - "
|
73
|
+
- - ">="
|
66
74
|
- !ruby/object:Gem::Version
|
67
|
-
version: '0
|
75
|
+
version: '0'
|
68
76
|
type: :development
|
69
77
|
prerelease: false
|
70
78
|
version_requirements: !ruby/object:Gem::Requirement
|
71
79
|
requirements:
|
72
|
-
- - "
|
80
|
+
- - ">="
|
73
81
|
- !ruby/object:Gem::Version
|
74
|
-
version: '0
|
82
|
+
version: '0'
|
75
83
|
- !ruby/object:Gem::Dependency
|
76
84
|
name: pg
|
77
85
|
requirement: !ruby/object:Gem::Requirement
|
78
86
|
requirements:
|
79
|
-
- - "
|
87
|
+
- - ">="
|
80
88
|
- !ruby/object:Gem::Version
|
81
|
-
version: '
|
89
|
+
version: '0'
|
82
90
|
type: :development
|
83
91
|
prerelease: false
|
84
92
|
version_requirements: !ruby/object:Gem::Requirement
|
85
93
|
requirements:
|
86
|
-
- - "
|
94
|
+
- - ">="
|
87
95
|
- !ruby/object:Gem::Version
|
88
|
-
version: '
|
96
|
+
version: '0'
|
89
97
|
- !ruby/object:Gem::Dependency
|
90
98
|
name: rake
|
91
99
|
requirement: !ruby/object:Gem::Requirement
|
92
100
|
requirements:
|
93
101
|
- - "~>"
|
94
102
|
- !ruby/object:Gem::Version
|
95
|
-
version: '
|
103
|
+
version: '13.0'
|
96
104
|
type: :development
|
97
105
|
prerelease: false
|
98
106
|
version_requirements: !ruby/object:Gem::Requirement
|
99
107
|
requirements:
|
100
108
|
- - "~>"
|
101
109
|
- !ruby/object:Gem::Version
|
102
|
-
version: '
|
110
|
+
version: '13.0'
|
103
111
|
- !ruby/object:Gem::Dependency
|
104
112
|
name: rspec
|
105
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -170,6 +178,24 @@ extensions: []
|
|
170
178
|
extra_rdoc_files: []
|
171
179
|
files:
|
172
180
|
- lib/database_validations.rb
|
181
|
+
- lib/database_validations/lib/adapters.rb
|
182
|
+
- lib/database_validations/lib/adapters/base_adapter.rb
|
183
|
+
- lib/database_validations/lib/adapters/mysql_adapter.rb
|
184
|
+
- lib/database_validations/lib/adapters/postgresql_adapter.rb
|
185
|
+
- lib/database_validations/lib/adapters/sqlite_adapter.rb
|
186
|
+
- lib/database_validations/lib/attribute_validator.rb
|
187
|
+
- lib/database_validations/lib/checkers/db_presence_validator.rb
|
188
|
+
- lib/database_validations/lib/checkers/db_uniqueness_validator.rb
|
189
|
+
- lib/database_validations/lib/errors.rb
|
190
|
+
- lib/database_validations/lib/injector.rb
|
191
|
+
- lib/database_validations/lib/key_generator.rb
|
192
|
+
- lib/database_validations/lib/presence_key_extractor.rb
|
193
|
+
- lib/database_validations/lib/rescuer.rb
|
194
|
+
- lib/database_validations/lib/storage.rb
|
195
|
+
- lib/database_validations/lib/uniqueness_key_extractor.rb
|
196
|
+
- lib/database_validations/lib/validations.rb
|
197
|
+
- lib/database_validations/lib/validators/db_presence_validator.rb
|
198
|
+
- lib/database_validations/lib/validators/db_uniqueness_validator.rb
|
173
199
|
- lib/database_validations/rails/railtie.rb
|
174
200
|
- lib/database_validations/rspec/matchers.rb
|
175
201
|
- lib/database_validations/rspec/uniqueness_validator_matcher.rb
|
@@ -177,26 +203,12 @@ files:
|
|
177
203
|
- lib/database_validations/rubocop/cop/uniqueness_of.rb
|
178
204
|
- lib/database_validations/rubocop/cops.rb
|
179
205
|
- lib/database_validations/tasks/database_validations.rake
|
180
|
-
- lib/database_validations/validations/adapters.rb
|
181
|
-
- lib/database_validations/validations/adapters/base_adapter.rb
|
182
|
-
- lib/database_validations/validations/adapters/mysql_adapter.rb
|
183
|
-
- lib/database_validations/validations/adapters/postgresql_adapter.rb
|
184
|
-
- lib/database_validations/validations/adapters/sqlite_adapter.rb
|
185
|
-
- lib/database_validations/validations/belongs_to_handlers.rb
|
186
|
-
- lib/database_validations/validations/belongs_to_options.rb
|
187
|
-
- lib/database_validations/validations/belongs_to_presence_validator.rb
|
188
|
-
- lib/database_validations/validations/errors.rb
|
189
|
-
- lib/database_validations/validations/helpers.rb
|
190
|
-
- lib/database_validations/validations/options_storage.rb
|
191
|
-
- lib/database_validations/validations/uniqueness_handlers.rb
|
192
|
-
- lib/database_validations/validations/uniqueness_options.rb
|
193
|
-
- lib/database_validations/validations/valid_without_database_validations.rb
|
194
206
|
- lib/database_validations/version.rb
|
195
207
|
homepage: https://github.com/toptal/database_validations
|
196
208
|
licenses:
|
197
209
|
- MIT
|
198
210
|
metadata: {}
|
199
|
-
post_install_message:
|
211
|
+
post_install_message:
|
200
212
|
rdoc_options: []
|
201
213
|
require_paths:
|
202
214
|
- lib
|
@@ -211,9 +223,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
211
223
|
- !ruby/object:Gem::Version
|
212
224
|
version: '0'
|
213
225
|
requirements: []
|
214
|
-
|
215
|
-
|
216
|
-
signing_key:
|
226
|
+
rubygems_version: 3.1.2
|
227
|
+
signing_key:
|
217
228
|
specification_version: 4
|
218
229
|
summary: Provide compatibility between database constraints and ActiveRecord validations
|
219
230
|
with better performance and consistency.
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'database_validations/validations/adapters/base_adapter'
|
2
|
-
require 'database_validations/validations/adapters/sqlite_adapter'
|
3
|
-
require 'database_validations/validations/adapters/postgresql_adapter'
|
4
|
-
require 'database_validations/validations/adapters/mysql_adapter'
|
5
|
-
|
6
|
-
module DatabaseValidations
|
7
|
-
module Adapters
|
8
|
-
module_function
|
9
|
-
|
10
|
-
def factory(model)
|
11
|
-
case (database = model.connection_config[:adapter].downcase.to_sym)
|
12
|
-
when SqliteAdapter::ADAPTER then SqliteAdapter.new(model)
|
13
|
-
when PostgresqlAdapter::ADAPTER then PostgresqlAdapter.new(model)
|
14
|
-
when MysqlAdapter::ADAPTER then MysqlAdapter.new(model)
|
15
|
-
else
|
16
|
-
raise Errors::UnknownDatabase, database
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module DatabaseValidations
|
2
|
-
module Adapters
|
3
|
-
class MysqlAdapter < BaseAdapter
|
4
|
-
SUPPORTED_OPTIONS = %i[scope message if unless index_name].freeze
|
5
|
-
ADAPTER = :mysql2
|
6
|
-
|
7
|
-
def index_name(error_message)
|
8
|
-
error_message[/key '([^']+)'/, 1]
|
9
|
-
end
|
10
|
-
|
11
|
-
def unique_error_columns(error_message)
|
12
|
-
find_index_by_name(index_name(error_message)).columns
|
13
|
-
end
|
14
|
-
|
15
|
-
def foreign_key_error_column(error_message)
|
16
|
-
error_message[/FOREIGN KEY \(`([^`]+)`\)/, 1]
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module DatabaseValidations
|
2
|
-
module Adapters
|
3
|
-
class PostgresqlAdapter < BaseAdapter
|
4
|
-
SUPPORTED_OPTIONS = %i[scope message where if unless index_name case_sensitive].freeze
|
5
|
-
ADAPTER = :postgresql
|
6
|
-
|
7
|
-
def index_name(error_message)
|
8
|
-
error_message[/unique constraint "([^"]+)"/, 1]
|
9
|
-
end
|
10
|
-
|
11
|
-
def unique_error_columns(error_message)
|
12
|
-
find_index_by_name(index_name(error_message)).columns
|
13
|
-
end
|
14
|
-
|
15
|
-
def foreign_key_error_column(error_message)
|
16
|
-
error_message[/Key \(([^)]+)\)/, 1]
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|