foreign_key_validation 0.0.2 → 0.0.3
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 +4 -4
- data/README.md +5 -2
- data/foreign_key_validation.gemspec +1 -2
- data/lib/foreign_key_validation/model_extension.rb +8 -11
- data/lib/foreign_key_validation/version.rb +1 -1
- data/spec/models/model_spec.rb +114 -59
- data/spec/spec_helper.rb +9 -3
- data/spec/support/load_models.rb +28 -0
- data/spec/support/reset_models.rb +5 -0
- data/spec/support/schema.rb +26 -0
- metadata +13 -10
- data/LICENSE.txt +0 -22
- data/spec/schema.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7af20d0ad7b99c870491e939cb66a117ef0f4def
|
4
|
+
data.tar.gz: e7c83abbc241fbcfab820e58b46b00fa64632ce8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 92ec77233357a8e66e4410364439e5de674ac84d3c6a5d7f43e96193798495287e9f43426a579f56e05560ee01e24a85a7e7f134262f2ac917620743e52f4a8a
|
7
|
+
data.tar.gz: 08191da451eb213cd450d013fac17de97eb433b5a9984c3358e406df51a75bf141da22c400995a689f3c27705dfe65d604a0e74b39f062841ce8febfc4334822
|
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# foreign_key_validation
|
2
2
|
|
3
|
-
Protect your models by specifying a collection of foreign keys that should be tested for consistency with the `belongs_to` relations. For example,
|
3
|
+
Protect your models by specifying a collection of foreign keys that should be tested for consistency with the `belongs_to` relations. For example, when the `user_id` is used in all models we can check if the `user_id` of `model a` matches `user_id` of `model b` before saving the records.
|
4
|
+
|
5
|
+
## Requirements
|
6
|
+
rails >= 3.2
|
4
7
|
|
5
8
|
## Installation
|
6
9
|
|
@@ -28,7 +31,7 @@ This would only check `model.project.admin_user_id` to match `model.admin_user_i
|
|
28
31
|
|
29
32
|
## Note
|
30
33
|
|
31
|
-
Only
|
34
|
+
Only tested with ruby 2.x and ActiveRecord
|
32
35
|
|
33
36
|
## TODO
|
34
37
|
|
@@ -18,8 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.
|
22
|
-
|
21
|
+
spec.add_development_dependency "rails", "~> 4.0.0"
|
23
22
|
spec.add_development_dependency "bundler", "~> 1.6"
|
24
23
|
spec.add_development_dependency "rake", "~> 10.1"
|
25
24
|
spec.add_development_dependency "rspec-rails", "~> 3.0"
|
@@ -3,6 +3,7 @@ module ForeignKeyValidation
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
included do
|
6
|
+
private
|
6
7
|
def validate_foreign_key(key_to_validate_against, validation_key)
|
7
8
|
relation = validation_key.gsub('_id', '')
|
8
9
|
|
@@ -17,34 +18,30 @@ module ForeignKeyValidation
|
|
17
18
|
end
|
18
19
|
|
19
20
|
module ClassMethods
|
20
|
-
|
21
21
|
def validate_foreign_keys(on: :user_id, with: nil)
|
22
|
-
key_to_validate_against = on
|
22
|
+
key_to_validate_against = on.to_s
|
23
23
|
|
24
24
|
# check if key_to_validate_against is present as column
|
25
|
-
raise ArgumentError, "No foreign key #{key_to_validate_against} on #{self.table_name} table!" unless self.column_names.include?(key_to_validate_against
|
25
|
+
raise ArgumentError, "No foreign key #{key_to_validate_against} on #{self.table_name} table!" unless self.column_names.include?(key_to_validate_against)
|
26
26
|
|
27
27
|
# use provided 'with' array or column_names from self to get all foreign keys
|
28
28
|
keys_to_validate_with = (Array(with).map(&:to_s) if with) || self.column_names.select {|n| n.match(/\w_id/)}
|
29
29
|
|
30
30
|
# reject keys that match either the key_to_validate_against or the current class name key (needed for sti models)
|
31
|
-
keys_to_validate_with.reject! {|n| n
|
31
|
+
keys_to_validate_with.reject! {|n| n == key_to_validate_against || n == "#{self.class.name.underscore}_id" }
|
32
32
|
|
33
33
|
define_method "validate_foreign_keys_on_#{key_to_validate_against}" do
|
34
|
-
|
35
34
|
keys_to_validate_with.each do |validation_key|
|
36
35
|
validate_foreign_key(key_to_validate_against, validation_key)
|
37
36
|
end
|
38
|
-
|
39
37
|
end
|
40
|
-
|
38
|
+
private "validate_foreign_keys_on_#{key_to_validate_against}".to_sym
|
41
39
|
|
40
|
+
# add before filter to validate key
|
41
|
+
before_validation "validate_foreign_keys_on_#{key_to_validate_against}"
|
42
42
|
end
|
43
|
-
|
44
43
|
end
|
45
|
-
|
46
44
|
end
|
47
|
-
|
48
45
|
end
|
49
46
|
|
50
|
-
ActiveRecord::Base.send :include, ForeignKeyValidation::ModelExtension
|
47
|
+
ActiveRecord::Base.send :include, ForeignKeyValidation::ModelExtension
|
data/spec/models/model_spec.rb
CHANGED
@@ -1,80 +1,135 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
has_many :projects
|
5
|
-
has_many :ideas
|
6
|
-
end
|
3
|
+
describe ForeignKeyValidation::ModelExtension do
|
7
4
|
|
8
|
-
|
9
|
-
belongs_to :user
|
10
|
-
has_many :ideas
|
11
|
-
validates_presence_of :user_id, presence: true
|
12
|
-
end
|
5
|
+
context "Without calling validation" do
|
13
6
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
7
|
+
# NOTE: it's important here to not create the object through relation (user.projects.create...)
|
8
|
+
# it looks like active_record is caching the classes - but we need to test different class configs
|
9
|
+
let(:user) { User.create }
|
10
|
+
let(:project) { Project.create user: user }
|
11
|
+
let(:idea) { Idea.create user: user, project: project }
|
12
|
+
let(:issue) { Issue.create user: user, project: project }
|
13
|
+
let(:comment) { Comment.create user: user, issue: issue }
|
19
14
|
|
20
|
-
|
15
|
+
it "uses same user ids by default" do
|
16
|
+
expect(project.user_id).to eq(user.id)
|
17
|
+
expect(idea.user_id).to eq(user.id)
|
18
|
+
end
|
21
19
|
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
it "allow to rewrite user id of idea" do
|
21
|
+
idea.user_id = 42
|
22
|
+
idea.save
|
23
|
+
idea.reload
|
24
|
+
expect(idea.user_id).to eq(42)
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
it "allow to rewrite user id of project" do
|
28
|
+
project.user_id = 42
|
29
|
+
project.save
|
30
|
+
project.reload
|
31
|
+
expect(project.user_id).to eq(42)
|
32
|
+
end
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
it "allow to rewrite user id of issue" do
|
35
|
+
issue.user_id = 42
|
36
|
+
issue.save
|
37
|
+
issue.reload
|
38
|
+
expect(issue.user_id).to eq(42)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "allow to rewrite user id of comment" do
|
42
|
+
comment.user_id = 42
|
43
|
+
comment.save
|
44
|
+
comment.reload
|
45
|
+
expect(comment.user_id).to eq(42)
|
46
|
+
end
|
38
47
|
|
39
|
-
it "allow to rewrite user id of project" do
|
40
|
-
project.user_id = 42
|
41
|
-
project.save
|
42
|
-
project.reload
|
43
|
-
expect(project.user_id).to eq(42)
|
44
48
|
end
|
45
49
|
|
46
|
-
|
50
|
+
context "With calling validation" do
|
51
|
+
before do
|
52
|
+
Idea.send :validate_foreign_keys
|
53
|
+
Project.send :validate_foreign_keys
|
54
|
+
Issue.send :validate_foreign_keys
|
55
|
+
Comment.send :validate_foreign_keys
|
56
|
+
end
|
47
57
|
|
48
|
-
|
58
|
+
# NOTE: it's important here to not create the object through relation (user.projects.create...)
|
59
|
+
# it looks like active_record is caching the classes - but we need to test different class configs
|
60
|
+
let(:user) { User.create }
|
61
|
+
let(:project) { Project.create user: user }
|
62
|
+
let(:idea) { Idea.create user: user, project: project }
|
63
|
+
let(:issue) { Issue.create user: user, project: project }
|
64
|
+
let(:comment) { Comment.create user: user, issue: issue }
|
65
|
+
|
66
|
+
it "uses same user ids by default" do
|
67
|
+
expect(project.user_id).to eq(user.id)
|
68
|
+
expect(idea.user_id).to eq(user.id)
|
69
|
+
end
|
49
70
|
|
50
|
-
|
51
|
-
|
52
|
-
|
71
|
+
it "does not allow to rewrite user id of idea" do
|
72
|
+
idea.user_id = 42
|
73
|
+
idea.save
|
74
|
+
expect(idea.errors.messages.values.flatten).to include("user_id of project does not match ideas user_id")
|
75
|
+
expect(idea.reload.user_id).to_not eq(42)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "does not allow to rewrite user id of issue" do
|
79
|
+
issue.user_id = 42
|
80
|
+
issue.save
|
81
|
+
expect(issue.errors.messages.values.flatten).to include("user_id of project does not match issues user_id")
|
82
|
+
expect(issue.reload.user_id).to_not eq(42)
|
53
83
|
end
|
54
|
-
end
|
55
84
|
|
56
|
-
|
57
|
-
|
58
|
-
|
85
|
+
it "does not allow to rewrite user id of comment" do
|
86
|
+
comment.user_id = 42
|
87
|
+
comment.save
|
88
|
+
expect(comment.errors.messages.values.flatten).to include("user_id of issue does not match comments user_id")
|
89
|
+
expect(comment.reload.user_id).to_not eq(42)
|
90
|
+
end
|
59
91
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
92
|
+
it "does allow to rewrite user id of project" do
|
93
|
+
project.user_id = 42
|
94
|
+
project.save
|
95
|
+
expect(project.errors).to be_empty
|
96
|
+
expect(project.reload.user_id).to eq(42)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "does not allow to call private validate_foreign_key method" do
|
100
|
+
expect{issue.validate_foreign_key("test", "unrat")}.to raise_exception(/private method `validate_foreign_key' called/)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "does not allow to call private validate_foreign_keys_on_* methods" do
|
104
|
+
expect{issue.validate_foreign_keys_on_user_id}.to raise_exception(/private method `validate_foreign_keys_on_user_id' called/)
|
105
|
+
end
|
65
106
|
|
66
|
-
it "does not allow to rewrite user id of idea" do
|
67
|
-
idea.user_id = 42
|
68
|
-
idea.save
|
69
|
-
expect(idea.errors.messages.values.flatten).to include("user_id of project does not match ideas user_id")
|
70
|
-
expect(idea.reload.user_id).to_not eq(42)
|
71
107
|
end
|
72
108
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
109
|
+
context "With calling validation with wrong attributes hash" do
|
110
|
+
|
111
|
+
# NOTE: it's important here to not create the object through relation (user.projects.create...)
|
112
|
+
# it looks like active_record is caching the classes - but we need to test different class configs
|
113
|
+
let(:user) { User.create }
|
114
|
+
let(:project) { Project.create user: user }
|
115
|
+
let(:issue) { Issue.create user: user, project: project }
|
116
|
+
let(:comment) { Comment.create user: user, issue: issue }
|
117
|
+
|
118
|
+
it "raises error due to wrong :on key" do
|
119
|
+
expect{Idea.class_eval { validate_foreign_keys on: :not_existing_id }}.to raise_error("No foreign key not_existing_id on ideas table!")
|
120
|
+
end
|
121
|
+
|
122
|
+
it "does not validate due to wrong :with key" do
|
123
|
+
Issue.class_eval do
|
124
|
+
validate_foreign_keys with: :not_existing_id
|
125
|
+
end
|
126
|
+
issue.user_id = 42
|
127
|
+
issue.save
|
128
|
+
issue.reload
|
129
|
+
expect(issue.user_id).to eq(42)
|
130
|
+
end
|
131
|
+
|
78
132
|
end
|
79
133
|
|
80
|
-
|
134
|
+
|
135
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -8,11 +8,17 @@ RSpec.configure do |config|
|
|
8
8
|
config.filter_run :focus
|
9
9
|
config.infer_base_class_for_anonymous_controllers = true
|
10
10
|
|
11
|
-
#
|
11
|
+
# reset and reload model classes for each run
|
12
|
+
config.before(:each) do
|
13
|
+
load "support/reset_models.rb"
|
14
|
+
load "support/load_models.rb"
|
15
|
+
end
|
16
|
+
|
17
|
+
config.order = 'random'
|
12
18
|
end
|
13
19
|
|
14
20
|
setup_sqlite_db = lambda do
|
15
21
|
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
16
|
-
load "schema.rb"
|
22
|
+
load "support/schema.rb"
|
17
23
|
end
|
18
|
-
silence_stream(STDOUT, &setup_sqlite_db)
|
24
|
+
silence_stream(STDOUT, &setup_sqlite_db)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "active_record"
|
2
|
+
|
3
|
+
class User < ActiveRecord::Base
|
4
|
+
has_many :projects
|
5
|
+
has_many :ideas
|
6
|
+
end
|
7
|
+
|
8
|
+
class Project < ActiveRecord::Base
|
9
|
+
belongs_to :user
|
10
|
+
has_many :ideas
|
11
|
+
has_many :issues
|
12
|
+
end
|
13
|
+
|
14
|
+
class Idea < ActiveRecord::Base
|
15
|
+
belongs_to :project
|
16
|
+
belongs_to :user
|
17
|
+
end
|
18
|
+
|
19
|
+
class Issue < ActiveRecord::Base
|
20
|
+
belongs_to :project
|
21
|
+
belongs_to :user
|
22
|
+
has_many :comments
|
23
|
+
end
|
24
|
+
|
25
|
+
class Comment < ActiveRecord::Base
|
26
|
+
belongs_to :issue
|
27
|
+
belongs_to :user
|
28
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
Object.send(:remove_const, :User) if Object.constants.include?(:User)
|
2
|
+
Object.send(:remove_const, :Project) if Object.constants.include?(:Project)
|
3
|
+
Object.send(:remove_const, :Idea) if Object.constants.include?(:Idea)
|
4
|
+
Object.send(:remove_const, :Issue) if Object.constants.include?(:Issue)
|
5
|
+
Object.send(:remove_const, :Comment) if Object.constants.include?(:Comment)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
self.verbose = false
|
3
|
+
|
4
|
+
create_table "users", force: true do |t|
|
5
|
+
end
|
6
|
+
|
7
|
+
create_table "projects", force: true do |t|
|
8
|
+
t.integer "user_id"
|
9
|
+
end
|
10
|
+
|
11
|
+
create_table "ideas", force: true do |t|
|
12
|
+
t.integer "user_id"
|
13
|
+
t.integer "project_id"
|
14
|
+
end
|
15
|
+
|
16
|
+
create_table "issues", force: true do |t|
|
17
|
+
t.integer "user_id"
|
18
|
+
t.integer "project_id"
|
19
|
+
end
|
20
|
+
|
21
|
+
create_table "comments", force: true do |t|
|
22
|
+
t.integer "user_id"
|
23
|
+
t.integer "issue_id"
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
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: 0.0.3
|
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-08-
|
11
|
+
date: 2014-08-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
20
|
-
type: :
|
19
|
+
version: 4.0.0
|
20
|
+
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 4.0.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -107,7 +107,6 @@ files:
|
|
107
107
|
- ".ruby-version"
|
108
108
|
- Gemfile
|
109
109
|
- LICENSE
|
110
|
-
- LICENSE.txt
|
111
110
|
- README.md
|
112
111
|
- Rakefile
|
113
112
|
- foreign_key_validation.gemspec
|
@@ -115,8 +114,10 @@ files:
|
|
115
114
|
- lib/foreign_key_validation/model_extension.rb
|
116
115
|
- lib/foreign_key_validation/version.rb
|
117
116
|
- spec/models/model_spec.rb
|
118
|
-
- spec/schema.rb
|
119
117
|
- spec/spec_helper.rb
|
118
|
+
- spec/support/load_models.rb
|
119
|
+
- spec/support/reset_models.rb
|
120
|
+
- spec/support/schema.rb
|
120
121
|
homepage: https://github.com/marcusg/foreign_key_validation
|
121
122
|
licenses:
|
122
123
|
- MIT
|
@@ -143,5 +144,7 @@ specification_version: 4
|
|
143
144
|
summary: Protect the foreign keys in your Rails models.
|
144
145
|
test_files:
|
145
146
|
- spec/models/model_spec.rb
|
146
|
-
- spec/schema.rb
|
147
147
|
- spec/spec_helper.rb
|
148
|
+
- spec/support/load_models.rb
|
149
|
+
- spec/support/reset_models.rb
|
150
|
+
- spec/support/schema.rb
|
data/LICENSE.txt
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
Copyright (c) 2014 Marcus Geißler
|
2
|
-
|
3
|
-
MIT License
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
-
a copy of this software and associated documentation files (the
|
7
|
-
"Software"), to deal in the Software without restriction, including
|
8
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
-
permit persons to whom the Software is furnished to do so, subject to
|
11
|
-
the following conditions:
|
12
|
-
|
13
|
-
The above copyright notice and this permission notice shall be
|
14
|
-
included in all copies or substantial portions of the Software.
|
15
|
-
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/spec/schema.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
ActiveRecord::Schema.define(:version => 20130730063732) do
|
2
|
-
|
3
|
-
create_table "users", :force => true do |t|
|
4
|
-
end
|
5
|
-
|
6
|
-
create_table "projects", :force => true do |t|
|
7
|
-
t.integer "user_id"
|
8
|
-
end
|
9
|
-
|
10
|
-
create_table "ideas", :force => true do |t|
|
11
|
-
t.integer "user_id"
|
12
|
-
t.integer "project_id"
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|