foreign_key_validation 1.0.1 → 1.1.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
- 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