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 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