database_validations 0.8.6 → 0.8.9
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/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
|