foreign_key_validation 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Coverage Status](https://coveralls.io/repos/marcusg/foreign_key_validation/badge.png?branch=master)](https://coveralls.io/r/marcusg/foreign_key_validation?branch=master)
|
4
4
|
[![Code Climate](https://codeclimate.com/github/marcusg/foreign_key_validation/badges/gpa.svg)](https://codeclimate.com/github/marcusg/foreign_key_validation)
|
5
5
|
[![Build Status](https://travis-ci.org/marcusg/foreign_key_validation.svg?branch=master)](https://travis-ci.org/marcusg/foreign_key_validation)
|
6
|
+
[![Gem Version](https://badge.fury.io/rb/foreign_key_validation.svg)](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
|