foreign_key_validation 0.0.6 → 0.0.7

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
- Zjg4OGRlMzQzMTJhZmQyOWFhOGQzNTI2NDhhYTE0YThlZWM0Y2NjMg==
4
+ NjBhMDg4MzJhYjgzMjlkMDJkZDBiODI5ZGM1ZjE5MWQ2NmI1YzIxMw==
5
5
  data.tar.gz: !binary |-
6
- NTRlZDQxNTUyMTdiZGRmYjQwNTlkMzkwOTUwMTMyMWNjZmUyYzBjZg==
6
+ ZTBiZTExNmE4NjM4MGRmM2QwYjI3MmExZDBlMmU3NjY4ZDlkYTIzMw==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NDAyZjI0YzcxYzQzOGVmMThkYzk5MmUyNDcyZmY4YjA3OWU2NTc1OTA2ODc2
10
- ODVjOWEwZDhhYWVjYjk3MzAyNzZlNzI5NTBmZDNjYjQ2Zjk4MTBlZTRhZjgx
11
- M2U2NzBjY2QzYzEyNmEyNzFhYTE1ZDA2ODAwNjYyMTQ0ZDFhNzY=
9
+ ZDM4OGQ0ZDRhYjNhOTFhNzIzMTM4YzUxOWI1ZWJhZGNjYjNhNDgzNDEyMWVl
10
+ YzA1ODQwYzRjYjY5MGY2MTBlMTMxOGU5MDE2YjRlMjgyNmMyZWFkNGJlODJk
11
+ NjFiYjAyZjIyYTg5MWUzMDhmMWU5MjNlNGIzNjMxZGE5MDIxOWQ=
12
12
  data.tar.gz: !binary |-
13
- NWM2OWNhNDVkMDdmNDgxNTRmNzEzMTU1NzNjYTI3M2Y2ZjA3ZWYzYWViYjll
14
- NjdmOGE2MjI2NTE3MjE3ZWZmZWZjYTI0ZDk5YTg2N2FlMmQzNDM4YWE0ZmU2
15
- NGNlMGM1YTFlNzZjMDEyNjY2OWU1ZjNiY2I5ZTk4Y2NlM2YwNTM=
13
+ YTYzOTRlODQ1ZjgwYWFlNTIzYmRmNDRmNTVjMDJhMTViNzVmNTc3ZmNlYThl
14
+ NWE0Yjc3NDA4OTgyZWUxN2M3ZTJlZDQ1MTYwMmFjZDY2MzM5ZDllMjM3ZTNk
15
+ YzI1NWNmZTI4ZjI1NmNiMDMzOWU0Y2U1MjRjZWVlY2Q1MDMyYTA=
data/README.md CHANGED
@@ -1,6 +1,6 @@
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, 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.
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`.
4
4
 
5
5
  ## Requirements
6
6
  ruby >= 1.9
@@ -23,21 +23,18 @@ Or install it yourself as:
23
23
 
24
24
  ## Usage
25
25
 
26
- Call `validate_foreign_keys` below the association definitions (`belongs_to`, ...) in your model. By default it assumes that it should check all foreign keys against the `user_id` column. So any relation (except `user`) will be checked for a matching `user_id` if the column exists.
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`.
27
27
 
28
28
  Change behaviour by calling `validate_foreign_keys` with arguments hash.
29
29
 
30
30
  validate_foreign_keys on: :admin_user, with: [:project]
31
31
 
32
- This would only check `model.project.admin_user_id` to match `model.admin_user_id`.
32
+ This would only check `model.project.admin_user_id` to match `model.admin_user_id` before saving the record.
33
33
 
34
34
  ## Note
35
35
 
36
36
  Only tested with ActiveRecord
37
37
 
38
- ## TODO
39
-
40
- - Tests!
41
38
 
42
39
  ## Contributing
43
40
 
@@ -4,27 +4,31 @@ module ForeignKeyValidation
4
4
 
5
5
  included do
6
6
  private
7
- def validate_foreign_key(validate_against, relation)
8
- return if send(relation).try("#{validate_against}_id").nil? or try("#{validate_against}_id").nil?
7
+ def validate_foreign_key(validate_against_key, reflection_name)
8
+ return if send(reflection_name).try(validate_against_key).nil? or try(validate_against_key).nil?
9
9
 
10
- if send(relation).send("#{validate_against}_id") != send("#{validate_against}_id")
11
- errors.add(validate_against, "#{validate_against} of #{relation} does not match #{self.class.table_name} #{validate_against}")
10
+ if send(reflection_name).send(validate_against_key) != send(validate_against_key)
11
+ errors.add(validate_against_key, "#{validate_against_key} of #{reflection_name} does not match #{self.class.name.tableize} #{validate_against_key}")
12
12
  end
13
13
  end
14
14
  end
15
15
 
16
16
  module ClassMethods
17
17
  def validate_foreign_keys(opt={})
18
- validate_against = (opt[:on] || :user).to_s
19
- reflections = reflect_on_all_associations(:belongs_to).map(&:name).map(&:to_s)
20
- validate_with = ((Array(opt[:with]).map(&:to_s) if opt[:with]) || reflections).reject {|n| n == validate_against}
18
+ subclasses.map {|klass| klass.send(:validate_foreign_keys, opt) } if subclasses.any?
21
19
 
22
- raise ArgumentError, "No foreign key #{validate_against} on #{table_name} table!" unless reflections.include?(validate_against)
23
- raise ArgumentError, "Unknown relation in #{validate_with}!" unless validate_with.all? {|k| reflections.include?(k) }
20
+ validate_against = (opt[:on] || :user).to_s
21
+ validate_against_key = "#{validate_against}_id"
22
+ reflection_names = reflect_on_all_associations(:belongs_to).map(&:name).map(&:to_s)
23
+ validate_with = ((Array(opt[:with]).map(&:to_s) if opt[:with]) || reflection_names).reject {|n| n == validate_against}
24
+
25
+ raise ArgumentError, "Can't find any belongs_to relations for #{name} class. Put validation call below association definitions" if reflection_names.empty?
26
+ raise ArgumentError, "No foreign key #{validate_against_key} on #{table_name} table!" unless reflection_names.include?(validate_against)
27
+ raise ArgumentError, "Unknown relation in #{validate_with}!" unless validate_with.all? {|k| reflection_names.include?(k) }
24
28
 
25
29
  define_method "validate_foreign_keys_on_#{validate_against}" do
26
- validate_with.each do |relation|
27
- validate_foreign_key(validate_against, relation)
30
+ validate_with.each do |reflection_name|
31
+ validate_foreign_key(validate_against_key, reflection_name)
28
32
  end
29
33
  end
30
34
  private "validate_foreign_keys_on_#{validate_against}".to_sym
@@ -1,3 +1,3 @@
1
1
  module ForeignKeyValidation
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.7"
3
3
  end
@@ -5,13 +5,16 @@ 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
- context "Without calling validation" do
8
+ context "without calling validation" do
9
9
 
10
10
  let(:user) { User.create }
11
11
  let(:project) { Project.create user: user }
12
12
  let(:idea) { Idea.create user: user, project: project }
13
13
  let(:issue) { Issue.create user: user, project: project }
14
14
  let(:comment) { Comment.create user: user, issue: issue }
15
+ # sti model
16
+ let(:manager) { Manager.create user: user }
17
+ let(:developer) { Developer.create user: user, boss: manager }
15
18
 
16
19
  it "uses same user ids by default" do
17
20
  expect(project.user_id).to eq(user.id)
@@ -46,14 +49,22 @@ describe ForeignKeyValidation::ModelExtension do
46
49
  expect(comment.user_id).to eq(42)
47
50
  end
48
51
 
52
+ it "allow to rewrite user id of developer" do
53
+ developer.user_id = 42
54
+ developer.save
55
+ developer.reload
56
+ expect(developer.user_id).to eq(42)
57
+ end
58
+
49
59
  end
50
60
 
51
- context "With calling validation" do
61
+ context "with calling validation" do
52
62
  before do
53
63
  Idea.send :validate_foreign_keys
54
64
  Project.send :validate_foreign_keys
55
65
  Issue.send :validate_foreign_keys
56
66
  Comment.send :validate_foreign_keys
67
+ Member.send :validate_foreign_keys # sti model
57
68
  end
58
69
 
59
70
  let(:user) { User.create }
@@ -61,6 +72,9 @@ describe ForeignKeyValidation::ModelExtension do
61
72
  let(:idea) { Idea.create user: user, project: project }
62
73
  let(:issue) { Issue.create user: user, project: project }
63
74
  let(:comment) { Comment.create user: user, issue: issue }
75
+ # sti model
76
+ let(:manager) { Manager.create user: user }
77
+ let(:developer) { Developer.create user: user, boss: manager }
64
78
 
65
79
  it "uses same user ids by default" do
66
80
  expect(project.user_id).to eq(user.id)
@@ -70,21 +84,21 @@ describe ForeignKeyValidation::ModelExtension do
70
84
  it "does not allow to rewrite user id of idea" do
71
85
  idea.user_id = 42
72
86
  idea.save
73
- expect(idea.errors.messages.values.flatten).to include("user of project does not match ideas user")
87
+ expect(idea.errors.messages.values.flatten).to include("user_id of project does not match ideas user_id")
74
88
  expect(idea.reload.user_id).to_not eq(42)
75
89
  end
76
90
 
77
91
  it "does not allow to rewrite user id of issue" do
78
92
  issue.user_id = 42
79
93
  issue.save
80
- expect(issue.errors.messages.values.flatten).to include("user of project does not match issues user")
94
+ expect(issue.errors.messages.values.flatten).to include("user_id of project does not match issues user_id")
81
95
  expect(issue.reload.user_id).to_not eq(42)
82
96
  end
83
97
 
84
98
  it "does not allow to rewrite user id of comment" do
85
99
  comment.user_id = 42
86
100
  comment.save
87
- expect(comment.errors.messages.values.flatten).to include("user of issue does not match comments user")
101
+ expect(comment.errors.messages.values.flatten).to include("user_id of issue does not match comments user_id")
88
102
  expect(comment.reload.user_id).to_not eq(42)
89
103
  end
90
104
 
@@ -95,6 +109,13 @@ describe ForeignKeyValidation::ModelExtension do
95
109
  expect(project.reload.user_id).to eq(42)
96
110
  end
97
111
 
112
+ it "does not allow to rewrite user id of developer" do
113
+ developer.user_id = 42
114
+ developer.save
115
+ 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)
117
+ end
118
+
98
119
  it "does not allow to call private validate_foreign_key method" do
99
120
  expect{issue.validate_foreign_key("test", "unrat")}.to raise_exception(/private method `validate_foreign_key' called/)
100
121
  end
@@ -105,7 +126,7 @@ describe ForeignKeyValidation::ModelExtension do
105
126
 
106
127
  end
107
128
 
108
- context "With calling validation with attributes hash" do
129
+ context "with calling validation with attributes hash" do
109
130
  before do
110
131
  Idea.class_eval do
111
132
  validate_foreign_keys on: :user, with: :project
@@ -126,7 +147,7 @@ describe ForeignKeyValidation::ModelExtension do
126
147
  it "does not allow to rewrite user id of idea" do
127
148
  idea.user_id = 42
128
149
  idea.save
129
- expect(idea.errors.messages.values.flatten).to include("user of project does not match ideas user")
150
+ expect(idea.errors.messages.values.flatten).to include("user_id of project does not match ideas user_id")
130
151
  expect(idea.reload.user_id).to_not eq(42)
131
152
  end
132
153
 
@@ -153,24 +174,28 @@ describe ForeignKeyValidation::ModelExtension do
153
174
 
154
175
  end
155
176
 
156
- context "With calling validation with wrong attributes hash" do
157
-
158
- let(:user) { User.create }
159
- let(:project) { Project.create user: user }
160
- let(:issue) { Issue.create user: user, project: project }
161
- let(:comment) { Comment.create user: user, issue: issue }
177
+ context "with calling validation and wrong attributes hash" do
162
178
 
163
179
  it "raises error due to wrong :on key" do
164
- expect{Idea.class_eval { validate_foreign_keys on: :not_existing_id }}.to raise_error("No foreign key not_existing_id on ideas table!")
180
+ expect{Idea.class_eval { validate_foreign_keys on: :not_existing }}.to raise_error("No foreign key not_existing_id on ideas table!")
165
181
  end
166
182
 
167
183
  it "raises error due to wrong :with key" do
168
- expect{Idea.class_eval { validate_foreign_keys with: :not_existing_id }}.to raise_error('Unknown relation in ["not_existing_id"]!')
184
+ expect{Idea.class_eval { validate_foreign_keys with: :not_existing }}.to raise_error('Unknown relation in ["not_existing"]!')
185
+ end
186
+
187
+ end
188
+
189
+ context "with calling validation and missing relations" do
190
+
191
+ it "raises error due to wrong :on key" do
192
+ 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")
169
193
  end
170
194
 
171
195
  end
172
196
 
173
- context "With calling validation and missing foreign key on self" do
197
+
198
+ context "with calling validation and missing foreign key on self" do
174
199
 
175
200
  before do
176
201
  Issue.class_eval do
@@ -185,13 +210,13 @@ describe ForeignKeyValidation::ModelExtension do
185
210
  it "does not allow to rewrite user id of issue" do
186
211
  issue.user_id = 42
187
212
  issue.save
188
- expect(issue.errors.messages.values.flatten).to include("user of project does not match issues user")
213
+ expect(issue.errors.messages.values.flatten).to include("user_id of project does not match issues user_id")
189
214
  expect(issue.reload.user_id).to_not eq(42)
190
215
  end
191
216
 
192
217
  end
193
218
 
194
- context "With calling validation and missing foreign key on relation" do
219
+ context "with calling validation and missing foreign key on relation" do
195
220
 
196
221
  before do
197
222
  Issue.class_eval do
@@ -3,10 +3,16 @@ require "active_record"
3
3
  class User < ActiveRecord::Base
4
4
  has_many :projects
5
5
  has_many :ideas
6
+ has_many :managers
7
+ has_many :developers
8
+ has_many :comments
9
+ has_many :issues
10
+ has_many :ideas
6
11
  end
7
12
 
8
13
  class Project < ActiveRecord::Base
9
14
  belongs_to :user
15
+ belongs_to :member
10
16
  has_many :ideas
11
17
  has_many :issues
12
18
  end
@@ -26,3 +32,19 @@ class Comment < ActiveRecord::Base
26
32
  belongs_to :issue
27
33
  belongs_to :user
28
34
  end
35
+
36
+ class Dummy < ActiveRecord::Base
37
+ end
38
+
39
+ class Member < ActiveRecord::Base
40
+ belongs_to :user
41
+ has_many :projects
42
+ end
43
+
44
+ class Manager < Member
45
+ has_many :developers
46
+ end
47
+
48
+ class Developer < Member
49
+ belongs_to :boss, class_name: "Manager", foreign_key: :boss_id
50
+ end
@@ -1,5 +1,8 @@
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)
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)
@@ -6,6 +6,7 @@ ActiveRecord::Schema.define do
6
6
 
7
7
  create_table "projects", force: true do |t|
8
8
  t.integer "user_id"
9
+ t.integer "member_id"
9
10
  end
10
11
 
11
12
  create_table "ideas", force: true do |t|
@@ -23,4 +24,13 @@ ActiveRecord::Schema.define do
23
24
  t.integer "issue_id"
24
25
  end
25
26
 
27
+ create_table "dummies", force: true do |t|
28
+ end
29
+
30
+ create_table "members", force: true do |t|
31
+ t.integer "user_id"
32
+ t.integer "boss_id"
33
+ t.string "type"
34
+ end
35
+
26
36
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreign_key_validation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
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-31 00:00:00.000000000 Z
11
+ date: 2014-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails