foreign_key_validation 1.0.1 → 1.1.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 +8 -8
- data/README.md +9 -0
- data/lib/foreign_key_validation.rb +23 -0
- data/lib/foreign_key_validation/collector.rb +19 -14
- data/lib/foreign_key_validation/filter.rb +1 -0
- data/lib/foreign_key_validation/model_extension.rb +2 -2
- data/lib/foreign_key_validation/validator.rb +36 -6
- data/lib/foreign_key_validation/version.rb +1 -1
- data/spec/collector/base_spec.rb +30 -0
- data/spec/model_extension/validation_with_config_block_spec.rb +127 -0
- data/spec/{models → model_extension}/validation_with_custom_attrs_spec.rb +0 -0
- data/spec/{models → model_extension}/validation_with_exceptions_spec.rb +0 -0
- data/spec/{models → model_extension}/validation_with_missing_foreign_key_spec.rb +0 -0
- data/spec/{models → model_extension}/validation_without_attrs_spec.rb +0 -0
- data/spec/{models → model_extension}/without_validation_spec.rb +0 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/validator/base_spec.rb +38 -0
- metadata +18 -12
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZDU3ZTY0MGMyNmIzYTJiOWFjMjZjMzE5NjllMWM1MjAyNmI5MTcwOA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
OTRhOTc5NzlmZDBjODAxNmE4Mzk4NjM1ZDQ2NDJhMjkyMTFhZWY4YQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ODdiYzEzM2FhNTE1ZGVjZmMzYWY2ZmMxMGFmZDM2NTIxZjlmYjcxMGY1Njdk
|
10
|
+
NmZmN2Q3ZmY1YTkzN2NlYjZiNzhhNjhmNDUwNTAxOTkyYjMwMzRlYjg1NWU3
|
11
|
+
ZTc2ZWE1ZTAxZDYzMzQ1MmRkNmVmYmZkOGViOGZjNTRiNGNhYTY=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NTczZTBjNGFjNTgyYThkNThlZTQwZDUzNTZlZTQ3ZTk2MGIzMDY1ODRhMTg1
|
14
|
+
OWE2MDRhMGUxYzA5Yzg2NTEwYmEzMDk2NDUzOTU3MjhjNjNiMmFlY2NiOTc0
|
15
|
+
OTUxNTMwMmQzZDNhZTEyNjk3MWRhN2JhMmVlODE4NzczZGI2MDk=
|
data/README.md
CHANGED
@@ -35,6 +35,15 @@ Change behaviour by calling `validate_foreign_keys` with arguments hash.
|
|
35
35
|
|
36
36
|
This would only check `model.project.admin_user_id` to match `model.admin_user_id` before saving the record.
|
37
37
|
|
38
|
+
## Configuration
|
39
|
+
|
40
|
+
You can customize the behaviour of the gem by calling the `configure` method on the module with a block (e.g. initializer).
|
41
|
+
|
42
|
+
ForeignKeyValidation.configure do |config|
|
43
|
+
config.error_message = proc { |key, name, object| "My custom msg!" }
|
44
|
+
config.inject_subclasses = true
|
45
|
+
end
|
46
|
+
|
38
47
|
## Note
|
39
48
|
|
40
49
|
Only tested with ActiveRecord
|
@@ -4,5 +4,28 @@ require "foreign_key_validation/collector"
|
|
4
4
|
require "foreign_key_validation/filter"
|
5
5
|
require "foreign_key_validation/validator"
|
6
6
|
require "foreign_key_validation/model_extension"
|
7
|
+
require "ostruct"
|
8
|
+
|
9
|
+
module ForeignKeyValidation
|
10
|
+
|
11
|
+
DEFAULT_CONFIG = {
|
12
|
+
inject_subclasses: true,
|
13
|
+
error_message: proc { |validate_against_key, reflection_name, object|
|
14
|
+
"#{validate_against_key} of #{reflection_name} does not match #{object.class.name.tableize} #{validate_against_key}."
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
class << self
|
19
|
+
attr_writer :configuration
|
20
|
+
|
21
|
+
def configure(&blk)
|
22
|
+
yield configuration
|
23
|
+
end
|
24
|
+
|
25
|
+
def configuration
|
26
|
+
@configuration ||= OpenStruct.new(DEFAULT_CONFIG)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
7
30
|
|
8
31
|
ActiveRecord::Base.send :include, ForeignKeyValidation::ModelExtension
|
@@ -1,17 +1,12 @@
|
|
1
1
|
module ForeignKeyValidation
|
2
2
|
|
3
3
|
class Collector
|
4
|
-
attr_accessor :
|
4
|
+
attr_accessor :options
|
5
5
|
|
6
6
|
DEFAULT_VALIDATE_AGAINST = :user
|
7
7
|
|
8
8
|
def initialize(opt={})
|
9
|
-
self.
|
10
|
-
self.validate_against = find_validate_against(opt[:on])
|
11
|
-
self.reflections = klass.reflect_on_all_associations(:belongs_to)
|
12
|
-
self.reflection_names = reflections.map(&:name).map(&:to_s)
|
13
|
-
self.validate_against_key = find_validate_against_key
|
14
|
-
self.validate_with = find_validate_with(opt[:with])
|
9
|
+
self.options = opt
|
15
10
|
end
|
16
11
|
|
17
12
|
def check!
|
@@ -21,18 +16,28 @@ module ForeignKeyValidation
|
|
21
16
|
true
|
22
17
|
end
|
23
18
|
|
24
|
-
|
19
|
+
def klass
|
20
|
+
@klass ||= options[:klass]
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate_against
|
24
|
+
@validate_against ||= (options[:on] || DEFAULT_VALIDATE_AGAINST).to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate_with
|
28
|
+
@validate_with ||= ((Array(options[:with]).map(&:to_s) if options[:with]) || reflection_names).reject {|n| n == validate_against}
|
29
|
+
end
|
25
30
|
|
26
|
-
def
|
27
|
-
|
31
|
+
def reflections
|
32
|
+
@reflections ||= klass.reflect_on_all_associations(:belongs_to)
|
28
33
|
end
|
29
34
|
|
30
|
-
def
|
31
|
-
|
35
|
+
def reflection_names
|
36
|
+
@reflection_names ||= reflections.map {|r| r.name.to_s }
|
32
37
|
end
|
33
38
|
|
34
|
-
def
|
35
|
-
reflections.select {|r| r.name.to_s == validate_against}.first.try(:foreign_key)
|
39
|
+
def validate_against_key
|
40
|
+
@validate_against_key ||= reflections.select {|r| r.name.to_s == validate_against}.first.try(:foreign_key)
|
36
41
|
end
|
37
42
|
|
38
43
|
end
|
@@ -10,6 +10,7 @@ module ForeignKeyValidation
|
|
10
10
|
def before_filter(&block)
|
11
11
|
collector.klass.send :define_method, filter_name do
|
12
12
|
self.instance_eval &block
|
13
|
+
return true
|
13
14
|
end
|
14
15
|
collector.klass.send :private, filter_name.to_sym
|
15
16
|
collector.klass.send :before_validation, filter_name
|
@@ -5,13 +5,13 @@ module ForeignKeyValidation
|
|
5
5
|
module ClassMethods
|
6
6
|
|
7
7
|
def validate_foreign_keys(opt={})
|
8
|
-
subclasses.map {|klass| klass.send(:validate_foreign_keys, opt)}
|
8
|
+
subclasses.map {|klass| klass.send(:validate_foreign_keys, opt)} if ForeignKeyValidation.configuration.inject_subclasses
|
9
9
|
|
10
10
|
collector = Collector.new(opt.merge(klass: self))
|
11
11
|
collector.check!
|
12
12
|
|
13
13
|
Filter.new(collector).before_filter do
|
14
|
-
Validator.
|
14
|
+
Validator.new(validate_against_key: collector.validate_against_key, reflection_names: collector.validate_with, object: self).validate
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
@@ -2,16 +2,46 @@ module ForeignKeyValidation
|
|
2
2
|
|
3
3
|
class Validator
|
4
4
|
|
5
|
-
|
6
|
-
validate_against_key, reflection_names, object = opt[:validate_against_key], opt[:reflection_names], opt[:object]
|
5
|
+
attr_accessor :validate_against_key, :reflection_names, :object
|
7
6
|
|
8
|
-
|
9
|
-
|
7
|
+
def initialize(opt={})
|
8
|
+
self.validate_against_key = opt[:validate_against_key]
|
9
|
+
self.reflection_names = opt[:reflection_names] || []
|
10
|
+
self.object = opt[:object]
|
11
|
+
end
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
+
def validate
|
14
|
+
has_errors = false
|
15
|
+
reflection_names.each do |reflection_name|
|
16
|
+
next unless keys_present?(reflection_name)
|
17
|
+
if keys_different?(reflection_name)
|
18
|
+
attach_error(reflection_name)
|
19
|
+
has_errors = true
|
13
20
|
end
|
14
21
|
end
|
22
|
+
has_errors
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def key_on_relation(relation)
|
28
|
+
object.send(relation).try(validate_against_key)
|
29
|
+
end
|
30
|
+
|
31
|
+
def key_on_object
|
32
|
+
object.try(validate_against_key)
|
33
|
+
end
|
34
|
+
|
35
|
+
def keys_present?(relation)
|
36
|
+
key_on_object.present? and key_on_relation(relation).present?
|
37
|
+
end
|
38
|
+
|
39
|
+
def keys_different?(relation)
|
40
|
+
key_on_object != key_on_relation(relation)
|
41
|
+
end
|
42
|
+
|
43
|
+
def attach_error(reflection_name)
|
44
|
+
object.errors.add(validate_against_key, ForeignKeyValidation.configuration.error_message.call(validate_against_key, reflection_name, object))
|
15
45
|
end
|
16
46
|
|
17
47
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ForeignKeyValidation::Collector do
|
4
|
+
|
5
|
+
let(:user) { User.create }
|
6
|
+
let(:other_user) { User.create }
|
7
|
+
|
8
|
+
describe ".new" do
|
9
|
+
|
10
|
+
subject { ForeignKeyValidation::Collector }
|
11
|
+
|
12
|
+
it "initializes new validator" do
|
13
|
+
expect(subject.new).to be_instance_of ForeignKeyValidation::Collector
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#check!" do
|
19
|
+
|
20
|
+
it "returns true for known class" do
|
21
|
+
expect(ForeignKeyValidation::Collector.new(klass: Issue).check!).to be true
|
22
|
+
end
|
23
|
+
|
24
|
+
it "raises error for class without relations" do
|
25
|
+
expect{ForeignKeyValidation::Collector.new(klass: Dummy).check!}.to raise_error(ForeignKeyValidation::Errors::NoReleationFoundError)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ForeignKeyValidation::ModelExtension do
|
4
|
+
|
5
|
+
let(:user) { User.create }
|
6
|
+
let(:other_user) { User.create }
|
7
|
+
|
8
|
+
let(:manager) { Manager.create user: user }
|
9
|
+
let(:other_manager) { Manager.create user: User.create }
|
10
|
+
let(:developer) { Developer.create user: user, boss: manager }
|
11
|
+
|
12
|
+
context "with custom error message without vars" do
|
13
|
+
|
14
|
+
before do
|
15
|
+
ForeignKeyValidation.configure {|c| c.error_message = proc {"CUSTOM MSG!"} }
|
16
|
+
Member.send :validate_foreign_keys
|
17
|
+
end
|
18
|
+
|
19
|
+
it "does not allow to rewrite user id of developer" do
|
20
|
+
developer.user_id = other_user.id
|
21
|
+
developer.save
|
22
|
+
expect(developer.errors.messages.values.flatten).to include("CUSTOM MSG!")
|
23
|
+
expect(developer.reload.user_id).to_not eq(other_user.id)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "does not allow to rewrite boss id of developer" do
|
27
|
+
developer.custom_boss_id = other_manager.id
|
28
|
+
developer.save
|
29
|
+
expect(developer.errors.messages.values.flatten).to include("CUSTOM MSG!")
|
30
|
+
expect(developer.reload.custom_boss_id).to_not eq(other_manager.id)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
context "with custom error message with vars" do
|
36
|
+
|
37
|
+
before do
|
38
|
+
ForeignKeyValidation.configure {|c| c.error_message = proc {|a,b,c| "#{a} #{b} #{c.id}"} }
|
39
|
+
Member.send :validate_foreign_keys
|
40
|
+
end
|
41
|
+
|
42
|
+
it "does not allow to rewrite user id of developer" do
|
43
|
+
developer.user_id = other_user.id
|
44
|
+
developer.save
|
45
|
+
expect(developer.errors.messages.values.flatten).to include("user_id boss #{developer.id}")
|
46
|
+
expect(developer.reload.user_id).to_not eq(other_user.id)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "does not allow to rewrite boss id of developer" do
|
50
|
+
developer.custom_boss_id = other_manager.id
|
51
|
+
developer.save
|
52
|
+
expect(developer.errors.messages.values.flatten).to include("user_id boss #{developer.id}")
|
53
|
+
expect(developer.reload.custom_boss_id).to_not eq(other_manager.id)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
context "without injecting subclasses via config block" do
|
59
|
+
|
60
|
+
before do
|
61
|
+
Member.send :validate_foreign_keys
|
62
|
+
end
|
63
|
+
|
64
|
+
it "does not allow to rewrite user id of developer" do
|
65
|
+
developer.user_id = other_user.id
|
66
|
+
developer.save
|
67
|
+
expect(developer.errors.messages.values.flatten).to include("user_id of boss does not match developers user_id.")
|
68
|
+
expect(developer.reload.user_id).to_not eq(other_user.id)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "does not allow to rewrite boss id of developer" do
|
72
|
+
developer.custom_boss_id = other_manager.id
|
73
|
+
developer.save
|
74
|
+
expect(developer.errors.messages.values.flatten).to include("user_id of boss does not match developers user_id.")
|
75
|
+
expect(developer.reload.custom_boss_id).to_not eq(other_manager.id)
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
context "with injecting subclasses set to true via config block" do
|
81
|
+
|
82
|
+
before do
|
83
|
+
ForeignKeyValidation.configure {|c| c.inject_subclasses = true }
|
84
|
+
Member.send :validate_foreign_keys
|
85
|
+
end
|
86
|
+
|
87
|
+
it "does not allow to rewrite user id of developer" do
|
88
|
+
developer.user_id = other_user.id
|
89
|
+
developer.save
|
90
|
+
expect(developer.errors.messages.values.flatten).to include("user_id of boss does not match developers user_id.")
|
91
|
+
expect(developer.reload.user_id).to_not eq(other_user.id)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "does not allow to rewrite boss id of developer" do
|
95
|
+
developer.custom_boss_id = other_manager.id
|
96
|
+
developer.save
|
97
|
+
expect(developer.errors.messages.values.flatten).to include("user_id of boss does not match developers user_id.")
|
98
|
+
expect(developer.reload.custom_boss_id).to_not eq(other_manager.id)
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
context "with injecting subclasses set to false via config block" do
|
105
|
+
|
106
|
+
before do
|
107
|
+
ForeignKeyValidation.configure {|c| c.inject_subclasses = false }
|
108
|
+
Member.send :validate_foreign_keys
|
109
|
+
end
|
110
|
+
|
111
|
+
it "does allow to rewrite user id of developer" do
|
112
|
+
developer.user_id = other_user.id
|
113
|
+
developer.save
|
114
|
+
expect(developer.errors.messages.values.flatten).to_not include("user_id of boss does not match developers user_id.")
|
115
|
+
expect(developer.reload.user_id).to eq(other_user.id)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "does allow to rewrite boss id of developer" do
|
119
|
+
developer.custom_boss_id = other_manager.id
|
120
|
+
developer.save
|
121
|
+
expect(developer.errors.messages.values.flatten).to_not include("user_id of boss does not match developers user_id.")
|
122
|
+
expect(developer.reload.custom_boss_id).to eq(other_manager.id)
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ForeignKeyValidation::Validator do
|
4
|
+
|
5
|
+
let(:user) { User.create }
|
6
|
+
let(:other_user) { User.create }
|
7
|
+
|
8
|
+
describe ".new" do
|
9
|
+
|
10
|
+
subject { ForeignKeyValidation::Validator }
|
11
|
+
|
12
|
+
it "initializes new validator" do
|
13
|
+
expect(subject.new).to be_instance_of ForeignKeyValidation::Validator
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#validate" do
|
19
|
+
|
20
|
+
subject { ForeignKeyValidation::Validator }
|
21
|
+
|
22
|
+
it "creates no validations if params blank" do
|
23
|
+
expect(subject.new.validate).to be false
|
24
|
+
end
|
25
|
+
|
26
|
+
it "creates no validations if object is valid" do
|
27
|
+
object = Issue.create(user: user, project: Project.create(user: user))
|
28
|
+
expect(subject.new(validate_against_key: :user_id, reflection_names: [:project], object: object).validate).to be false
|
29
|
+
end
|
30
|
+
|
31
|
+
it "creates validations if object is invalid" do
|
32
|
+
object = Issue.create(user: user, project: Project.create(user: other_user))
|
33
|
+
expect(subject.new(validate_against_key: :user_id, reflection_names: [:project], object: object).validate).to be true
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreign_key_validation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marcus Geißler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-10
|
11
|
+
date: 2014-11-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -167,15 +167,18 @@ files:
|
|
167
167
|
- lib/foreign_key_validation/model_extension.rb
|
168
168
|
- lib/foreign_key_validation/validator.rb
|
169
169
|
- lib/foreign_key_validation/version.rb
|
170
|
-
- spec/
|
171
|
-
- spec/
|
172
|
-
- spec/
|
173
|
-
- spec/
|
174
|
-
- spec/
|
170
|
+
- spec/collector/base_spec.rb
|
171
|
+
- spec/model_extension/validation_with_config_block_spec.rb
|
172
|
+
- spec/model_extension/validation_with_custom_attrs_spec.rb
|
173
|
+
- spec/model_extension/validation_with_exceptions_spec.rb
|
174
|
+
- spec/model_extension/validation_with_missing_foreign_key_spec.rb
|
175
|
+
- spec/model_extension/validation_without_attrs_spec.rb
|
176
|
+
- spec/model_extension/without_validation_spec.rb
|
175
177
|
- spec/spec_helper.rb
|
176
178
|
- spec/support/load_models.rb
|
177
179
|
- spec/support/reset_models.rb
|
178
180
|
- spec/support/schema.rb
|
181
|
+
- spec/validator/base_spec.rb
|
179
182
|
homepage: https://github.com/marcusg/foreign_key_validation
|
180
183
|
licenses:
|
181
184
|
- MIT
|
@@ -201,12 +204,15 @@ signing_key:
|
|
201
204
|
specification_version: 4
|
202
205
|
summary: Protect the foreign keys in your Rails models.
|
203
206
|
test_files:
|
204
|
-
- spec/
|
205
|
-
- spec/
|
206
|
-
- spec/
|
207
|
-
- spec/
|
208
|
-
- spec/
|
207
|
+
- spec/collector/base_spec.rb
|
208
|
+
- spec/model_extension/validation_with_config_block_spec.rb
|
209
|
+
- spec/model_extension/validation_with_custom_attrs_spec.rb
|
210
|
+
- spec/model_extension/validation_with_exceptions_spec.rb
|
211
|
+
- spec/model_extension/validation_with_missing_foreign_key_spec.rb
|
212
|
+
- spec/model_extension/validation_without_attrs_spec.rb
|
213
|
+
- spec/model_extension/without_validation_spec.rb
|
209
214
|
- spec/spec_helper.rb
|
210
215
|
- spec/support/load_models.rb
|
211
216
|
- spec/support/reset_models.rb
|
212
217
|
- spec/support/schema.rb
|
218
|
+
- spec/validator/base_spec.rb
|