database_validations 0.8.6 → 0.8.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/database_validations/lib/adapters.rb +3 -3
- data/lib/database_validations/lib/adapters/base_adapter.rb +1 -1
- data/lib/database_validations/lib/adapters/mysql_adapter.rb +8 -8
- data/lib/database_validations/lib/adapters/postgresql_adapter.rb +10 -8
- data/lib/database_validations/lib/adapters/sqlite_adapter.rb +8 -10
- data/lib/database_validations/lib/db_belongs_to/belongs_to_handlers.rb +2 -2
- data/lib/database_validations/lib/db_belongs_to/belongs_to_options.rb +1 -6
- data/lib/database_validations/lib/errors.rb +1 -1
- data/lib/database_validations/lib/helpers.rb +14 -20
- data/lib/database_validations/lib/options_storage.rb +5 -8
- data/lib/database_validations/lib/validates_db_uniqueness_of/uniqueness_handlers.rb +2 -2
- data/lib/database_validations/lib/validates_db_uniqueness_of/uniqueness_options.rb +21 -9
- data/lib/database_validations/rspec/uniqueness_validator_matcher.rb +11 -9
- data/lib/database_validations/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c5d3a56f3d69b34fdc1da4e3b74b9ff3797d0ca93772df93746e709f9b9fd64
|
4
|
+
data.tar.gz: db772a3a726ccce4ce21bfceb0ddac72da07d9da3650f89d1845e92e580519b2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da0b57c3e7203be84466c31f72a517852d1b4484a306607bf48e6d6f6d683e43dc1ebdcd678bc258075c5ce1f9c52af756805b749ab5562cc84ac83a6ac48ee4
|
7
|
+
data.tar.gz: 1580a4718c53aa42267a2d7efd04148df0de38ddba3ce7c4c5f6c4c226736e4f568919cbe600b79d269566f081c7c9537b09fca0f5b0d65cdd73378e81b9ae71
|
@@ -9,9 +9,9 @@ module DatabaseValidations
|
|
9
9
|
|
10
10
|
def factory(model)
|
11
11
|
case (database = model.connection_config[:adapter].downcase.to_sym)
|
12
|
-
when SqliteAdapter::ADAPTER then SqliteAdapter
|
13
|
-
when PostgresqlAdapter::ADAPTER then PostgresqlAdapter
|
14
|
-
when MysqlAdapter::ADAPTER then MysqlAdapter
|
12
|
+
when SqliteAdapter::ADAPTER then SqliteAdapter
|
13
|
+
when PostgresqlAdapter::ADAPTER then PostgresqlAdapter
|
14
|
+
when MysqlAdapter::ADAPTER then MysqlAdapter
|
15
15
|
else
|
16
16
|
raise Errors::UnknownDatabase, database
|
17
17
|
end
|
@@ -28,7 +28,7 @@ module DatabaseValidations
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def find_foreign_key_by_column(column)
|
31
|
-
|
31
|
+
foreign_keys.find { |foreign_key| foreign_key.column.to_s == column.to_s }
|
32
32
|
end
|
33
33
|
|
34
34
|
# @return [Symbol]
|
@@ -4,16 +4,16 @@ module DatabaseValidations
|
|
4
4
|
SUPPORTED_OPTIONS = %i[scope message if unless index_name].freeze
|
5
5
|
ADAPTER = :mysql2
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
class << self
|
8
|
+
def unique_index_name(error_message)
|
9
|
+
error_message[/key '([^']+)'/, 1]
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
find_index_by_name(index_name(error_message)).columns
|
13
|
-
end
|
12
|
+
def unique_error_columns(_error_message); end
|
14
13
|
|
15
|
-
|
16
|
-
|
14
|
+
def foreign_key_error_column(error_message)
|
15
|
+
error_message[/FOREIGN KEY \(`([^`]+)`\)/, 1]
|
16
|
+
end
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -4,16 +4,18 @@ module DatabaseValidations
|
|
4
4
|
SUPPORTED_OPTIONS = %i[scope message where if unless index_name case_sensitive].freeze
|
5
5
|
ADAPTER = :postgresql
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
class << self
|
8
|
+
def unique_index_name(error_message)
|
9
|
+
error_message[/unique constraint "([^"]+)"/, 1]
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
def unique_error_columns(error_message)
|
13
|
+
error_message[/Key \((.+)\)=/, 1].split(', ')
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
16
|
+
def foreign_key_error_column(error_message)
|
17
|
+
error_message[/Key \(([^)]+)\)/, 1]
|
18
|
+
end
|
17
19
|
end
|
18
20
|
end
|
19
21
|
end
|
@@ -4,18 +4,16 @@ module DatabaseValidations
|
|
4
4
|
SUPPORTED_OPTIONS = %i[scope message if unless].freeze
|
5
5
|
ADAPTER = :sqlite3
|
6
6
|
|
7
|
-
|
7
|
+
class << self
|
8
|
+
def unique_index_name(_error_message); end
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def unique_error_columns(error_message)
|
14
|
-
error_message.scan(/#{model.table_name}\.([^,:]+)/).flatten
|
15
|
-
end
|
10
|
+
def unique_error_columns(error_message)
|
11
|
+
error_message.scan(/\w+\.([^,:]+)/).flatten
|
12
|
+
end
|
16
13
|
|
17
|
-
|
18
|
-
|
14
|
+
def foreign_key_error_column(error_message)
|
15
|
+
error_message[/\("([^"]+)"\) VALUES/, 1]
|
16
|
+
end
|
19
17
|
end
|
20
18
|
end
|
21
19
|
end
|
@@ -3,13 +3,13 @@ module DatabaseValidations
|
|
3
3
|
def db_belongs_to(name, scope = nil, **options)
|
4
4
|
Helpers.cache_valid_method!(self)
|
5
5
|
|
6
|
-
@
|
6
|
+
@database_validations_storage ||= DatabaseValidations::OptionsStorage.new(self)
|
7
7
|
|
8
8
|
belongs_to(name, scope, options.merge(optional: true))
|
9
9
|
|
10
10
|
foreign_key = reflections[name.to_s].foreign_key
|
11
11
|
|
12
|
-
@
|
12
|
+
@database_validations_storage.push_belongs_to(foreign_key, name)
|
13
13
|
|
14
14
|
validates_with DatabaseValidations::DBPresenceValidator,
|
15
15
|
DatabaseValidations::BelongsToOptions.validator_options(name, foreign_key)
|
@@ -17,12 +17,7 @@ module DatabaseValidations
|
|
17
17
|
|
18
18
|
# @return [String]
|
19
19
|
def key
|
20
|
-
Helpers.generate_key_for_belongs_to(column)
|
21
|
-
end
|
22
|
-
|
23
|
-
# @return [Boolean]
|
24
|
-
def column_and_relation_blank_for?(instance)
|
25
|
-
instance.public_send(column).blank? && instance.public_send(relation).blank?
|
20
|
+
@key ||= Helpers.generate_key_for_belongs_to(column)
|
26
21
|
end
|
27
22
|
|
28
23
|
def handle_foreign_key_error(instance)
|
@@ -59,7 +59,7 @@ module DatabaseValidations
|
|
59
59
|
@column = column
|
60
60
|
@foreign_keys = foreign_keys
|
61
61
|
|
62
|
-
super "No foreign key found with column: \"#{column}\".
|
62
|
+
super "No foreign key found with column: \"#{column}\". Found foreign keys are: #{foreign_keys}. " + env_message
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
@@ -18,14 +18,16 @@ module DatabaseValidations
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
def handle_unique_error!(instance, error)
|
21
|
+
def handle_unique_error!(instance, error)
|
22
22
|
adapter = Adapters.factory(instance.class)
|
23
|
-
|
24
|
-
|
23
|
+
|
24
|
+
keys = [
|
25
|
+
generate_key_for_uniqueness_index(adapter.unique_index_name(error.message)),
|
26
|
+
generate_key_for_uniqueness(adapter.unique_error_columns(error.message))
|
27
|
+
]
|
25
28
|
|
26
29
|
each_options_storage(instance.class) do |storage|
|
27
|
-
return storage[
|
28
|
-
return storage[column_key].handle_unique_error(instance) if storage[column_key]
|
30
|
+
keys.each { |key| return storage[key].handle_unique_error(instance) if storage[key] }
|
29
31
|
end
|
30
32
|
|
31
33
|
false
|
@@ -44,26 +46,18 @@ module DatabaseValidations
|
|
44
46
|
|
45
47
|
def each_options_storage(klass)
|
46
48
|
while klass.respond_to?(:validates_db_uniqueness_of)
|
47
|
-
storage = klass
|
49
|
+
storage = storage_of(klass)
|
48
50
|
yield(storage) if storage
|
49
51
|
klass = klass.superclass
|
50
52
|
end
|
51
53
|
end
|
52
54
|
|
53
|
-
def
|
54
|
-
|
55
|
-
storage.each_uniqueness_validator { |validator| yield(validator) }
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def each_belongs_to_presence_validator(klass)
|
60
|
-
each_options_storage(klass) do |storage|
|
61
|
-
storage.each_belongs_to_presence_validator { |validator| yield(validator) }
|
62
|
-
end
|
55
|
+
def storage_of(klass)
|
56
|
+
klass.instance_variable_get(:'@database_validations_storage')
|
63
57
|
end
|
64
58
|
|
65
|
-
def unify_columns(*
|
66
|
-
|
59
|
+
def unify_columns(*args)
|
60
|
+
args.flatten.compact.map(&:to_s).sort
|
67
61
|
end
|
68
62
|
|
69
63
|
def generate_key_for_uniqueness_index(index_name)
|
@@ -78,8 +72,8 @@ module DatabaseValidations
|
|
78
72
|
generate_key(:belongs_to, column)
|
79
73
|
end
|
80
74
|
|
81
|
-
def generate_key(type, *
|
82
|
-
[type, *unify_columns(
|
75
|
+
def generate_key(type, *args)
|
76
|
+
[type, *unify_columns(args)].join('__')
|
83
77
|
end
|
84
78
|
end
|
85
79
|
end
|
@@ -1,13 +1,14 @@
|
|
1
1
|
module DatabaseValidations
|
2
2
|
class OptionsStorage
|
3
3
|
def initialize(klass)
|
4
|
-
@adapter = Adapters.factory(klass)
|
4
|
+
@adapter = Adapters.factory(klass).new(klass)
|
5
5
|
@storage = {}
|
6
6
|
end
|
7
7
|
|
8
8
|
def push_uniqueness(field, options)
|
9
9
|
uniqueness_options = UniquenessOptions.new(field, options, adapter)
|
10
|
-
storage[uniqueness_options.
|
10
|
+
storage[uniqueness_options.index_key] = uniqueness_options
|
11
|
+
storage[uniqueness_options.column_key] = uniqueness_options
|
11
12
|
end
|
12
13
|
|
13
14
|
def push_belongs_to(field, relation)
|
@@ -19,12 +20,8 @@ module DatabaseValidations
|
|
19
20
|
storage[key]
|
20
21
|
end
|
21
22
|
|
22
|
-
def
|
23
|
-
storage.values
|
24
|
-
end
|
25
|
-
|
26
|
-
def each_belongs_to_presence_validator
|
27
|
-
storage.values.grep(BelongsToOptions).each { |validator| yield(validator) }
|
23
|
+
def options
|
24
|
+
storage.values
|
28
25
|
end
|
29
26
|
|
30
27
|
private
|
@@ -3,12 +3,12 @@ module DatabaseValidations
|
|
3
3
|
def validates_db_uniqueness_of(*attributes)
|
4
4
|
Helpers.cache_valid_method!(self)
|
5
5
|
|
6
|
-
@
|
6
|
+
@database_validations_storage ||= DatabaseValidations::OptionsStorage.new(self)
|
7
7
|
|
8
8
|
options = attributes.extract_options!
|
9
9
|
|
10
10
|
attributes.each do |attribute|
|
11
|
-
@
|
11
|
+
@database_validations_storage.push_uniqueness(attribute, options.merge(attributes: attribute))
|
12
12
|
end
|
13
13
|
|
14
14
|
validates_with DatabaseValidations::DBUniquenessValidator,
|
@@ -11,7 +11,7 @@ module DatabaseValidations
|
|
11
11
|
.tap { |opts| opts[:conditions] = -> { where(options[:where]) } if options[:where] }
|
12
12
|
end
|
13
13
|
|
14
|
-
attr_reader :field
|
14
|
+
attr_reader :field, :calculated_index_name
|
15
15
|
|
16
16
|
def initialize(field, options, adapter)
|
17
17
|
@field = field
|
@@ -19,7 +19,13 @@ module DatabaseValidations
|
|
19
19
|
@adapter = adapter
|
20
20
|
|
21
21
|
raise_if_unsupported_options!
|
22
|
-
|
22
|
+
|
23
|
+
return if ENV['SKIP_DB_UNIQUENESS_VALIDATOR_INDEX_CHECK']
|
24
|
+
|
25
|
+
index = responsible_index
|
26
|
+
raise_if_index_missed!(index)
|
27
|
+
|
28
|
+
@calculated_index_name = index.name
|
23
29
|
end
|
24
30
|
|
25
31
|
def handle_unique_error(instance)
|
@@ -30,8 +36,13 @@ module DatabaseValidations
|
|
30
36
|
end
|
31
37
|
|
32
38
|
# @return [String]
|
33
|
-
def
|
34
|
-
@
|
39
|
+
def index_key
|
40
|
+
@index_key ||= Helpers.generate_key_for_uniqueness_index(index_name || calculated_index_name)
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [String]
|
44
|
+
def column_key
|
45
|
+
@column_key ||= Helpers.generate_key_for_uniqueness(columns)
|
35
46
|
end
|
36
47
|
|
37
48
|
# @return [Array<String>]
|
@@ -76,11 +87,12 @@ module DatabaseValidations
|
|
76
87
|
end
|
77
88
|
end
|
78
89
|
|
79
|
-
def
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
90
|
+
def responsible_index
|
91
|
+
index_name ? adapter.find_index_by_name(index_name.to_s) : adapter.find_index(columns, where_clause)
|
92
|
+
end
|
93
|
+
|
94
|
+
def raise_if_index_missed!(index)
|
95
|
+
raise Errors::IndexNotFound.new(columns, where_clause, index_name, adapter.indexes, adapter.table_name) unless index
|
84
96
|
end
|
85
97
|
end
|
86
98
|
end
|
@@ -46,15 +46,17 @@ RSpec::Matchers.define :validate_db_uniqueness_of do |field| # rubocop:disable M
|
|
46
46
|
|
47
47
|
model = object.is_a?(Class) ? object : object.class
|
48
48
|
|
49
|
-
DatabaseValidations::Helpers.
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
49
|
+
DatabaseValidations::Helpers.each_options_storage(model) do |storage|
|
50
|
+
storage.options.grep(DatabaseValidations::UniquenessOptions).each do |validator|
|
51
|
+
@validators << {
|
52
|
+
field: validator.field,
|
53
|
+
scope: validator.scope,
|
54
|
+
where: validator.where_clause,
|
55
|
+
message: validator.message,
|
56
|
+
index_name: validator.index_name,
|
57
|
+
case_sensitive: validator.case_sensitive
|
58
|
+
}
|
59
|
+
end
|
58
60
|
end
|
59
61
|
|
60
62
|
@validators.include?(
|
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.8.
|
4
|
+
version: 0.8.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evgeniy Demin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-02-
|
11
|
+
date: 2019-02-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|