foreign_key_validation 0.0.6 → 0.0.7

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