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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZmIyZjgxODI4YjY3ZjBiMWE5YzAxMWY1OTU2MjBjMmM2NDY4ZTVlNA==
4
+ ZjUwMTlkNzAzZDliZTUxOGViNzJmNDA4YTVhNGY5NzEzNjdlYzJkZg==
5
5
  data.tar.gz: !binary |-
6
- NGE0M2NkZDBmZDM0ODU0NWVjNzA4NWQ3NWFiNWNiOTYxMDZkOGYxZg==
6
+ OWJkZjlkM2I4NWIyYmY3Y2IyZDRjZDQ2NTk3MjBjZDQ0YzkyMjFmNw==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- OTRiYzRkNzFhMTI3MzllZTdiOWMwZjIyOWM5NDcyYjMzNjg5Y2U5YTZjMTZi
10
- MDk0NzZhYjM0ZjE0NDM3ZTA5OWMyNTIwNGU0YjRmM2EwZjZlNzEzZGJiNmY1
11
- OTgxMTU1YzQ0NzU2YjlmZDQxOGQ2OGQ3MmE2ZGEyYTM0YzkwNTg=
9
+ OTBmZmRhZDZlZDI5MDc1ZWM3Y2ExOWMxODIzZmQ1YjI3YmFkZjJkYTg2OTI3
10
+ NTYxNWNhZmJmOTAyYzI2NjhiNTY4ODYzYjdhNjIxM2JiZTEyNjhhNTI0ZTcx
11
+ MDEwZDc5ZDc1ZWE2M2EyZDUxMmQyNDMxYTdkYzk0M2M3ZGFlMTQ=
12
12
  data.tar.gz: !binary |-
13
- NmY1ZjI1MDVmMmVhM2U4Yjk3MTg0OTE5NTZjYzg5ZjhkNzg5NTIzYjIxNzFh
14
- ZWI4M2JkY2E4YjM3MTVjMTI1Y2IzZjFjNDY3ODQxODlkMWNlZTEyYzIzYTcy
15
- ZmYwYzhkNGQ4M2M2Y2YyODg3ZDNlZThjYzRjNDhiMTgxMzY2ODM=
13
+ MmVhODBlOWU0MzQxNTRhYjIyN2U4MGJhNjViZDUwNzc3MTIyNDljY2M2MGE3
14
+ NjJjZjUwODEzNTJiMjM4ZGYwMTI2MTNmNWMwZmQ0YjhiY2FlMDVjOWM5OTFj
15
+ YmMwM2ZkN2NkZjMxOTc1Y2VlZDBhOThjNmI1YzZkZTMzMDJjNTk=
data/.coveralls.yml ADDED
@@ -0,0 +1,2 @@
1
+ service_name: travis-ci
2
+ repo_token: p5anrHEyw6IcvCwmbaQD4ohlizZOLEwIO
data/.gitignore CHANGED
@@ -20,3 +20,4 @@ tmp
20
20
  *.o
21
21
  *.a
22
22
  mkmf.log
23
+ gemfiles/*.lock
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+
3
+ script: "bundle exec rspec"
4
+
5
+ rvm:
6
+ - 1.9.3
7
+ - 2.0.0
8
+ - 2.1.2
9
+
10
+ gemfile:
11
+ - gemfiles/3.2.gemfile
12
+ - gemfiles/4.0.gemfile
13
+ - gemfiles/4.1.gemfile
14
+ - gemfiles/4.2.gemfile
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
- Protect your models by specifying a collection of relations that should be tested for consistency with the `user_id` column. 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 - if the IDs are different, an error will be attached to the errors hash of `self`.
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 = "foreign_key_validation"
7
+ spec.name = 'foreign_key_validation'
8
8
  spec.version = ForeignKeyValidation::VERSION
9
- spec.authors = ["Marcus Geißler"]
10
- spec.email = ["marcus3006@gmail.com"]
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 = "https://github.com/marcusg/foreign_key_validation"
14
- spec.license = "MIT"
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 = ["lib"]
19
+ spec.require_paths = ['lib']
20
20
 
21
- spec.add_development_dependency "rails", "~> 4.0"
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,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 3.2.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 4.0.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 4.1.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 4.2.0.beta2"
6
+
7
+ gemspec :path => "../"
@@ -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) } if subclasses.any?
18
+ subclasses.map {|klass| klass.send(:validate_foreign_keys, opt)}
19
19
 
20
- validate_against = (opt[:on] || :user).to_s
21
- reflections = reflect_on_all_associations(:belongs_to)
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
- raise ArgumentError, "Can't find any belongs_to relations for #{name} class. Put validation call below association definitions!" if reflection_names.empty?
27
- raise ArgumentError, "No foreign key for relation #{validate_against} on #{table_name} table!" unless reflection_names.include?(validate_against)
28
- raise ArgumentError, "Unknown relation in #{validate_with}!" unless validate_with.all? {|k| reflection_names.include?(k) }
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 "validate_foreign_keys_on_#{validate_against}".to_sym
28
+ private validator.filter_method_name.to_sym
36
29
 
37
- before_validation "validate_foreign_keys_on_#{validate_against}"
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
@@ -1,3 +1,3 @@
1
1
  module ForeignKeyValidation
2
- VERSION = "0.0.8"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -1,2 +1,4 @@
1
1
  require "foreign_key_validation/version"
2
+ require "foreign_key_validation/errors"
3
+ require "foreign_key_validation/validator"
2
4
  require "foreign_key_validation/model_extension"
@@ -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 = 42
45
+ project.user_id = other_user.id
33
46
  project.save
34
47
  project.reload
35
- expect(project.user_id).to eq(42)
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 = 42
52
+ issue.user_id = other_user.id
40
53
  issue.save
41
54
  issue.reload
42
- expect(issue.user_id).to eq(42)
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 = 42
59
+ comment.user_id = other_user.id
47
60
  comment.save
48
61
  comment.reload
49
- expect(comment.user_id).to eq(42)
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 = 42
66
+ developer.user_id = other_user.id
54
67
  developer.save
55
68
  developer.reload
56
- expect(developer.user_id).to eq(42)
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 # sti model
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 = 42
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(42)
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 = 42
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(42)
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 = 42
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(42)
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 = 42
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.user_id).to_not eq(42)
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
- Idea.class_eval do
132
- validate_foreign_keys on: :user, with: :project
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(:idea) { Idea.create user: user, project: project }
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(idea.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)
145
215
  end
146
216
 
147
- it "does not allow to rewrite user id of idea" do
148
- idea.user_id = 42
149
- idea.save
150
- expect(idea.errors.messages.values.flatten).to include("user_id of project does not match ideas user_id.")
151
- expect(idea.reload.user_id).to_not eq(42)
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
- it "allow to rewrite user id of project" do
155
- project.user_id = 42
156
- project.save
157
- project.reload
158
- expect(project.user_id).to eq(42)
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 = 42
241
+ issue.user_id = other_user.id
163
242
  issue.save
164
243
  issue.reload
165
- expect(issue.user_id).to eq(42)
244
+ expect(issue.user_id).to eq(other_user.id)
166
245
  end
167
246
 
168
- it "allow to rewrite user id of comment" do
169
- comment.user_id = 42
170
- comment.save
171
- comment.reload
172
- expect(comment.user_id).to eq(42)
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 wrong :on key" do
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 = 42
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.user_id).to eq(42)
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
 
@@ -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
- 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)
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
@@ -22,6 +22,7 @@ ActiveRecord::Schema.define do
22
22
  create_table "comments", force: true do |t|
23
23
  t.integer "user_id"
24
24
  t.integer "issue_id"
25
+ t.integer "member_id"
25
26
  end
26
27
 
27
28
  create_table "dummies", force: true do |t|
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.8
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-01 00:00:00.000000000 Z
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: '4.0'
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: '4.0'
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