foreign_key_validation 1.0.1 → 1.1.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
- MTRkMDgxMDUxZmJiN2ViODhiNjc2MjlkMjkwNzU3NWE1NGYyNDMxMw==
4
+ ZDU3ZTY0MGMyNmIzYTJiOWFjMjZjMzE5NjllMWM1MjAyNmI5MTcwOA==
5
5
  data.tar.gz: !binary |-
6
- NWRmMDQzMmRkNTM4OTE5MGIzZTRmMjk3ODllMDU1ZmYyOGZjZTkyMw==
6
+ OTRhOTc5NzlmZDBjODAxNmE4Mzk4NjM1ZDQ2NDJhMjkyMTFhZWY4YQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- M2NhODc1MzQ0NDEwNGMwZmU0MzRkN2E2ZWJjZDg5OTlhYzFkYzhlYWEyM2Yx
10
- MTUwMTQ3ZWU3ZGM0OTIxNTZhZTQ1MTMyZTk5ZDUzOGI2NzhjZTA1Nzg0MTk2
11
- NWNhNjQzYzllOTIwYWJlNGM1ODk1NjIwY2ZhZDc0YzczYjRiNmY=
9
+ ODdiYzEzM2FhNTE1ZGVjZmMzYWY2ZmMxMGFmZDM2NTIxZjlmYjcxMGY1Njdk
10
+ NmZmN2Q3ZmY1YTkzN2NlYjZiNzhhNjhmNDUwNTAxOTkyYjMwMzRlYjg1NWU3
11
+ ZTc2ZWE1ZTAxZDYzMzQ1MmRkNmVmYmZkOGViOGZjNTRiNGNhYTY=
12
12
  data.tar.gz: !binary |-
13
- ODBhMWQ2MmZiMmE2N2NkZDc3YmJmYjU2MGQwYjQzYTliNjg3NzBlMGJjMGIw
14
- OGVjYjQ2YTk2ZDA1MzdmNzFkNDdiMTU0ODE1MWUwYzlkNmJiMGY4ZDI0YTUy
15
- ZjFiMjRhNWNmMjc0N2Y0NDNiYjhhMjljYTkyM2ZkZDM1NzI4Yzg=
13
+ NTczZTBjNGFjNTgyYThkNThlZTQwZDUzNTZlZTQ3ZTk2MGIzMDY1ODRhMTg1
14
+ OWE2MDRhMGUxYzA5Yzg2NTEwYmEzMDk2NDUzOTU3MjhjNjNiMmFlY2NiOTc0
15
+ OTUxNTMwMmQzZDNhZTEyNjk3MWRhN2JhMmVlODE4NzczZGI2MDk=
data/README.md CHANGED
@@ -35,6 +35,15 @@ Change behaviour by calling `validate_foreign_keys` with arguments hash.
35
35
 
36
36
  This would only check `model.project.admin_user_id` to match `model.admin_user_id` before saving the record.
37
37
 
38
+ ## Configuration
39
+
40
+ You can customize the behaviour of the gem by calling the `configure` method on the module with a block (e.g. initializer).
41
+
42
+ ForeignKeyValidation.configure do |config|
43
+ config.error_message = proc { |key, name, object| "My custom msg!" }
44
+ config.inject_subclasses = true
45
+ end
46
+
38
47
  ## Note
39
48
 
40
49
  Only tested with ActiveRecord
@@ -4,5 +4,28 @@ require "foreign_key_validation/collector"
4
4
  require "foreign_key_validation/filter"
5
5
  require "foreign_key_validation/validator"
6
6
  require "foreign_key_validation/model_extension"
7
+ require "ostruct"
8
+
9
+ module ForeignKeyValidation
10
+
11
+ DEFAULT_CONFIG = {
12
+ inject_subclasses: true,
13
+ error_message: proc { |validate_against_key, reflection_name, object|
14
+ "#{validate_against_key} of #{reflection_name} does not match #{object.class.name.tableize} #{validate_against_key}."
15
+ }
16
+ }
17
+
18
+ class << self
19
+ attr_writer :configuration
20
+
21
+ def configure(&blk)
22
+ yield configuration
23
+ end
24
+
25
+ def configuration
26
+ @configuration ||= OpenStruct.new(DEFAULT_CONFIG)
27
+ end
28
+ end
29
+ end
7
30
 
8
31
  ActiveRecord::Base.send :include, ForeignKeyValidation::ModelExtension
@@ -1,17 +1,12 @@
1
1
  module ForeignKeyValidation
2
2
 
3
3
  class Collector
4
- attr_accessor :klass, :validate_against, :reflections, :reflection_names, :validate_against_key, :validate_with
4
+ attr_accessor :options
5
5
 
6
6
  DEFAULT_VALIDATE_AGAINST = :user
7
7
 
8
8
  def initialize(opt={})
9
- self.klass = opt[: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])
9
+ self.options = opt
15
10
  end
16
11
 
17
12
  def check!
@@ -21,18 +16,28 @@ module ForeignKeyValidation
21
16
  true
22
17
  end
23
18
 
24
- private
19
+ def klass
20
+ @klass ||= options[:klass]
21
+ end
22
+
23
+ def validate_against
24
+ @validate_against ||= (options[:on] || DEFAULT_VALIDATE_AGAINST).to_s
25
+ end
26
+
27
+ def validate_with
28
+ @validate_with ||= ((Array(options[:with]).map(&:to_s) if options[:with]) || reflection_names).reject {|n| n == validate_against}
29
+ end
25
30
 
26
- def find_validate_against(on)
27
- (on || DEFAULT_VALIDATE_AGAINST).to_s
31
+ def reflections
32
+ @reflections ||= klass.reflect_on_all_associations(:belongs_to)
28
33
  end
29
34
 
30
- def find_validate_with(with)
31
- ((Array(with).map(&:to_s) if with) || reflection_names).reject {|n| n == validate_against}
35
+ def reflection_names
36
+ @reflection_names ||= reflections.map {|r| r.name.to_s }
32
37
  end
33
38
 
34
- def find_validate_against_key
35
- reflections.select {|r| r.name.to_s == validate_against}.first.try(:foreign_key)
39
+ def validate_against_key
40
+ @validate_against_key ||= reflections.select {|r| r.name.to_s == validate_against}.first.try(:foreign_key)
36
41
  end
37
42
 
38
43
  end
@@ -10,6 +10,7 @@ module ForeignKeyValidation
10
10
  def before_filter(&block)
11
11
  collector.klass.send :define_method, filter_name do
12
12
  self.instance_eval &block
13
+ return true
13
14
  end
14
15
  collector.klass.send :private, filter_name.to_sym
15
16
  collector.klass.send :before_validation, filter_name
@@ -5,13 +5,13 @@ module ForeignKeyValidation
5
5
  module ClassMethods
6
6
 
7
7
  def validate_foreign_keys(opt={})
8
- subclasses.map {|klass| klass.send(:validate_foreign_keys, opt)}
8
+ subclasses.map {|klass| klass.send(:validate_foreign_keys, opt)} if ForeignKeyValidation.configuration.inject_subclasses
9
9
 
10
10
  collector = Collector.new(opt.merge(klass: self))
11
11
  collector.check!
12
12
 
13
13
  Filter.new(collector).before_filter do
14
- Validator.validate(validate_against_key: collector.validate_against_key, reflection_names: collector.validate_with, object: self)
14
+ Validator.new(validate_against_key: collector.validate_against_key, reflection_names: collector.validate_with, object: self).validate
15
15
  end
16
16
  end
17
17
 
@@ -2,16 +2,46 @@ module ForeignKeyValidation
2
2
 
3
3
  class Validator
4
4
 
5
- def self.validate(opt={})
6
- validate_against_key, reflection_names, object = opt[:validate_against_key], opt[:reflection_names], opt[:object]
5
+ attr_accessor :validate_against_key, :reflection_names, :object
7
6
 
8
- reflection_names.each do |reflection_name|
9
- next if object.send(reflection_name).try(validate_against_key).nil? or object.try(validate_against_key).nil?
7
+ def initialize(opt={})
8
+ self.validate_against_key = opt[:validate_against_key]
9
+ self.reflection_names = opt[:reflection_names] || []
10
+ self.object = opt[:object]
11
+ end
10
12
 
11
- if object.send(reflection_name).send(validate_against_key) != object.send(validate_against_key)
12
- object.errors.add(validate_against_key, "#{validate_against_key} of #{reflection_name} does not match #{object.class.name.tableize} #{validate_against_key}.")
13
+ def validate
14
+ has_errors = false
15
+ reflection_names.each do |reflection_name|
16
+ next unless keys_present?(reflection_name)
17
+ if keys_different?(reflection_name)
18
+ attach_error(reflection_name)
19
+ has_errors = true
13
20
  end
14
21
  end
22
+ has_errors
23
+ end
24
+
25
+ private
26
+
27
+ def key_on_relation(relation)
28
+ object.send(relation).try(validate_against_key)
29
+ end
30
+
31
+ def key_on_object
32
+ object.try(validate_against_key)
33
+ end
34
+
35
+ def keys_present?(relation)
36
+ key_on_object.present? and key_on_relation(relation).present?
37
+ end
38
+
39
+ def keys_different?(relation)
40
+ key_on_object != key_on_relation(relation)
41
+ end
42
+
43
+ def attach_error(reflection_name)
44
+ object.errors.add(validate_against_key, ForeignKeyValidation.configuration.error_message.call(validate_against_key, reflection_name, object))
15
45
  end
16
46
 
17
47
  end
@@ -1,3 +1,3 @@
1
1
  module ForeignKeyValidation
2
- VERSION = "1.0.1"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe ForeignKeyValidation::Collector do
4
+
5
+ let(:user) { User.create }
6
+ let(:other_user) { User.create }
7
+
8
+ describe ".new" do
9
+
10
+ subject { ForeignKeyValidation::Collector }
11
+
12
+ it "initializes new validator" do
13
+ expect(subject.new).to be_instance_of ForeignKeyValidation::Collector
14
+ end
15
+
16
+ end
17
+
18
+ describe "#check!" do
19
+
20
+ it "returns true for known class" do
21
+ expect(ForeignKeyValidation::Collector.new(klass: Issue).check!).to be true
22
+ end
23
+
24
+ it "raises error for class without relations" do
25
+ expect{ForeignKeyValidation::Collector.new(klass: Dummy).check!}.to raise_error(ForeignKeyValidation::Errors::NoReleationFoundError)
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,127 @@
1
+ require 'spec_helper'
2
+
3
+ describe ForeignKeyValidation::ModelExtension do
4
+
5
+ let(:user) { User.create }
6
+ let(:other_user) { User.create }
7
+
8
+ let(:manager) { Manager.create user: user }
9
+ let(:other_manager) { Manager.create user: User.create }
10
+ let(:developer) { Developer.create user: user, boss: manager }
11
+
12
+ context "with custom error message without vars" do
13
+
14
+ before do
15
+ ForeignKeyValidation.configure {|c| c.error_message = proc {"CUSTOM MSG!"} }
16
+ Member.send :validate_foreign_keys
17
+ end
18
+
19
+ it "does not allow to rewrite user id of developer" do
20
+ developer.user_id = other_user.id
21
+ developer.save
22
+ expect(developer.errors.messages.values.flatten).to include("CUSTOM MSG!")
23
+ expect(developer.reload.user_id).to_not eq(other_user.id)
24
+ end
25
+
26
+ it "does not allow to rewrite boss id of developer" do
27
+ developer.custom_boss_id = other_manager.id
28
+ developer.save
29
+ expect(developer.errors.messages.values.flatten).to include("CUSTOM MSG!")
30
+ expect(developer.reload.custom_boss_id).to_not eq(other_manager.id)
31
+ end
32
+
33
+ end
34
+
35
+ context "with custom error message with vars" do
36
+
37
+ before do
38
+ ForeignKeyValidation.configure {|c| c.error_message = proc {|a,b,c| "#{a} #{b} #{c.id}"} }
39
+ Member.send :validate_foreign_keys
40
+ end
41
+
42
+ it "does not allow to rewrite user id of developer" do
43
+ developer.user_id = other_user.id
44
+ developer.save
45
+ expect(developer.errors.messages.values.flatten).to include("user_id boss #{developer.id}")
46
+ expect(developer.reload.user_id).to_not eq(other_user.id)
47
+ end
48
+
49
+ it "does not allow to rewrite boss id of developer" do
50
+ developer.custom_boss_id = other_manager.id
51
+ developer.save
52
+ expect(developer.errors.messages.values.flatten).to include("user_id boss #{developer.id}")
53
+ expect(developer.reload.custom_boss_id).to_not eq(other_manager.id)
54
+ end
55
+
56
+ end
57
+
58
+ context "without injecting subclasses via config block" do
59
+
60
+ before do
61
+ Member.send :validate_foreign_keys
62
+ end
63
+
64
+ it "does not allow to rewrite user id of developer" do
65
+ developer.user_id = other_user.id
66
+ developer.save
67
+ expect(developer.errors.messages.values.flatten).to include("user_id of boss does not match developers user_id.")
68
+ expect(developer.reload.user_id).to_not eq(other_user.id)
69
+ end
70
+
71
+ it "does not allow to rewrite boss id of developer" do
72
+ developer.custom_boss_id = other_manager.id
73
+ developer.save
74
+ expect(developer.errors.messages.values.flatten).to include("user_id of boss does not match developers user_id.")
75
+ expect(developer.reload.custom_boss_id).to_not eq(other_manager.id)
76
+ end
77
+
78
+ end
79
+
80
+ context "with injecting subclasses set to true via config block" do
81
+
82
+ before do
83
+ ForeignKeyValidation.configure {|c| c.inject_subclasses = true }
84
+ Member.send :validate_foreign_keys
85
+ end
86
+
87
+ it "does not allow to rewrite user id of developer" do
88
+ developer.user_id = other_user.id
89
+ developer.save
90
+ expect(developer.errors.messages.values.flatten).to include("user_id of boss does not match developers user_id.")
91
+ expect(developer.reload.user_id).to_not eq(other_user.id)
92
+ end
93
+
94
+ it "does not allow to rewrite boss id of developer" do
95
+ developer.custom_boss_id = other_manager.id
96
+ developer.save
97
+ expect(developer.errors.messages.values.flatten).to include("user_id of boss does not match developers user_id.")
98
+ expect(developer.reload.custom_boss_id).to_not eq(other_manager.id)
99
+ end
100
+
101
+ end
102
+
103
+
104
+ context "with injecting subclasses set to false via config block" do
105
+
106
+ before do
107
+ ForeignKeyValidation.configure {|c| c.inject_subclasses = false }
108
+ Member.send :validate_foreign_keys
109
+ end
110
+
111
+ it "does allow to rewrite user id of developer" do
112
+ developer.user_id = other_user.id
113
+ developer.save
114
+ expect(developer.errors.messages.values.flatten).to_not include("user_id of boss does not match developers user_id.")
115
+ expect(developer.reload.user_id).to eq(other_user.id)
116
+ end
117
+
118
+ it "does allow to rewrite boss id of developer" do
119
+ developer.custom_boss_id = other_manager.id
120
+ developer.save
121
+ expect(developer.errors.messages.values.flatten).to_not include("user_id of boss does not match developers user_id.")
122
+ expect(developer.reload.custom_boss_id).to eq(other_manager.id)
123
+ end
124
+
125
+ end
126
+
127
+ end
data/spec/spec_helper.rb CHANGED
@@ -14,6 +14,7 @@ RSpec.configure do |config|
14
14
 
15
15
  # reset and reload model classes for each run
16
16
  config.before(:each) do
17
+ ForeignKeyValidation.configuration = nil
17
18
  load "support/reset_models.rb"
18
19
  load "support/load_models.rb"
19
20
  end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe ForeignKeyValidation::Validator do
4
+
5
+ let(:user) { User.create }
6
+ let(:other_user) { User.create }
7
+
8
+ describe ".new" do
9
+
10
+ subject { ForeignKeyValidation::Validator }
11
+
12
+ it "initializes new validator" do
13
+ expect(subject.new).to be_instance_of ForeignKeyValidation::Validator
14
+ end
15
+
16
+ end
17
+
18
+ describe "#validate" do
19
+
20
+ subject { ForeignKeyValidation::Validator }
21
+
22
+ it "creates no validations if params blank" do
23
+ expect(subject.new.validate).to be false
24
+ end
25
+
26
+ it "creates no validations if object is valid" do
27
+ object = Issue.create(user: user, project: Project.create(user: user))
28
+ expect(subject.new(validate_against_key: :user_id, reflection_names: [:project], object: object).validate).to be false
29
+ end
30
+
31
+ it "creates validations if object is invalid" do
32
+ object = Issue.create(user: user, project: Project.create(user: other_user))
33
+ expect(subject.new(validate_against_key: :user_id, reflection_names: [:project], object: object).validate).to be true
34
+ end
35
+
36
+ end
37
+
38
+ 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: 1.0.1
4
+ version: 1.1.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-10-06 00:00:00.000000000 Z
11
+ date: 2014-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -167,15 +167,18 @@ files:
167
167
  - lib/foreign_key_validation/model_extension.rb
168
168
  - lib/foreign_key_validation/validator.rb
169
169
  - lib/foreign_key_validation/version.rb
170
- - spec/models/validation_with_custom_attrs_spec.rb
171
- - spec/models/validation_with_exceptions_spec.rb
172
- - spec/models/validation_with_missing_foreign_key_spec.rb
173
- - spec/models/validation_without_attrs_spec.rb
174
- - spec/models/without_validation_spec.rb
170
+ - spec/collector/base_spec.rb
171
+ - spec/model_extension/validation_with_config_block_spec.rb
172
+ - spec/model_extension/validation_with_custom_attrs_spec.rb
173
+ - spec/model_extension/validation_with_exceptions_spec.rb
174
+ - spec/model_extension/validation_with_missing_foreign_key_spec.rb
175
+ - spec/model_extension/validation_without_attrs_spec.rb
176
+ - spec/model_extension/without_validation_spec.rb
175
177
  - spec/spec_helper.rb
176
178
  - spec/support/load_models.rb
177
179
  - spec/support/reset_models.rb
178
180
  - spec/support/schema.rb
181
+ - spec/validator/base_spec.rb
179
182
  homepage: https://github.com/marcusg/foreign_key_validation
180
183
  licenses:
181
184
  - MIT
@@ -201,12 +204,15 @@ signing_key:
201
204
  specification_version: 4
202
205
  summary: Protect the foreign keys in your Rails models.
203
206
  test_files:
204
- - spec/models/validation_with_custom_attrs_spec.rb
205
- - spec/models/validation_with_exceptions_spec.rb
206
- - spec/models/validation_with_missing_foreign_key_spec.rb
207
- - spec/models/validation_without_attrs_spec.rb
208
- - spec/models/without_validation_spec.rb
207
+ - spec/collector/base_spec.rb
208
+ - spec/model_extension/validation_with_config_block_spec.rb
209
+ - spec/model_extension/validation_with_custom_attrs_spec.rb
210
+ - spec/model_extension/validation_with_exceptions_spec.rb
211
+ - spec/model_extension/validation_with_missing_foreign_key_spec.rb
212
+ - spec/model_extension/validation_without_attrs_spec.rb
213
+ - spec/model_extension/without_validation_spec.rb
209
214
  - spec/spec_helper.rb
210
215
  - spec/support/load_models.rb
211
216
  - spec/support/reset_models.rb
212
217
  - spec/support/schema.rb
218
+ - spec/validator/base_spec.rb