foreign_key_validation 0.0.8 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +8 -8
- data/.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
|
+
[](https://coveralls.io/r/marcusg/foreign_key_validation?branch=master)
|
4
|
+
[](https://codeclimate.com/github/marcusg/foreign_key_validation)
|
5
|
+
[](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
|