foreign_key_validation 0.0.8 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.coveralls.yml +2 -0
- data/.gitignore +1 -0
- data/.travis.yml +14 -0
- data/Appraisals +19 -0
- data/README.md +16 -5
- data/foreign_key_validation.gemspec +15 -12
- data/gemfiles/3.2.gemfile +7 -0
- data/gemfiles/4.0.gemfile +7 -0
- data/gemfiles/4.1.gemfile +7 -0
- data/gemfiles/4.2.gemfile +7 -0
- data/lib/foreign_key_validation/errors.rb +25 -0
- data/lib/foreign_key_validation/model_extension.rb +8 -15
- data/lib/foreign_key_validation/validator.rb +43 -0
- data/lib/foreign_key_validation/version.rb +1 -1
- data/lib/foreign_key_validation.rb +2 -0
- data/spec/models/model_spec.rb +148 -51
- data/spec/spec_helper.rb +16 -0
- data/spec/support/load_models.rb +2 -0
- data/spec/support/reset_models.rb +3 -8
- data/spec/support/schema.rb +1 -0
- metadata +55 -4
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZjUwMTlkNzAzZDliZTUxOGViNzJmNDA4YTVhNGY5NzEzNjdlYzJkZg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
OWJkZjlkM2I4NWIyYmY3Y2IyZDRjZDQ2NTk3MjBjZDQ0YzkyMjFmNw==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
OTBmZmRhZDZlZDI5MDc1ZWM3Y2ExOWMxODIzZmQ1YjI3YmFkZjJkYTg2OTI3
|
10
|
+
NTYxNWNhZmJmOTAyYzI2NjhiNTY4ODYzYjdhNjIxM2JiZTEyNjhhNTI0ZTcx
|
11
|
+
MDEwZDc5ZDc1ZWE2M2EyZDUxMmQyNDMxYTdkYzk0M2M3ZGFlMTQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MmVhODBlOWU0MzQxNTRhYjIyN2U4MGJhNjViZDUwNzc3MTIyNDljY2M2MGE3
|
14
|
+
NjJjZjUwODEzNTJiMjM4ZGYwMTI2MTNmNWMwZmQ0YjhiY2FlMDVjOWM5OTFj
|
15
|
+
YmMwM2ZkN2NkZjMxOTc1Y2VlZDBhOThjNmI1YzZkZTMzMDJjNTk=
|
data/.coveralls.yml
ADDED
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Appraisals
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
appraise "3.2" do
|
2
|
+
gem "rails", "~> 3.2.0"
|
3
|
+
gemspec
|
4
|
+
end
|
5
|
+
|
6
|
+
appraise "4.0" do
|
7
|
+
gem "rails", "~> 4.0.0"
|
8
|
+
gemspec
|
9
|
+
end
|
10
|
+
|
11
|
+
appraise "4.1" do
|
12
|
+
gem "rails", "~> 4.1.0"
|
13
|
+
gemspec
|
14
|
+
end
|
15
|
+
|
16
|
+
appraise "4.2" do
|
17
|
+
gem "rails", "~> 4.2.0.beta2"
|
18
|
+
gemspec
|
19
|
+
end
|
data/README.md
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
# foreign_key_validation
|
2
2
|
|
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
|
+
[![Code Climate](https://codeclimate.com/github/marcusg/foreign_key_validation/badges/gpa.svg)](https://codeclimate.com/github/marcusg/foreign_key_validation)
|
5
|
+
[![Build Status](https://travis-ci.org/marcusg/foreign_key_validation.svg?branch=master)](https://travis-ci.org/marcusg/foreign_key_validation)
|
6
|
+
|
7
|
+
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.
|
4
8
|
|
5
9
|
## Requirements
|
6
|
-
ruby >= 1.9
|
7
|
-
rails
|
8
|
-
active_record
|
10
|
+
ruby >= 1.9.3
|
11
|
+
rails >= 3.2.0
|
9
12
|
|
10
13
|
## Installation
|
11
14
|
|
@@ -23,7 +26,7 @@ Or install it yourself as:
|
|
23
26
|
|
24
27
|
## Usage
|
25
28
|
|
26
|
-
Call `validate_foreign_keys` below the association definitions (`belongs_to`, ...) in your model. By default it assumes that it should check all `belongs_to` relations against the `user_id` column. So any relation will be checked for a matching `user_id`.
|
29
|
+
Call `validate_foreign_keys` below the association definitions (`belongs_to`, ...) in your model. By default it assumes that it should check all `belongs_to` relations against the `user_id` column. So any relation will be checked for a matching `user_id`.
|
27
30
|
|
28
31
|
Change behaviour by calling `validate_foreign_keys` with arguments hash.
|
29
32
|
|
@@ -35,6 +38,14 @@ This would only check `model.project.admin_user_id` to match `model.admin_user_i
|
|
35
38
|
|
36
39
|
Only tested with ActiveRecord
|
37
40
|
|
41
|
+
## Tests
|
42
|
+
|
43
|
+
Use these commands to run the testsuite against different versions of Rails
|
44
|
+
|
45
|
+
bundle
|
46
|
+
appraisal install
|
47
|
+
appraisal rspec
|
48
|
+
|
38
49
|
|
39
50
|
## Contributing
|
40
51
|
|
@@ -4,25 +4,28 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'foreign_key_validation/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'foreign_key_validation'
|
8
8
|
spec.version = ForeignKeyValidation::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
9
|
+
spec.authors = ['Marcus Geißler']
|
10
|
+
spec.email = ['marcus3006@gmail.com']
|
11
11
|
spec.summary = %q{Protect the foreign keys in your Rails models.}
|
12
12
|
spec.description = %q{Protect the foreign keys in your Rails models by checking the user_id of self against the user_id of all relations.}
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
13
|
+
spec.homepage = 'https://github.com/marcusg/foreign_key_validation'
|
14
|
+
spec.license = 'MIT'
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = [
|
19
|
+
spec.require_paths = ['lib']
|
20
20
|
|
21
|
-
spec.
|
22
|
-
spec.add_development_dependency "bundler", "~> 1.6"
|
23
|
-
spec.add_development_dependency "rake", "~> 10.1"
|
24
|
-
spec.add_development_dependency "rspec-rails", "~> 3.0"
|
25
|
-
spec.add_development_dependency "sqlite3", '~> 1.3'
|
26
|
-
spec.add_development_dependency "pry", '~> 0.10'
|
21
|
+
spec.add_runtime_dependency "rails", ">= 3.2"
|
27
22
|
|
23
|
+
spec.add_development_dependency 'appraisal', '~> 1.0'
|
24
|
+
spec.add_development_dependency 'coveralls', '~> 0.7'
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
26
|
+
spec.add_development_dependency 'rake', '~> 10.1'
|
27
|
+
spec.add_development_dependency 'rspec-rails', '~> 3.0'
|
28
|
+
spec.add_development_dependency 'database_cleaner', '~> 1.3'
|
29
|
+
spec.add_development_dependency 'sqlite3', '~> 1.3'
|
30
|
+
spec.add_development_dependency 'pry', '~> 0.10'
|
28
31
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ForeignKeyValidation
|
2
|
+
module Errors
|
3
|
+
|
4
|
+
class NoReleationFoundError < StandardError
|
5
|
+
def initialize(name)
|
6
|
+
super("Can't find any belongs_to relations for #{name} class. Put validation call below association definitions!")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class NoForeignKeyFoundError < StandardError
|
11
|
+
def initialize(validate_against, table_name)
|
12
|
+
super("No foreign key for relation #{validate_against} on #{table_name} table!")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class UnknownRelationError < StandardError
|
17
|
+
def initialize(validate_with)
|
18
|
+
super("Unknown relation in #{validate_with}!")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
@@ -15,26 +15,19 @@ module ForeignKeyValidation
|
|
15
15
|
|
16
16
|
module ClassMethods
|
17
17
|
def validate_foreign_keys(opt={})
|
18
|
-
subclasses.map {|klass| klass.send(:validate_foreign_keys, opt)
|
18
|
+
subclasses.map {|klass| klass.send(:validate_foreign_keys, opt)}
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
reflection_names = reflections.map(&:name).map(&:to_s)
|
23
|
-
validate_against_key = reflections.select {|r| r.name.to_s == validate_against}.first.try(:foreign_key)
|
24
|
-
validate_with = ((Array(opt[:with]).map(&:to_s) if opt[:with]) || reflection_names).reject {|n| n == validate_against}
|
20
|
+
validator = Validator.new(self, opt)
|
21
|
+
validator.check
|
25
22
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
define_method "validate_foreign_keys_on_#{validate_against}" do
|
31
|
-
validate_with.each do |reflection_name|
|
32
|
-
validate_foreign_key(validate_against_key, reflection_name)
|
23
|
+
define_method validator.filter_method_name do
|
24
|
+
validator.validate_with.each do |reflection_name|
|
25
|
+
validate_foreign_key(validator.validate_against_key, reflection_name)
|
33
26
|
end
|
34
27
|
end
|
35
|
-
private
|
28
|
+
private validator.filter_method_name.to_sym
|
36
29
|
|
37
|
-
before_validation
|
30
|
+
before_validation validator.filter_method_name
|
38
31
|
end
|
39
32
|
end
|
40
33
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module ForeignKeyValidation
|
2
|
+
|
3
|
+
class Validator
|
4
|
+
attr_accessor :klass, :validate_against, :reflections, :reflection_names, :validate_against_key, :validate_with
|
5
|
+
|
6
|
+
DEFAULT_VALIDATE_AGAINST = :user
|
7
|
+
|
8
|
+
def initialize(klass, opt={})
|
9
|
+
self.klass = 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
|
+
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
|
36
|
+
|
37
|
+
def find_validate_against_key
|
38
|
+
reflections.select {|r| r.name.to_s == validate_against}.first.try(:foreign_key)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
data/spec/models/model_spec.rb
CHANGED
@@ -5,55 +5,75 @@ describe ForeignKeyValidation::ModelExtension do
|
|
5
5
|
# NOTE: it's important to not create the objects through relation (user.projects.create...)
|
6
6
|
# it looks like active_record is caching the classes - but we need to test different class configs
|
7
7
|
|
8
|
+
let(:user) { User.create }
|
9
|
+
let(:other_user) { User.create }
|
10
|
+
|
8
11
|
context "without calling validation" do
|
9
12
|
|
10
|
-
let(:user) { User.create }
|
11
13
|
let(:project) { Project.create user: user }
|
12
14
|
let(:idea) { Idea.create user: user, project: project }
|
13
15
|
let(:issue) { Issue.create user: user, project: project }
|
14
16
|
let(:comment) { Comment.create user: user, issue: issue }
|
15
|
-
# sti model
|
16
17
|
let(:manager) { Manager.create user: user }
|
18
|
+
let(:other_manager) { Manager.create user: User.create }
|
17
19
|
let(:developer) { Developer.create user: user, boss: manager }
|
18
20
|
|
19
21
|
it "uses same user ids by default" do
|
20
22
|
expect(project.user_id).to eq(user.id)
|
21
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)
|
22
28
|
end
|
23
29
|
|
24
|
-
it "allow to rewrite user id of idea" do
|
30
|
+
it "allow to rewrite user id of idea with random id" do
|
25
31
|
idea.user_id = 42
|
26
32
|
idea.save
|
27
33
|
idea.reload
|
28
34
|
expect(idea.user_id).to eq(42)
|
29
35
|
end
|
30
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
|
+
|
31
44
|
it "allow to rewrite user id of project" do
|
32
|
-
project.user_id =
|
45
|
+
project.user_id = other_user.id
|
33
46
|
project.save
|
34
47
|
project.reload
|
35
|
-
expect(project.user_id).to eq(
|
48
|
+
expect(project.user_id).to eq(other_user.id)
|
36
49
|
end
|
37
50
|
|
38
51
|
it "allow to rewrite user id of issue" do
|
39
|
-
issue.user_id =
|
52
|
+
issue.user_id = other_user.id
|
40
53
|
issue.save
|
41
54
|
issue.reload
|
42
|
-
expect(issue.user_id).to eq(
|
55
|
+
expect(issue.user_id).to eq(other_user.id)
|
43
56
|
end
|
44
57
|
|
45
58
|
it "allow to rewrite user id of comment" do
|
46
|
-
comment.user_id =
|
59
|
+
comment.user_id = other_user.id
|
47
60
|
comment.save
|
48
61
|
comment.reload
|
49
|
-
expect(comment.user_id).to eq(
|
62
|
+
expect(comment.user_id).to eq(other_user.id)
|
50
63
|
end
|
51
64
|
|
52
65
|
it "allow to rewrite user id of developer" do
|
53
|
-
developer.user_id =
|
66
|
+
developer.user_id = other_user.id
|
54
67
|
developer.save
|
55
68
|
developer.reload
|
56
|
-
expect(developer.user_id).to eq(
|
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)
|
57
77
|
end
|
58
78
|
|
59
79
|
end
|
@@ -64,58 +84,105 @@ describe ForeignKeyValidation::ModelExtension do
|
|
64
84
|
Project.send :validate_foreign_keys
|
65
85
|
Issue.send :validate_foreign_keys
|
66
86
|
Comment.send :validate_foreign_keys
|
67
|
-
Member.send :validate_foreign_keys
|
87
|
+
Member.send :validate_foreign_keys
|
68
88
|
end
|
69
89
|
|
70
|
-
let(:user) { User.create }
|
71
90
|
let(:project) { Project.create user: user }
|
91
|
+
let(:other_project) { Project.create user: other_user }
|
72
92
|
let(:idea) { Idea.create user: user, project: project }
|
73
93
|
let(:issue) { Issue.create user: user, project: project }
|
94
|
+
let(:other_issue) { Issue.create user: other_user, project: other_project }
|
74
95
|
let(:comment) { Comment.create user: user, issue: issue }
|
75
|
-
# sti model
|
76
96
|
let(:manager) { Manager.create user: user }
|
97
|
+
let(:other_manager) { Manager.create user: User.create }
|
77
98
|
let(:developer) { Developer.create user: user, boss: manager }
|
78
99
|
|
79
100
|
it "uses same user ids by default" do
|
80
101
|
expect(project.user_id).to eq(user.id)
|
81
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)
|
82
107
|
end
|
83
108
|
|
84
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
|
85
117
|
idea.user_id = 42
|
86
118
|
idea.save
|
87
119
|
expect(idea.errors.messages.values.flatten).to include("user_id of project does not match ideas user_id.")
|
88
120
|
expect(idea.reload.user_id).to_not eq(42)
|
89
121
|
end
|
90
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
|
+
|
91
130
|
it "does not allow to rewrite user id of issue" do
|
92
|
-
issue.user_id =
|
131
|
+
issue.user_id = other_user.id
|
93
132
|
issue.save
|
94
133
|
expect(issue.errors.messages.values.flatten).to include("user_id of project does not match issues user_id.")
|
95
|
-
expect(issue.reload.user_id).to_not eq(
|
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)
|
96
142
|
end
|
97
143
|
|
98
144
|
it "does not allow to rewrite user id of comment" do
|
99
|
-
comment.user_id =
|
145
|
+
comment.user_id = other_user.id
|
100
146
|
comment.save
|
101
147
|
expect(comment.errors.messages.values.flatten).to include("user_id of issue does not match comments user_id.")
|
102
|
-
expect(comment.reload.user_id).to_not eq(
|
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)
|
103
156
|
end
|
104
157
|
|
105
158
|
it "does allow to rewrite user id of project" do
|
106
|
-
project.user_id =
|
159
|
+
project.user_id = other_user.id
|
107
160
|
project.save
|
108
161
|
expect(project.errors).to be_empty
|
109
|
-
expect(project.reload.user_id).to eq(
|
162
|
+
expect(project.reload.user_id).to eq(other_user.id)
|
110
163
|
end
|
111
164
|
|
112
165
|
it "does not allow to rewrite user id of developer" do
|
113
|
-
developer.user_id =
|
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
|
114
174
|
developer.save
|
115
175
|
expect(developer.errors.messages.values.flatten).to include("user_id of boss does not match developers user_id.")
|
116
|
-
expect(developer.reload.
|
176
|
+
expect(developer.reload.custom_boss_id).to_not eq(other_manager.id)
|
117
177
|
end
|
118
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
|
+
|
119
186
|
it "does not allow to call private validate_foreign_key method" do
|
120
187
|
expect{issue.validate_foreign_key("test", "unrat")}.to raise_exception(/private method `validate_foreign_key' called/)
|
121
188
|
end
|
@@ -123,53 +190,65 @@ describe ForeignKeyValidation::ModelExtension do
|
|
123
190
|
it "does not allow to call private validate_foreign_keys_on_* methods" do
|
124
191
|
expect{issue.validate_foreign_keys_on_user}.to raise_exception(/private method `validate_foreign_keys_on_user' called/)
|
125
192
|
end
|
126
|
-
|
127
193
|
end
|
128
194
|
|
129
|
-
context "with calling validation with attributes hash" do
|
195
|
+
context "with calling validation with custom attributes hash" do
|
130
196
|
before do
|
131
|
-
|
132
|
-
validate_foreign_keys on: :user, with: :
|
197
|
+
Comment.class_eval do
|
198
|
+
validate_foreign_keys on: :user, with: :issue # member is not in list - should be editable
|
133
199
|
end
|
134
200
|
end
|
135
201
|
|
136
|
-
let(:user) { User.create }
|
137
202
|
let(:project) { Project.create user: user }
|
138
|
-
let(:
|
203
|
+
let(:other_project) { Project.create user: other_user }
|
139
204
|
let(:issue) { Issue.create user: user, project: project }
|
205
|
+
let(:other_issue) { Issue.create user: other_user, project: other_project }
|
140
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 }
|
141
209
|
|
142
210
|
it "uses same user ids by default" do
|
143
211
|
expect(project.user_id).to eq(user.id)
|
144
|
-
expect(
|
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)
|
145
215
|
end
|
146
216
|
|
147
|
-
it "does not allow to rewrite
|
148
|
-
|
149
|
-
|
150
|
-
expect(
|
151
|
-
expect(
|
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)
|
152
222
|
end
|
153
223
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
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)
|
159
238
|
end
|
160
239
|
|
161
240
|
it "allow to rewrite user id of issue" do
|
162
|
-
issue.user_id =
|
241
|
+
issue.user_id = other_user.id
|
163
242
|
issue.save
|
164
243
|
issue.reload
|
165
|
-
expect(issue.user_id).to eq(
|
244
|
+
expect(issue.user_id).to eq(other_user.id)
|
166
245
|
end
|
167
246
|
|
168
|
-
it "allow to rewrite user id of
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
expect(
|
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)
|
173
252
|
end
|
174
253
|
|
175
254
|
end
|
@@ -180,6 +259,10 @@ describe ForeignKeyValidation::ModelExtension do
|
|
180
259
|
expect{Idea.class_eval { validate_foreign_keys on: :not_existing }}.to raise_error("No foreign key for relation not_existing on ideas table!")
|
181
260
|
end
|
182
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
|
+
|
183
266
|
it "raises error due to wrong :with key" do
|
184
267
|
expect{Idea.class_eval { validate_foreign_keys with: :not_existing }}.to raise_error('Unknown relation in ["not_existing"]!')
|
185
268
|
end
|
@@ -188,7 +271,7 @@ describe ForeignKeyValidation::ModelExtension do
|
|
188
271
|
|
189
272
|
context "with calling validation and missing relations" do
|
190
273
|
|
191
|
-
it "raises error due to
|
274
|
+
it "raises error due to no existing relations" do
|
192
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!")
|
193
276
|
end
|
194
277
|
|
@@ -203,17 +286,24 @@ describe ForeignKeyValidation::ModelExtension do
|
|
203
286
|
end
|
204
287
|
end
|
205
288
|
|
206
|
-
let(:user) { User.create }
|
207
289
|
let(:project) { Project.create user: user }
|
208
290
|
let(:issue) { Issue.create project: project }
|
209
291
|
|
210
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
|
211
300
|
issue.user_id = 42
|
212
301
|
issue.save
|
213
302
|
expect(issue.errors.messages.values.flatten).to include("user_id of project does not match issues user_id.")
|
214
303
|
expect(issue.reload.user_id).to_not eq(42)
|
215
304
|
end
|
216
305
|
|
306
|
+
|
217
307
|
end
|
218
308
|
|
219
309
|
context "with calling validation and missing foreign key on relation" do
|
@@ -224,15 +314,22 @@ describe ForeignKeyValidation::ModelExtension do
|
|
224
314
|
end
|
225
315
|
end
|
226
316
|
|
227
|
-
let(:user) { User.create }
|
228
317
|
let(:project) { Project.create }
|
318
|
+
let(:other_project) { Project.create }
|
229
319
|
let(:issue) { Issue.create project: project, user: user }
|
230
320
|
|
231
321
|
it "allow to rewrite user id of issue" do
|
232
|
-
issue.user_id =
|
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
|
233
330
|
issue.save
|
234
331
|
issue.reload
|
235
|
-
expect(issue.
|
332
|
+
expect(issue.project_id).to eq(other_project.id)
|
236
333
|
end
|
237
334
|
|
238
335
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
|
+
require 'coveralls'
|
2
|
+
Coveralls.wear!
|
3
|
+
|
1
4
|
require "rails/all"
|
2
5
|
require 'foreign_key_validation'
|
3
6
|
require 'rspec/rails'
|
7
|
+
require 'database_cleaner'
|
4
8
|
require 'pry'
|
5
9
|
|
6
10
|
RSpec.configure do |config|
|
@@ -14,6 +18,18 @@ RSpec.configure do |config|
|
|
14
18
|
load "support/load_models.rb"
|
15
19
|
end
|
16
20
|
|
21
|
+
config.before(:suite) do
|
22
|
+
puts "Running specs against Rails #{Rails.version}" if defined?(Rails)
|
23
|
+
DatabaseCleaner.strategy = :transaction
|
24
|
+
DatabaseCleaner.clean_with(:truncation)
|
25
|
+
end
|
26
|
+
|
27
|
+
config.around(:each) do |example|
|
28
|
+
DatabaseCleaner.cleaning do
|
29
|
+
example.run
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
17
33
|
config.order = 'random'
|
18
34
|
end
|
19
35
|
|
data/spec/support/load_models.rb
CHANGED
@@ -31,6 +31,7 @@ end
|
|
31
31
|
class Comment < ActiveRecord::Base
|
32
32
|
belongs_to :issue
|
33
33
|
belongs_to :user
|
34
|
+
belongs_to :member
|
34
35
|
end
|
35
36
|
|
36
37
|
class Dummy < ActiveRecord::Base
|
@@ -39,6 +40,7 @@ end
|
|
39
40
|
class Member < ActiveRecord::Base
|
40
41
|
belongs_to :user
|
41
42
|
has_many :projects
|
43
|
+
has_many :comments
|
42
44
|
end
|
43
45
|
|
44
46
|
class Manager < Member
|
@@ -1,8 +1,3 @@
|
|
1
|
-
|
2
|
-
Object.send(:remove_const,
|
3
|
-
|
4
|
-
Object.send(:remove_const, :Issue) if Object.constants.include?(:Issue)
|
5
|
-
Object.send(:remove_const, :Comment) if Object.constants.include?(:Comment)
|
6
|
-
Object.send(:remove_const, :Member) if Object.constants.include?(:Member)
|
7
|
-
Object.send(:remove_const, :Developer) if Object.constants.include?(:Developer)
|
8
|
-
Object.send(:remove_const, :Manager) if Object.constants.include?(:Manager)
|
1
|
+
ActiveRecord::Base.descendants.each do |klass|
|
2
|
+
Object.send(:remove_const, klass.name.to_sym) if Object.constants.include?(klass.name.to_sym)
|
3
|
+
end
|
data/spec/support/schema.rb
CHANGED
metadata
CHANGED
@@ -1,29 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreign_key_validation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.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-09-
|
11
|
+
date: 2014-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: appraisal
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: coveralls
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
16
44
|
requirements:
|
17
45
|
- - ~>
|
18
46
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
47
|
+
version: '0.7'
|
20
48
|
type: :development
|
21
49
|
prerelease: false
|
22
50
|
version_requirements: !ruby/object:Gem::Requirement
|
23
51
|
requirements:
|
24
52
|
- - ~>
|
25
53
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
54
|
+
version: '0.7'
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
56
|
name: bundler
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +94,20 @@ dependencies:
|
|
66
94
|
- - ~>
|
67
95
|
- !ruby/object:Gem::Version
|
68
96
|
version: '3.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: database_cleaner
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.3'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.3'
|
69
111
|
- !ruby/object:Gem::Dependency
|
70
112
|
name: sqlite3
|
71
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -102,17 +144,26 @@ executables: []
|
|
102
144
|
extensions: []
|
103
145
|
extra_rdoc_files: []
|
104
146
|
files:
|
147
|
+
- .coveralls.yml
|
105
148
|
- .gitignore
|
106
149
|
- .rspec
|
107
150
|
- .ruby-gemset
|
108
151
|
- .ruby-version
|
152
|
+
- .travis.yml
|
153
|
+
- Appraisals
|
109
154
|
- Gemfile
|
110
155
|
- LICENSE
|
111
156
|
- README.md
|
112
157
|
- Rakefile
|
113
158
|
- foreign_key_validation.gemspec
|
159
|
+
- gemfiles/3.2.gemfile
|
160
|
+
- gemfiles/4.0.gemfile
|
161
|
+
- gemfiles/4.1.gemfile
|
162
|
+
- gemfiles/4.2.gemfile
|
114
163
|
- lib/foreign_key_validation.rb
|
164
|
+
- lib/foreign_key_validation/errors.rb
|
115
165
|
- lib/foreign_key_validation/model_extension.rb
|
166
|
+
- lib/foreign_key_validation/validator.rb
|
116
167
|
- lib/foreign_key_validation/version.rb
|
117
168
|
- spec/models/model_spec.rb
|
118
169
|
- spec/spec_helper.rb
|