database_validations 0.3.0 → 0.3.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/adapters.rb +20 -0
- data/lib/database_validations/adapters/base_adapter.rb +18 -0
- data/lib/database_validations/adapters/mysql_adapter.rb +10 -0
- data/lib/database_validations/adapters/postgresql_adapter.rb +10 -0
- data/lib/database_validations/adapters/sqlite_adapter.rb +9 -0
- data/lib/database_validations/errors.rb +23 -0
- data/lib/database_validations/helpers.rb +29 -0
- data/lib/database_validations/uniqueness_validator.rb +64 -0
- data/lib/database_validations/version.rb +3 -0
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a7c3f11bd0b6a20ebd9cbcbe9134ae84e049c53e13d0a67faaa3da570994adc8
|
4
|
+
data.tar.gz: 1a45078331bccc6a9fdd6e59426b766dce95375ec9ffa7ac3130bb91df86f761
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 75563aa5561d03288ae9dbe9b38e5fc8d87955f9227150d0c3d782eb42bf02e12d259d2614c4e35d2dd344958bb306ba87a66f46747b04608b5082c5f430210e
|
7
|
+
data.tar.gz: 00f656712648b20f62d90d72db0b233e8f69d6a552e9adc7eb42c45cd85d2f17622812776223990dae9aea21ed42d586719cdff4a030eff25218cfde513c8b7e
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'database_validations/adapters/base_adapter'
|
2
|
+
require 'database_validations/adapters/sqlite_adapter'
|
3
|
+
require 'database_validations/adapters/postgresql_adapter'
|
4
|
+
require 'database_validations/adapters/mysql_adapter'
|
5
|
+
|
6
|
+
module DatabaseValidations
|
7
|
+
module Adapters
|
8
|
+
module_function
|
9
|
+
|
10
|
+
def factory(model)
|
11
|
+
case (database = model.connection.adapter_name.downcase.to_sym)
|
12
|
+
when :sqlite then Adapters::SqliteAdapter.new(model)
|
13
|
+
when :postgresql then Adapters::PostgresqlAdapter.new(model)
|
14
|
+
when :mysql2 then Adapters::MysqlAdapter.new(model)
|
15
|
+
else
|
16
|
+
raise Errors::UnknownDatabase.new(database)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module DatabaseValidations
|
2
|
+
module Adapters
|
3
|
+
class BaseAdapter
|
4
|
+
attr_reader :model
|
5
|
+
|
6
|
+
def initialize(model)
|
7
|
+
@model = model
|
8
|
+
end
|
9
|
+
|
10
|
+
def index_columns(index_name)
|
11
|
+
model.connection
|
12
|
+
.indexes(model.table_name)
|
13
|
+
.find { |index| index.name == index_name }
|
14
|
+
.columns
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module DatabaseValidations
|
2
|
+
module Errors
|
3
|
+
class Base < StandardError; end
|
4
|
+
|
5
|
+
class IndexNotFound < Base
|
6
|
+
attr_reader :columns
|
7
|
+
|
8
|
+
def initialize(columns)
|
9
|
+
@columns = columns.map(&:to_s)
|
10
|
+
super "No unique index found with columns: #{self.columns}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class UnknownDatabase < Base
|
15
|
+
attr_reader :database
|
16
|
+
|
17
|
+
def initialize(database)
|
18
|
+
@database = database
|
19
|
+
super "Unknown database: #{self.database}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module DatabaseValidations
|
2
|
+
module Helpers
|
3
|
+
module_function
|
4
|
+
|
5
|
+
def raise_if_index_missed!(model, columns)
|
6
|
+
index = model.connection
|
7
|
+
.indexes(model.table_name)
|
8
|
+
.select(&:unique)
|
9
|
+
.find { |index| index.columns.map(&:to_s).sort == columns }
|
10
|
+
|
11
|
+
raise Errors::IndexNotFound.new(columns) unless index
|
12
|
+
end
|
13
|
+
|
14
|
+
def handle_unique_error(instance, error)
|
15
|
+
columns = DatabaseValidations::Adapters
|
16
|
+
.factory(instance.class)
|
17
|
+
.error_columns(error.message)
|
18
|
+
.map!(&:to_s)
|
19
|
+
.sort!
|
20
|
+
|
21
|
+
options = instance.class.validates_db_uniqueness[columns]
|
22
|
+
|
23
|
+
error_options = options.except(:case_sensitive, :scope, :conditions, :attributes)
|
24
|
+
error_options[:value] = instance.public_send(options[:attributes])
|
25
|
+
|
26
|
+
instance.errors.add(options[:attributes], :taken, error_options)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module DatabaseValidations
|
2
|
+
module DatabaseUniquenessValidator
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
alias_method :valid_without_uniqueness?, :valid?
|
7
|
+
|
8
|
+
def valid?(context = nil)
|
9
|
+
output = super(context)
|
10
|
+
|
11
|
+
self.class.validates_db_uniqueness.each_value do |opts|
|
12
|
+
validates_with(ActiveRecord::Validations::UniquenessValidator, opts.merge(allow_nil: true))
|
13
|
+
end
|
14
|
+
|
15
|
+
errors.empty? && output
|
16
|
+
end
|
17
|
+
|
18
|
+
alias_method :validate, :valid?
|
19
|
+
end
|
20
|
+
|
21
|
+
def save(options = {})
|
22
|
+
super
|
23
|
+
rescue ActiveRecord::RecordNotUnique => e
|
24
|
+
DatabaseValidations::Helpers.handle_unique_error(self, e)
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def save!(options = {})
|
29
|
+
super
|
30
|
+
rescue ActiveRecord::RecordNotUnique => e
|
31
|
+
DatabaseValidations::Helpers.handle_unique_error(self, e)
|
32
|
+
raise ActiveRecord::RecordInvalid, self
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def perform_validations(options = {})
|
38
|
+
options[:validate] == false || valid_without_uniqueness?(options[:context])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
def validates_db_uniqueness_of(*attributes)
|
44
|
+
@validates_db_uniqueness ||= {}
|
45
|
+
|
46
|
+
options = attributes.extract_options!
|
47
|
+
|
48
|
+
attributes.each do |attribute|
|
49
|
+
columns = [attribute, Array.wrap(options[:scope])].flatten!.map!(&:to_s).sort!
|
50
|
+
|
51
|
+
DatabaseValidations::Helpers.raise_if_index_missed!(self, columns)
|
52
|
+
|
53
|
+
@validates_db_uniqueness[columns] = options.merge(attributes: attribute)
|
54
|
+
end
|
55
|
+
|
56
|
+
include(DatabaseUniquenessValidator)
|
57
|
+
end
|
58
|
+
|
59
|
+
def validates_db_uniqueness
|
60
|
+
derived = superclass.respond_to?(:validates_db_uniqueness) ? superclass.validates_db_uniqueness : {}
|
61
|
+
derived.merge(@validates_db_uniqueness || {})
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
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.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evgeniy Demin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-09-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -144,6 +144,15 @@ files:
|
|
144
144
|
- LICENSE.txt
|
145
145
|
- README.md
|
146
146
|
- lib/database_validations.rb
|
147
|
+
- lib/database_validations/adapters.rb
|
148
|
+
- lib/database_validations/adapters/base_adapter.rb
|
149
|
+
- lib/database_validations/adapters/mysql_adapter.rb
|
150
|
+
- lib/database_validations/adapters/postgresql_adapter.rb
|
151
|
+
- lib/database_validations/adapters/sqlite_adapter.rb
|
152
|
+
- lib/database_validations/errors.rb
|
153
|
+
- lib/database_validations/helpers.rb
|
154
|
+
- lib/database_validations/uniqueness_validator.rb
|
155
|
+
- lib/database_validations/version.rb
|
147
156
|
homepage: https://github.com/toptal/database_validations
|
148
157
|
licenses:
|
149
158
|
- MIT
|