foreign_key_validation 1.0.0 → 1.0.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 +8 -8
- data/README.md +1 -0
- data/lib/foreign_key_validation/collector.rb +40 -0
- data/lib/foreign_key_validation/filter.rb +26 -0
- data/lib/foreign_key_validation/model_extension.rb +6 -22
- data/lib/foreign_key_validation/validator.rb +8 -32
- data/lib/foreign_key_validation/version.rb +1 -1
- data/lib/foreign_key_validation.rb +4 -0
- data/spec/models/validation_with_custom_attrs_spec.rb +69 -0
- data/spec/models/validation_with_exceptions_spec.rb +42 -0
- data/spec/models/validation_with_missing_foreign_key_spec.rb +63 -0
- data/spec/models/validation_without_attrs_spec.rb +107 -0
- data/spec/models/without_validation_spec.rb +82 -0
- metadata +14 -4
- data/spec/models/model_spec.rb +0 -337
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MTRkMDgxMDUxZmJiN2ViODhiNjc2MjlkMjkwNzU3NWE1NGYyNDMxMw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NWRmMDQzMmRkNTM4OTE5MGIzZTRmMjk3ODllMDU1ZmYyOGZjZTkyMw==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
M2NhODc1MzQ0NDEwNGMwZmU0MzRkN2E2ZWJjZDg5OTlhYzFkYzhlYWEyM2Yx
|
10
|
+
MTUwMTQ3ZWU3ZGM0OTIxNTZhZTQ1MTMyZTk5ZDUzOGI2NzhjZTA1Nzg0MTk2
|
11
|
+
NWNhNjQzYzllOTIwYWJlNGM1ODk1NjIwY2ZhZDc0YzczYjRiNmY=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ODBhMWQ2MmZiMmE2N2NkZDc3YmJmYjU2MGQwYjQzYTliNjg3NzBlMGJjMGIw
|
14
|
+
OGVjYjQ2YTk2ZDA1MzdmNzFkNDdiMTU0ODE1MWUwYzlkNmJiMGY4ZDI0YTUy
|
15
|
+
ZjFiMjRhNWNmMjc0N2Y0NDNiYjhhMjljYTkyM2ZkZDM1NzI4Yzg=
|
data/README.md
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
[](https://coveralls.io/r/marcusg/foreign_key_validation?branch=master)
|
4
4
|
[](https://codeclimate.com/github/marcusg/foreign_key_validation)
|
5
5
|
[](https://travis-ci.org/marcusg/foreign_key_validation)
|
6
|
+
[](http://badge.fury.io/rb/foreign_key_validation)
|
6
7
|
|
7
8
|
Protect your models by specifying a collection of relations that should be tested for consistency with a predefined column (e.g. `user_id`).This is useful when the column `user_id` is used in multiple models. We can check if the `user_id` of *model A* matches `user_id` of *model B* before saving the records - if the IDs are different, an error will be attached to the errors hash of checked model.
|
8
9
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ForeignKeyValidation
|
2
|
+
|
3
|
+
class Collector
|
4
|
+
attr_accessor :klass, :validate_against, :reflections, :reflection_names, :validate_against_key, :validate_with
|
5
|
+
|
6
|
+
DEFAULT_VALIDATE_AGAINST = :user
|
7
|
+
|
8
|
+
def initialize(opt={})
|
9
|
+
self.klass = opt[:klass]
|
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])
|
15
|
+
end
|
16
|
+
|
17
|
+
def check!
|
18
|
+
raise Errors::NoReleationFoundError.new(klass.name) if reflection_names.empty?
|
19
|
+
raise Errors::NoForeignKeyFoundError.new(validate_against, klass.table_name) unless reflection_names.include?(validate_against)
|
20
|
+
raise Errors::UnknownRelationError.new(validate_with) unless validate_with.all? {|k| reflection_names.include?(k)}
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def find_validate_against(on)
|
27
|
+
(on || DEFAULT_VALIDATE_AGAINST).to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
def find_validate_with(with)
|
31
|
+
((Array(with).map(&:to_s) if with) || reflection_names).reject {|n| n == validate_against}
|
32
|
+
end
|
33
|
+
|
34
|
+
def find_validate_against_key
|
35
|
+
reflections.select {|r| r.name.to_s == validate_against}.first.try(:foreign_key)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ForeignKeyValidation
|
2
|
+
|
3
|
+
class Filter
|
4
|
+
attr_accessor :collector
|
5
|
+
|
6
|
+
def initialize(collector)
|
7
|
+
self.collector = collector
|
8
|
+
end
|
9
|
+
|
10
|
+
def before_filter(&block)
|
11
|
+
collector.klass.send :define_method, filter_name do
|
12
|
+
self.instance_eval &block
|
13
|
+
end
|
14
|
+
collector.klass.send :private, filter_name.to_sym
|
15
|
+
collector.klass.send :before_validation, filter_name
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def filter_name
|
21
|
+
"validate_foreign_keys_on_#{collector.validate_against}"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -2,35 +2,19 @@ module ForeignKeyValidation
|
|
2
2
|
module ModelExtension
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
-
included do
|
6
|
-
private
|
7
|
-
def validate_foreign_key(validate_against_key, reflection_name)
|
8
|
-
return if send(reflection_name).try(validate_against_key).nil? or try(validate_against_key).nil?
|
9
|
-
|
10
|
-
if send(reflection_name).send(validate_against_key) != send(validate_against_key)
|
11
|
-
errors.add(validate_against_key, "#{validate_against_key} of #{reflection_name} does not match #{self.class.name.tableize} #{validate_against_key}.")
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
5
|
module ClassMethods
|
6
|
+
|
17
7
|
def validate_foreign_keys(opt={})
|
18
8
|
subclasses.map {|klass| klass.send(:validate_foreign_keys, opt)}
|
19
9
|
|
20
|
-
|
21
|
-
|
10
|
+
collector = Collector.new(opt.merge(klass: self))
|
11
|
+
collector.check!
|
22
12
|
|
23
|
-
|
24
|
-
|
25
|
-
validate_foreign_key(validator.validate_against_key, reflection_name)
|
26
|
-
end
|
13
|
+
Filter.new(collector).before_filter do
|
14
|
+
Validator.validate(validate_against_key: collector.validate_against_key, reflection_names: collector.validate_with, object: self)
|
27
15
|
end
|
28
|
-
private validator.filter_method_name.to_sym
|
29
|
-
|
30
|
-
before_validation validator.filter_method_name
|
31
16
|
end
|
17
|
+
|
32
18
|
end
|
33
19
|
end
|
34
20
|
end
|
35
|
-
|
36
|
-
ActiveRecord::Base.send :include, ForeignKeyValidation::ModelExtension
|
@@ -1,41 +1,17 @@
|
|
1
1
|
module ForeignKeyValidation
|
2
2
|
|
3
3
|
class Validator
|
4
|
-
attr_accessor :klass, :validate_against, :reflections, :reflection_names, :validate_against_key, :validate_with
|
5
4
|
|
6
|
-
|
5
|
+
def self.validate(opt={})
|
6
|
+
validate_against_key, reflection_names, object = opt[:validate_against_key], opt[:reflection_names], opt[:object]
|
7
7
|
|
8
|
-
|
9
|
-
|
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])
|
15
|
-
end
|
16
|
-
|
17
|
-
def check
|
18
|
-
raise Errors::NoReleationFoundError.new(klass.name) if reflection_names.empty?
|
19
|
-
raise Errors::NoForeignKeyFoundError.new(validate_against, klass.table_name) unless reflection_names.include?(validate_against)
|
20
|
-
raise Errors::UnknownRelationError.new(validate_with) unless validate_with.all? {|k| reflection_names.include?(k)}
|
21
|
-
end
|
22
|
-
|
23
|
-
def filter_method_name
|
24
|
-
"validate_foreign_keys_on_#{validate_against}"
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def find_validate_against(opt_on)
|
30
|
-
(opt_on || DEFAULT_VALIDATE_AGAINST).to_s
|
31
|
-
end
|
32
|
-
|
33
|
-
def find_validate_with(opt_with)
|
34
|
-
((Array(opt_with).map(&:to_s) if opt_with) || reflection_names).reject {|n| n == validate_against}
|
35
|
-
end
|
8
|
+
reflection_names.each do |reflection_name|
|
9
|
+
next if object.send(reflection_name).try(validate_against_key).nil? or object.try(validate_against_key).nil?
|
36
10
|
|
37
|
-
|
38
|
-
|
11
|
+
if object.send(reflection_name).send(validate_against_key) != object.send(validate_against_key)
|
12
|
+
object.errors.add(validate_against_key, "#{validate_against_key} of #{reflection_name} does not match #{object.class.name.tableize} #{validate_against_key}.")
|
13
|
+
end
|
14
|
+
end
|
39
15
|
end
|
40
16
|
|
41
17
|
end
|
@@ -1,4 +1,8 @@
|
|
1
1
|
require "foreign_key_validation/version"
|
2
2
|
require "foreign_key_validation/errors"
|
3
|
+
require "foreign_key_validation/collector"
|
4
|
+
require "foreign_key_validation/filter"
|
3
5
|
require "foreign_key_validation/validator"
|
4
6
|
require "foreign_key_validation/model_extension"
|
7
|
+
|
8
|
+
ActiveRecord::Base.send :include, ForeignKeyValidation::ModelExtension
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ForeignKeyValidation::ModelExtension do
|
4
|
+
|
5
|
+
let(:user) { User.create }
|
6
|
+
let(:other_user) { User.create }
|
7
|
+
|
8
|
+
context "with calling validation with custom attributes hash" do
|
9
|
+
before do
|
10
|
+
Comment.class_eval do
|
11
|
+
validate_foreign_keys on: :user, with: :issue # member is not in list - should be editable
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:project) { Project.create user: user }
|
16
|
+
let(:other_project) { Project.create user: other_user }
|
17
|
+
let(:issue) { Issue.create user: user, project: project }
|
18
|
+
let(:other_issue) { Issue.create user: other_user, project: other_project }
|
19
|
+
let(:comment) { Comment.create user: user, issue: issue }
|
20
|
+
let(:manager) { Manager.create user: user }
|
21
|
+
let(:other_manager) { Manager.create user: User.create }
|
22
|
+
|
23
|
+
it "uses same user ids by default" do
|
24
|
+
expect(project.user_id).to eq(user.id)
|
25
|
+
expect(issue.user_id).to eq(user.id)
|
26
|
+
expect(comment.user_id).to eq(user.id)
|
27
|
+
expect(manager.user_id).to eq(user.id)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "does not allow to rewrite issue id of comment" do
|
31
|
+
comment.issue_id = other_issue.id
|
32
|
+
comment.save
|
33
|
+
expect(comment.errors.messages.values.flatten).to include("user_id of issue does not match comments user_id.")
|
34
|
+
expect(comment.reload.issue_id).to_not eq(other_issue.id)
|
35
|
+
end
|
36
|
+
|
37
|
+
# NOTE: this is possible here because Issue model is not configured to check ids
|
38
|
+
# comment model can only check id against issue model if it is present
|
39
|
+
it "does allow to rewrite issue id of comment with random id" do
|
40
|
+
comment.issue_id = 42
|
41
|
+
comment.save
|
42
|
+
comment.reload
|
43
|
+
expect(comment.issue_id).to eq(42)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "allow to rewrite member id of comment" do
|
47
|
+
comment.member_id = other_manager.id
|
48
|
+
comment.save
|
49
|
+
comment.reload
|
50
|
+
expect(comment.member_id).to eq(other_manager.id)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "allow to rewrite user id of issue" do
|
54
|
+
issue.user_id = other_user.id
|
55
|
+
issue.save
|
56
|
+
issue.reload
|
57
|
+
expect(issue.user_id).to eq(other_user.id)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "allow to rewrite user id of manager" do
|
61
|
+
manager.user_id = other_user.id
|
62
|
+
manager.save
|
63
|
+
manager.reload
|
64
|
+
expect(manager.user_id).to eq(other_user.id)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ForeignKeyValidation::ModelExtension do
|
4
|
+
|
5
|
+
let(:user) { User.create }
|
6
|
+
let(:other_user) { User.create }
|
7
|
+
|
8
|
+
context "with calling private methods from model" do
|
9
|
+
before { Issue.send :validate_foreign_keys }
|
10
|
+
|
11
|
+
let(:issue) { Issue.create }
|
12
|
+
|
13
|
+
it "does not allow to call private validate_foreign_keys_on_* methods" do
|
14
|
+
expect{issue.validate_foreign_keys_on_user}.to raise_exception(/private method `validate_foreign_keys_on_user' called/)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "with calling validation and wrong attributes hash" do
|
19
|
+
|
20
|
+
it "raises error due to wrong :on key" do
|
21
|
+
expect{Idea.class_eval { validate_foreign_keys on: :not_existing }}.to raise_error("No foreign key for relation not_existing on ideas table!")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "raises error due to not related :on key" do
|
25
|
+
expect{Project.class_eval { validate_foreign_keys on: :comment }}.to raise_error("No foreign key for relation comment on projects table!")
|
26
|
+
end
|
27
|
+
|
28
|
+
it "raises error due to wrong :with key" do
|
29
|
+
expect{Idea.class_eval { validate_foreign_keys with: :not_existing }}.to raise_error('Unknown relation in ["not_existing"]!')
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
context "with calling validation and missing relations" do
|
35
|
+
|
36
|
+
it "raises error due to no existing relations" do
|
37
|
+
expect{Dummy.class_eval { validate_foreign_keys }}.to raise_error("Can't find any belongs_to relations for Dummy class. Put validation call below association definitions!")
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ForeignKeyValidation::ModelExtension do
|
4
|
+
|
5
|
+
let(:user) { User.create }
|
6
|
+
let(:other_user) { User.create }
|
7
|
+
|
8
|
+
context "with calling validation and missing foreign key on relation" do
|
9
|
+
|
10
|
+
before do
|
11
|
+
Issue.class_eval do
|
12
|
+
validate_foreign_keys
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:project) { Project.create }
|
17
|
+
let(:other_project) { Project.create }
|
18
|
+
let(:issue) { Issue.create project: project, user: user }
|
19
|
+
|
20
|
+
it "allow to rewrite user id of issue" do
|
21
|
+
issue.user_id = other_user.id
|
22
|
+
issue.save
|
23
|
+
issue.reload
|
24
|
+
expect(issue.user_id).to eq(other_user.id)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "allow to rewrite project id of issue" do
|
28
|
+
issue.project_id = other_project.id
|
29
|
+
issue.save
|
30
|
+
issue.reload
|
31
|
+
expect(issue.project_id).to eq(other_project.id)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
context "with calling validation and missing foreign key on self" do
|
37
|
+
|
38
|
+
before do
|
39
|
+
Issue.class_eval do
|
40
|
+
validate_foreign_keys
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
let(:project) { Project.create user: user }
|
45
|
+
let(:issue) { Issue.create project: project }
|
46
|
+
|
47
|
+
it "does not allow to rewrite user id of issue" do
|
48
|
+
issue.user_id = other_user.id
|
49
|
+
issue.save
|
50
|
+
expect(issue.errors.messages.values.flatten).to include("user_id of project does not match issues user_id.")
|
51
|
+
expect(issue.reload.user_id).to_not eq(other_user.id)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "does not allow to rewrite user id of issue with random id" do
|
55
|
+
issue.user_id = 42
|
56
|
+
issue.save
|
57
|
+
expect(issue.errors.messages.values.flatten).to include("user_id of project does not match issues user_id.")
|
58
|
+
expect(issue.reload.user_id).to_not eq(42)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ForeignKeyValidation::ModelExtension do
|
4
|
+
|
5
|
+
let(:user) { User.create }
|
6
|
+
let(:other_user) { User.create }
|
7
|
+
|
8
|
+
context "with calling validation" do
|
9
|
+
before do
|
10
|
+
Idea.send :validate_foreign_keys
|
11
|
+
Project.send :validate_foreign_keys
|
12
|
+
Issue.send :validate_foreign_keys
|
13
|
+
Comment.send :validate_foreign_keys
|
14
|
+
Member.send :validate_foreign_keys
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:project) { Project.create user: user }
|
18
|
+
let(:other_project) { Project.create user: other_user }
|
19
|
+
let(:idea) { Idea.create user: user, project: project }
|
20
|
+
let(:issue) { Issue.create user: user, project: project }
|
21
|
+
let(:other_issue) { Issue.create user: other_user, project: other_project }
|
22
|
+
let(:comment) { Comment.create user: user, issue: issue }
|
23
|
+
let(:manager) { Manager.create user: user }
|
24
|
+
let(:other_manager) { Manager.create user: User.create }
|
25
|
+
let(:developer) { Developer.create user: user, boss: manager }
|
26
|
+
|
27
|
+
it "uses same user ids by default" do
|
28
|
+
expect(project.user_id).to eq(user.id)
|
29
|
+
expect(idea.user_id).to eq(user.id)
|
30
|
+
expect(issue.user_id).to eq(user.id)
|
31
|
+
expect(comment.user_id).to eq(user.id)
|
32
|
+
expect(manager.user_id).to eq(user.id)
|
33
|
+
expect(developer.user_id).to eq(user.id)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "does not allow to rewrite user id of idea" do
|
37
|
+
idea.user_id = other_user.id
|
38
|
+
idea.save
|
39
|
+
expect(idea.errors.messages.values.flatten).to include("user_id of project does not match ideas user_id.")
|
40
|
+
expect(idea.reload.user_id).to_not eq(other_user.id)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "does not allow to rewrite user id of idea with random id" do
|
44
|
+
idea.user_id = 42
|
45
|
+
idea.save
|
46
|
+
expect(idea.errors.messages.values.flatten).to include("user_id of project does not match ideas user_id.")
|
47
|
+
expect(idea.reload.user_id).to_not eq(42)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "does not allow to rewrite project id of idea" do
|
51
|
+
idea.project_id = other_project.id
|
52
|
+
idea.save
|
53
|
+
expect(idea.errors.messages.values.flatten).to include("user_id of project does not match ideas user_id.")
|
54
|
+
expect(idea.reload.user_id).to_not eq(other_project.id)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "does not allow to rewrite user id of issue" do
|
58
|
+
issue.user_id = other_user.id
|
59
|
+
issue.save
|
60
|
+
expect(issue.errors.messages.values.flatten).to include("user_id of project does not match issues user_id.")
|
61
|
+
expect(issue.reload.user_id).to_not eq(other_user.id)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "does not allow to rewrite project id of issue" do
|
65
|
+
issue.project_id = other_project.id
|
66
|
+
issue.save
|
67
|
+
expect(issue.errors.messages.values.flatten).to include("user_id of project does not match issues user_id.")
|
68
|
+
expect(issue.reload.user_id).to_not eq(other_project.id)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "does not allow to rewrite user id of comment" do
|
72
|
+
comment.user_id = other_user.id
|
73
|
+
comment.save
|
74
|
+
expect(comment.errors.messages.values.flatten).to include("user_id of issue does not match comments user_id.")
|
75
|
+
expect(comment.reload.user_id).to_not eq(other_user.id)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "does not allow to rewrite issue id of comment" do
|
79
|
+
comment.issue_id = other_issue.id
|
80
|
+
comment.save
|
81
|
+
expect(comment.errors.messages.values.flatten).to include("user_id of issue does not match comments user_id.")
|
82
|
+
expect(comment.reload.user_id).to_not eq(other_issue.id)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "does allow to rewrite user id of project" do
|
86
|
+
project.user_id = other_user.id
|
87
|
+
project.save
|
88
|
+
expect(project.errors).to be_empty
|
89
|
+
expect(project.reload.user_id).to eq(other_user.id)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "does not allow to rewrite user id of developer" do
|
93
|
+
developer.user_id = other_user.id
|
94
|
+
developer.save
|
95
|
+
expect(developer.errors.messages.values.flatten).to include("user_id of boss does not match developers user_id.")
|
96
|
+
expect(developer.reload.user_id).to_not eq(other_user.id)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "does not allow to rewrite boss id of developer" do
|
100
|
+
developer.custom_boss_id = other_manager.id
|
101
|
+
developer.save
|
102
|
+
expect(developer.errors.messages.values.flatten).to include("user_id of boss does not match developers user_id.")
|
103
|
+
expect(developer.reload.custom_boss_id).to_not eq(other_manager.id)
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ForeignKeyValidation::ModelExtension do
|
4
|
+
|
5
|
+
# NOTE: it's important to not create the objects through relation (user.projects.create...)
|
6
|
+
# it looks like active_record is caching the classes - but we need to test different class configs
|
7
|
+
|
8
|
+
let(:user) { User.create }
|
9
|
+
let(:other_user) { User.create }
|
10
|
+
|
11
|
+
context "without calling validation" do
|
12
|
+
|
13
|
+
let(:project) { Project.create user: user }
|
14
|
+
let(:idea) { Idea.create user: user, project: project }
|
15
|
+
let(:issue) { Issue.create user: user, project: project }
|
16
|
+
let(:comment) { Comment.create user: user, issue: issue }
|
17
|
+
let(:manager) { Manager.create user: user }
|
18
|
+
let(:other_manager) { Manager.create user: User.create }
|
19
|
+
let(:developer) { Developer.create user: user, boss: manager }
|
20
|
+
|
21
|
+
it "uses same user ids by default" do
|
22
|
+
expect(project.user_id).to eq(user.id)
|
23
|
+
expect(idea.user_id).to eq(user.id)
|
24
|
+
expect(issue.user_id).to eq(user.id)
|
25
|
+
expect(comment.user_id).to eq(user.id)
|
26
|
+
expect(manager.user_id).to eq(user.id)
|
27
|
+
expect(developer.user_id).to eq(user.id)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "allow to rewrite user id of idea with random id" do
|
31
|
+
idea.user_id = 42
|
32
|
+
idea.save
|
33
|
+
idea.reload
|
34
|
+
expect(idea.user_id).to eq(42)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "allow to rewrite user id of idea" do
|
38
|
+
idea.user_id = other_user.id
|
39
|
+
idea.save
|
40
|
+
idea.reload
|
41
|
+
expect(idea.user_id).to eq(other_user.id)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "allow to rewrite user id of project" do
|
45
|
+
project.user_id = other_user.id
|
46
|
+
project.save
|
47
|
+
project.reload
|
48
|
+
expect(project.user_id).to eq(other_user.id)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "allow to rewrite user id of issue" do
|
52
|
+
issue.user_id = other_user.id
|
53
|
+
issue.save
|
54
|
+
issue.reload
|
55
|
+
expect(issue.user_id).to eq(other_user.id)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "allow to rewrite user id of comment" do
|
59
|
+
comment.user_id = other_user.id
|
60
|
+
comment.save
|
61
|
+
comment.reload
|
62
|
+
expect(comment.user_id).to eq(other_user.id)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "allow to rewrite user id of developer" do
|
66
|
+
developer.user_id = other_user.id
|
67
|
+
developer.save
|
68
|
+
developer.reload
|
69
|
+
expect(developer.user_id).to eq(other_user.id)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "allow to rewrite boss id of developer" do
|
73
|
+
developer.custom_boss_id = other_user.id
|
74
|
+
developer.save
|
75
|
+
developer.reload
|
76
|
+
expect(developer.custom_boss_id).to eq(other_user.id)
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
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.0.1
|
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-
|
11
|
+
date: 2014-10-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -161,11 +161,17 @@ files:
|
|
161
161
|
- gemfiles/4.1.gemfile
|
162
162
|
- gemfiles/4.2.gemfile
|
163
163
|
- lib/foreign_key_validation.rb
|
164
|
+
- lib/foreign_key_validation/collector.rb
|
164
165
|
- lib/foreign_key_validation/errors.rb
|
166
|
+
- lib/foreign_key_validation/filter.rb
|
165
167
|
- lib/foreign_key_validation/model_extension.rb
|
166
168
|
- lib/foreign_key_validation/validator.rb
|
167
169
|
- lib/foreign_key_validation/version.rb
|
168
|
-
- spec/models/
|
170
|
+
- spec/models/validation_with_custom_attrs_spec.rb
|
171
|
+
- spec/models/validation_with_exceptions_spec.rb
|
172
|
+
- spec/models/validation_with_missing_foreign_key_spec.rb
|
173
|
+
- spec/models/validation_without_attrs_spec.rb
|
174
|
+
- spec/models/without_validation_spec.rb
|
169
175
|
- spec/spec_helper.rb
|
170
176
|
- spec/support/load_models.rb
|
171
177
|
- spec/support/reset_models.rb
|
@@ -195,7 +201,11 @@ signing_key:
|
|
195
201
|
specification_version: 4
|
196
202
|
summary: Protect the foreign keys in your Rails models.
|
197
203
|
test_files:
|
198
|
-
- spec/models/
|
204
|
+
- spec/models/validation_with_custom_attrs_spec.rb
|
205
|
+
- spec/models/validation_with_exceptions_spec.rb
|
206
|
+
- spec/models/validation_with_missing_foreign_key_spec.rb
|
207
|
+
- spec/models/validation_without_attrs_spec.rb
|
208
|
+
- spec/models/without_validation_spec.rb
|
199
209
|
- spec/spec_helper.rb
|
200
210
|
- spec/support/load_models.rb
|
201
211
|
- spec/support/reset_models.rb
|
data/spec/models/model_spec.rb
DELETED
@@ -1,337 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe ForeignKeyValidation::ModelExtension do
|
4
|
-
|
5
|
-
# NOTE: it's important to not create the objects through relation (user.projects.create...)
|
6
|
-
# it looks like active_record is caching the classes - but we need to test different class configs
|
7
|
-
|
8
|
-
let(:user) { User.create }
|
9
|
-
let(:other_user) { User.create }
|
10
|
-
|
11
|
-
context "without calling validation" do
|
12
|
-
|
13
|
-
let(:project) { Project.create user: user }
|
14
|
-
let(:idea) { Idea.create user: user, project: project }
|
15
|
-
let(:issue) { Issue.create user: user, project: project }
|
16
|
-
let(:comment) { Comment.create user: user, issue: issue }
|
17
|
-
let(:manager) { Manager.create user: user }
|
18
|
-
let(:other_manager) { Manager.create user: User.create }
|
19
|
-
let(:developer) { Developer.create user: user, boss: manager }
|
20
|
-
|
21
|
-
it "uses same user ids by default" do
|
22
|
-
expect(project.user_id).to eq(user.id)
|
23
|
-
expect(idea.user_id).to eq(user.id)
|
24
|
-
expect(issue.user_id).to eq(user.id)
|
25
|
-
expect(comment.user_id).to eq(user.id)
|
26
|
-
expect(manager.user_id).to eq(user.id)
|
27
|
-
expect(developer.user_id).to eq(user.id)
|
28
|
-
end
|
29
|
-
|
30
|
-
it "allow to rewrite user id of idea with random id" do
|
31
|
-
idea.user_id = 42
|
32
|
-
idea.save
|
33
|
-
idea.reload
|
34
|
-
expect(idea.user_id).to eq(42)
|
35
|
-
end
|
36
|
-
|
37
|
-
it "allow to rewrite user id of idea" do
|
38
|
-
idea.user_id = other_user.id
|
39
|
-
idea.save
|
40
|
-
idea.reload
|
41
|
-
expect(idea.user_id).to eq(other_user.id)
|
42
|
-
end
|
43
|
-
|
44
|
-
it "allow to rewrite user id of project" do
|
45
|
-
project.user_id = other_user.id
|
46
|
-
project.save
|
47
|
-
project.reload
|
48
|
-
expect(project.user_id).to eq(other_user.id)
|
49
|
-
end
|
50
|
-
|
51
|
-
it "allow to rewrite user id of issue" do
|
52
|
-
issue.user_id = other_user.id
|
53
|
-
issue.save
|
54
|
-
issue.reload
|
55
|
-
expect(issue.user_id).to eq(other_user.id)
|
56
|
-
end
|
57
|
-
|
58
|
-
it "allow to rewrite user id of comment" do
|
59
|
-
comment.user_id = other_user.id
|
60
|
-
comment.save
|
61
|
-
comment.reload
|
62
|
-
expect(comment.user_id).to eq(other_user.id)
|
63
|
-
end
|
64
|
-
|
65
|
-
it "allow to rewrite user id of developer" do
|
66
|
-
developer.user_id = other_user.id
|
67
|
-
developer.save
|
68
|
-
developer.reload
|
69
|
-
expect(developer.user_id).to eq(other_user.id)
|
70
|
-
end
|
71
|
-
|
72
|
-
it "allow to rewrite boss id of developer" do
|
73
|
-
developer.custom_boss_id = other_user.id
|
74
|
-
developer.save
|
75
|
-
developer.reload
|
76
|
-
expect(developer.custom_boss_id).to eq(other_user.id)
|
77
|
-
end
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
context "with calling validation" do
|
82
|
-
before do
|
83
|
-
Idea.send :validate_foreign_keys
|
84
|
-
Project.send :validate_foreign_keys
|
85
|
-
Issue.send :validate_foreign_keys
|
86
|
-
Comment.send :validate_foreign_keys
|
87
|
-
Member.send :validate_foreign_keys
|
88
|
-
end
|
89
|
-
|
90
|
-
let(:project) { Project.create user: user }
|
91
|
-
let(:other_project) { Project.create user: other_user }
|
92
|
-
let(:idea) { Idea.create user: user, project: project }
|
93
|
-
let(:issue) { Issue.create user: user, project: project }
|
94
|
-
let(:other_issue) { Issue.create user: other_user, project: other_project }
|
95
|
-
let(:comment) { Comment.create user: user, issue: issue }
|
96
|
-
let(:manager) { Manager.create user: user }
|
97
|
-
let(:other_manager) { Manager.create user: User.create }
|
98
|
-
let(:developer) { Developer.create user: user, boss: manager }
|
99
|
-
|
100
|
-
it "uses same user ids by default" do
|
101
|
-
expect(project.user_id).to eq(user.id)
|
102
|
-
expect(idea.user_id).to eq(user.id)
|
103
|
-
expect(issue.user_id).to eq(user.id)
|
104
|
-
expect(comment.user_id).to eq(user.id)
|
105
|
-
expect(manager.user_id).to eq(user.id)
|
106
|
-
expect(developer.user_id).to eq(user.id)
|
107
|
-
end
|
108
|
-
|
109
|
-
it "does not allow to rewrite user id of idea" do
|
110
|
-
idea.user_id = other_user.id
|
111
|
-
idea.save
|
112
|
-
expect(idea.errors.messages.values.flatten).to include("user_id of project does not match ideas user_id.")
|
113
|
-
expect(idea.reload.user_id).to_not eq(other_user.id)
|
114
|
-
end
|
115
|
-
|
116
|
-
it "does not allow to rewrite user id of idea with random id" do
|
117
|
-
idea.user_id = 42
|
118
|
-
idea.save
|
119
|
-
expect(idea.errors.messages.values.flatten).to include("user_id of project does not match ideas user_id.")
|
120
|
-
expect(idea.reload.user_id).to_not eq(42)
|
121
|
-
end
|
122
|
-
|
123
|
-
it "does not allow to rewrite project id of idea" do
|
124
|
-
idea.project_id = other_project.id
|
125
|
-
idea.save
|
126
|
-
expect(idea.errors.messages.values.flatten).to include("user_id of project does not match ideas user_id.")
|
127
|
-
expect(idea.reload.user_id).to_not eq(other_project.id)
|
128
|
-
end
|
129
|
-
|
130
|
-
it "does not allow to rewrite user id of issue" do
|
131
|
-
issue.user_id = other_user.id
|
132
|
-
issue.save
|
133
|
-
expect(issue.errors.messages.values.flatten).to include("user_id of project does not match issues user_id.")
|
134
|
-
expect(issue.reload.user_id).to_not eq(other_user.id)
|
135
|
-
end
|
136
|
-
|
137
|
-
it "does not allow to rewrite project id of issue" do
|
138
|
-
issue.project_id = other_project.id
|
139
|
-
issue.save
|
140
|
-
expect(issue.errors.messages.values.flatten).to include("user_id of project does not match issues user_id.")
|
141
|
-
expect(issue.reload.user_id).to_not eq(other_project.id)
|
142
|
-
end
|
143
|
-
|
144
|
-
it "does not allow to rewrite user id of comment" do
|
145
|
-
comment.user_id = other_user.id
|
146
|
-
comment.save
|
147
|
-
expect(comment.errors.messages.values.flatten).to include("user_id of issue does not match comments user_id.")
|
148
|
-
expect(comment.reload.user_id).to_not eq(other_user.id)
|
149
|
-
end
|
150
|
-
|
151
|
-
it "does not allow to rewrite issue id of comment" do
|
152
|
-
comment.issue_id = other_issue.id
|
153
|
-
comment.save
|
154
|
-
expect(comment.errors.messages.values.flatten).to include("user_id of issue does not match comments user_id.")
|
155
|
-
expect(comment.reload.user_id).to_not eq(other_issue.id)
|
156
|
-
end
|
157
|
-
|
158
|
-
it "does allow to rewrite user id of project" do
|
159
|
-
project.user_id = other_user.id
|
160
|
-
project.save
|
161
|
-
expect(project.errors).to be_empty
|
162
|
-
expect(project.reload.user_id).to eq(other_user.id)
|
163
|
-
end
|
164
|
-
|
165
|
-
it "does not allow to rewrite user id of developer" do
|
166
|
-
developer.user_id = other_user.id
|
167
|
-
developer.save
|
168
|
-
expect(developer.errors.messages.values.flatten).to include("user_id of boss does not match developers user_id.")
|
169
|
-
expect(developer.reload.user_id).to_not eq(other_user.id)
|
170
|
-
end
|
171
|
-
|
172
|
-
it "does not allow to rewrite boss id of developer" do
|
173
|
-
developer.custom_boss_id = other_manager.id
|
174
|
-
developer.save
|
175
|
-
expect(developer.errors.messages.values.flatten).to include("user_id of boss does not match developers user_id.")
|
176
|
-
expect(developer.reload.custom_boss_id).to_not eq(other_manager.id)
|
177
|
-
end
|
178
|
-
|
179
|
-
end
|
180
|
-
|
181
|
-
context "with calling private methods from model" do
|
182
|
-
before { Issue.send :validate_foreign_keys }
|
183
|
-
|
184
|
-
let(:issue) { Issue.create }
|
185
|
-
|
186
|
-
it "does not allow to call private validate_foreign_key method" do
|
187
|
-
expect{issue.validate_foreign_key("test", "unrat")}.to raise_exception(/private method `validate_foreign_key' called/)
|
188
|
-
end
|
189
|
-
|
190
|
-
it "does not allow to call private validate_foreign_keys_on_* methods" do
|
191
|
-
expect{issue.validate_foreign_keys_on_user}.to raise_exception(/private method `validate_foreign_keys_on_user' called/)
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
context "with calling validation with custom attributes hash" do
|
196
|
-
before do
|
197
|
-
Comment.class_eval do
|
198
|
-
validate_foreign_keys on: :user, with: :issue # member is not in list - should be editable
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
let(:project) { Project.create user: user }
|
203
|
-
let(:other_project) { Project.create user: other_user }
|
204
|
-
let(:issue) { Issue.create user: user, project: project }
|
205
|
-
let(:other_issue) { Issue.create user: other_user, project: other_project }
|
206
|
-
let(:comment) { Comment.create user: user, issue: issue }
|
207
|
-
let(:manager) { Manager.create user: user }
|
208
|
-
let(:other_manager) { Manager.create user: User.create }
|
209
|
-
|
210
|
-
it "uses same user ids by default" do
|
211
|
-
expect(project.user_id).to eq(user.id)
|
212
|
-
expect(issue.user_id).to eq(user.id)
|
213
|
-
expect(comment.user_id).to eq(user.id)
|
214
|
-
expect(manager.user_id).to eq(user.id)
|
215
|
-
end
|
216
|
-
|
217
|
-
it "does not allow to rewrite issue id of comment" do
|
218
|
-
comment.issue_id = other_issue.id
|
219
|
-
comment.save
|
220
|
-
expect(comment.errors.messages.values.flatten).to include("user_id of issue does not match comments user_id.")
|
221
|
-
expect(comment.reload.issue_id).to_not eq(other_issue.id)
|
222
|
-
end
|
223
|
-
|
224
|
-
# NOTE: this is possible here because Issue model is not configured to check ids
|
225
|
-
# comment model can only check id against issue model if it is present
|
226
|
-
it "does allow to rewrite issue id of comment with random id" do
|
227
|
-
comment.issue_id = 42
|
228
|
-
comment.save
|
229
|
-
comment.reload
|
230
|
-
expect(comment.issue_id).to eq(42)
|
231
|
-
end
|
232
|
-
|
233
|
-
it "allow to rewrite member id of comment" do
|
234
|
-
comment.member_id = other_manager.id
|
235
|
-
comment.save
|
236
|
-
comment.reload
|
237
|
-
expect(comment.member_id).to eq(other_manager.id)
|
238
|
-
end
|
239
|
-
|
240
|
-
it "allow to rewrite user id of issue" do
|
241
|
-
issue.user_id = other_user.id
|
242
|
-
issue.save
|
243
|
-
issue.reload
|
244
|
-
expect(issue.user_id).to eq(other_user.id)
|
245
|
-
end
|
246
|
-
|
247
|
-
it "allow to rewrite user id of manager" do
|
248
|
-
manager.user_id = other_user.id
|
249
|
-
manager.save
|
250
|
-
manager.reload
|
251
|
-
expect(manager.user_id).to eq(other_user.id)
|
252
|
-
end
|
253
|
-
|
254
|
-
end
|
255
|
-
|
256
|
-
context "with calling validation and wrong attributes hash" do
|
257
|
-
|
258
|
-
it "raises error due to wrong :on key" do
|
259
|
-
expect{Idea.class_eval { validate_foreign_keys on: :not_existing }}.to raise_error("No foreign key for relation not_existing on ideas table!")
|
260
|
-
end
|
261
|
-
|
262
|
-
it "raises error due to not related :on key" do
|
263
|
-
expect{Project.class_eval { validate_foreign_keys on: :comment }}.to raise_error("No foreign key for relation comment on projects table!")
|
264
|
-
end
|
265
|
-
|
266
|
-
it "raises error due to wrong :with key" do
|
267
|
-
expect{Idea.class_eval { validate_foreign_keys with: :not_existing }}.to raise_error('Unknown relation in ["not_existing"]!')
|
268
|
-
end
|
269
|
-
|
270
|
-
end
|
271
|
-
|
272
|
-
context "with calling validation and missing relations" do
|
273
|
-
|
274
|
-
it "raises error due to no existing relations" do
|
275
|
-
expect{Dummy.class_eval { validate_foreign_keys }}.to raise_error("Can't find any belongs_to relations for Dummy class. Put validation call below association definitions!")
|
276
|
-
end
|
277
|
-
|
278
|
-
end
|
279
|
-
|
280
|
-
|
281
|
-
context "with calling validation and missing foreign key on self" do
|
282
|
-
|
283
|
-
before do
|
284
|
-
Issue.class_eval do
|
285
|
-
validate_foreign_keys
|
286
|
-
end
|
287
|
-
end
|
288
|
-
|
289
|
-
let(:project) { Project.create user: user }
|
290
|
-
let(:issue) { Issue.create project: project }
|
291
|
-
|
292
|
-
it "does not allow to rewrite user id of issue" do
|
293
|
-
issue.user_id = other_user.id
|
294
|
-
issue.save
|
295
|
-
expect(issue.errors.messages.values.flatten).to include("user_id of project does not match issues user_id.")
|
296
|
-
expect(issue.reload.user_id).to_not eq(other_user.id)
|
297
|
-
end
|
298
|
-
|
299
|
-
it "does not allow to rewrite user id of issue with random id" do
|
300
|
-
issue.user_id = 42
|
301
|
-
issue.save
|
302
|
-
expect(issue.errors.messages.values.flatten).to include("user_id of project does not match issues user_id.")
|
303
|
-
expect(issue.reload.user_id).to_not eq(42)
|
304
|
-
end
|
305
|
-
|
306
|
-
|
307
|
-
end
|
308
|
-
|
309
|
-
context "with calling validation and missing foreign key on relation" do
|
310
|
-
|
311
|
-
before do
|
312
|
-
Issue.class_eval do
|
313
|
-
validate_foreign_keys
|
314
|
-
end
|
315
|
-
end
|
316
|
-
|
317
|
-
let(:project) { Project.create }
|
318
|
-
let(:other_project) { Project.create }
|
319
|
-
let(:issue) { Issue.create project: project, user: user }
|
320
|
-
|
321
|
-
it "allow to rewrite user id of issue" do
|
322
|
-
issue.user_id = other_user.id
|
323
|
-
issue.save
|
324
|
-
issue.reload
|
325
|
-
expect(issue.user_id).to eq(other_user.id)
|
326
|
-
end
|
327
|
-
|
328
|
-
it "allow to rewrite project id of issue" do
|
329
|
-
issue.project_id = other_project.id
|
330
|
-
issue.save
|
331
|
-
issue.reload
|
332
|
-
expect(issue.project_id).to eq(other_project.id)
|
333
|
-
end
|
334
|
-
|
335
|
-
end
|
336
|
-
|
337
|
-
end
|