poro_validator 0.0.1

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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +13 -0
  3. data/Gemfile +11 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +382 -0
  6. data/Rakefile +139 -0
  7. data/lib/poro_validator.rb +54 -0
  8. data/lib/poro_validator/configuration.rb +63 -0
  9. data/lib/poro_validator/error_store.rb +52 -0
  10. data/lib/poro_validator/errors.rb +56 -0
  11. data/lib/poro_validator/exceptions.rb +14 -0
  12. data/lib/poro_validator/validator.rb +82 -0
  13. data/lib/poro_validator/validator/base_class.rb +55 -0
  14. data/lib/poro_validator/validator/conditions.rb +71 -0
  15. data/lib/poro_validator/validator/context.rb +19 -0
  16. data/lib/poro_validator/validator/factory.rb +32 -0
  17. data/lib/poro_validator/validator/validation.rb +50 -0
  18. data/lib/poro_validator/validator/validations.rb +55 -0
  19. data/lib/poro_validator/validators/base_class.rb +53 -0
  20. data/lib/poro_validator/validators/exclusion_validator.rb +26 -0
  21. data/lib/poro_validator/validators/float_validator.rb +17 -0
  22. data/lib/poro_validator/validators/format_validator.rb +16 -0
  23. data/lib/poro_validator/validators/inclusion_validator.rb +26 -0
  24. data/lib/poro_validator/validators/integer_validator.rb +17 -0
  25. data/lib/poro_validator/validators/length_validator.rb +41 -0
  26. data/lib/poro_validator/validators/numeric_validator.rb +48 -0
  27. data/lib/poro_validator/validators/presence_validator.rb +23 -0
  28. data/lib/poro_validator/validators/range_array_validator.rb +19 -0
  29. data/lib/poro_validator/validators/with_validator.rb +21 -0
  30. data/lib/poro_validator/version.rb +3 -0
  31. data/poro_validator.gemspec +97 -0
  32. data/spec/features/composable_validations_spec.rb +26 -0
  33. data/spec/features/inheritable_spec.rb +29 -0
  34. data/spec/features/nested_validations_spec.rb +136 -0
  35. data/spec/lib/poro_validator/configuration_spec.rb +37 -0
  36. data/spec/lib/poro_validator/error_store_spec.rb +125 -0
  37. data/spec/lib/poro_validator/errors_spec.rb +79 -0
  38. data/spec/lib/poro_validator/validator/base_class_spec.rb +62 -0
  39. data/spec/lib/poro_validator/validator/conditions_spec.rb +112 -0
  40. data/spec/lib/poro_validator/validator/factory_spec.rb +23 -0
  41. data/spec/lib/poro_validator/validator/validation_spec.rb +69 -0
  42. data/spec/lib/poro_validator/validator/validations_spec.rb +34 -0
  43. data/spec/lib/poro_validator/validator_spec.rb +55 -0
  44. data/spec/lib/poro_validator/validators/base_class_spec.rb +11 -0
  45. data/spec/lib/poro_validator/validators/exclusion_validator_spec.rb +81 -0
  46. data/spec/lib/poro_validator/validators/float_validator_spec.rb +43 -0
  47. data/spec/lib/poro_validator/validators/format_validator_spec.rb +48 -0
  48. data/spec/lib/poro_validator/validators/inclusion_validator_spec.rb +81 -0
  49. data/spec/lib/poro_validator/validators/integer_validator_spec.rb +43 -0
  50. data/spec/lib/poro_validator/validators/length_validator_spec.rb +64 -0
  51. data/spec/lib/poro_validator/validators/numeric_validator_spec.rb +68 -0
  52. data/spec/lib/poro_validator/validators/presence_validator_spec.rb +52 -0
  53. data/spec/lib/poro_validator/validators/with_validator_spec.rb +90 -0
  54. data/spec/poro_validator_spec.rb +25 -0
  55. data/spec/spec_helper.rb +34 -0
  56. data/spec/support/spec_helpers/concerns.rb +46 -0
  57. data/spec/support/spec_helpers/validator_test_macros.rb +99 -0
  58. metadata +199 -0
@@ -0,0 +1,79 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe PoroValidator::Errors do
4
+ subject(:errors) { described_class.new }
5
+
6
+ describe "#add" do
7
+ it "adds a message stored in an array for an attribute" do
8
+ errors.add(:foo, "manbearpig")
9
+ expect(errors.on(:foo)).to eq(["manbearpig"])
10
+ end
11
+
12
+ it "adds additional messages for an attribute if it's already been set" do
13
+ errors.add(:foo, "manbearpig")
14
+ errors.add(:foo, "excelsior")
15
+ expect(errors.on(:foo)).to eq(["manbearpig", "excelsior"])
16
+ end
17
+ end
18
+
19
+ describe "#count" do
20
+ it "counts the messages assigned for an attribute" do
21
+ errors.add(:presence, "manbearpig")
22
+ errors.add(:presence, "magiclife")
23
+ errors.add(:foo, "excelsior")
24
+ expect(errors.count).to eq(3)
25
+ end
26
+ end
27
+
28
+ describe "#empty?" do
29
+ it "returns true if count is zero" do
30
+ expect(errors.count).to eq(0)
31
+ expect(errors.empty?).to be_truthy
32
+ end
33
+
34
+ it "returns false if count is not zero" do
35
+ errors.add(:presence, "manbearpig")
36
+ expect(errors.count).to eq(1)
37
+ expect(errors.empty?).to be_falsey
38
+ end
39
+ end
40
+
41
+ describe "#full_messages" do
42
+ before(:each) do
43
+ errors.add(:foo, "manbearpig")
44
+ end
45
+
46
+ it "returns an array" do
47
+ expect(errors.full_messages).to be_a(::Array)
48
+ end
49
+
50
+ it "returns an array of messages" do
51
+ expect(errors.full_messages).to eq(["foo manbearpig"])
52
+ end
53
+ end
54
+
55
+ describe "#on" do
56
+ context "if the key has an existing value" do
57
+ it "returns the message associated with the key" do
58
+ errors.add(:foo, "manbearpig")
59
+ expect(errors.on(:foo)).to eq(["manbearpig"])
60
+ end
61
+
62
+ end
63
+
64
+ context "if the key has no existing value" do
65
+ it "returns nil" do
66
+ expect(errors.on(:batman)).to be_nil
67
+ end
68
+ end
69
+ end
70
+
71
+ describe "#clear_errors" do
72
+ it "clears the stored error messages" do
73
+ errors.add(:foo, "manbearpig")
74
+ expect(errors.count).to eq(1)
75
+ errors.clear_errors
76
+ expect(errors.empty?).to be_truthy
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe PoroValidator::Validator::BaseClass do
4
+ subject { described_class.new }
5
+
6
+ describe "<<" do
7
+ it "add/set validations" do
8
+ validation = [:foo, :faa]
9
+ subject << validation
10
+ expect(subject.validations).to eq([validation])
11
+ end
12
+ end
13
+
14
+ describe "#run_validations" do
15
+ let(:kontext) do
16
+ Class.new do
17
+ attr_accessor :entity
18
+ end.new
19
+ end
20
+
21
+ let(:klass) do
22
+ Class.new do
23
+ def __validate__(kotenxt)
24
+ true
25
+ end
26
+ end
27
+ end
28
+
29
+ context "if the conditions are matched" do
30
+ it "runs each validation" do
31
+ allow(PoroValidator::Validator::Conditions).to receive(:matched?).and_return(true)
32
+
33
+ validator1 = { validator: klass.new, conditions: [nil] }
34
+ validator2 = { validator: klass.new, conditions: [nil] }
35
+
36
+ subject << validator1
37
+ subject << validator2
38
+
39
+ expect(validator1[:validator]).to receive(:__validate__).
40
+ with(kontext).once
41
+ expect(validator2[:validator]).to receive(:__validate__).
42
+ with(kontext).once
43
+
44
+ subject.run_validations(kontext)
45
+ end
46
+ end
47
+
48
+ context "if the conditions are not matched" do
49
+ it "does not runs the validation" do
50
+ allow(PoroValidator::Validator::Conditions).to receive(:matched?).and_return(false)
51
+
52
+ validator = { validator: klass.new, conditions: [nil] }
53
+
54
+ subject << validator
55
+
56
+ expect(validator[:validator]).to_not receive(:__validate__)
57
+
58
+ subject.run_validations(kontext)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,112 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe PoroValidator::Validator::Conditions do
4
+ describe ".matched?" do
5
+ let(:entity) do
6
+ Class.new do
7
+ attr_accessor :name
8
+ end.new
9
+ end
10
+
11
+ let(:kontext) do
12
+ Class.new do
13
+ attr_accessor :entity
14
+ end.new
15
+ end
16
+
17
+ before(:each) do
18
+ kontext.entity = entity
19
+ end
20
+
21
+ context "if the conditions are matched" do
22
+ it "returns true" do
23
+ conditions = [ { if: proc { true } }, { if: proc { true } } ]
24
+
25
+ expect(described_class.matched?(conditions, kontext)).to be_truthy
26
+ end
27
+
28
+ context "if :unless conditions are met" do
29
+ it "returns true" do
30
+ conditions = [ { if: proc { true } }, { unless: proc { false } } ]
31
+
32
+ expect(described_class.matched?(conditions, kontext)).to be_truthy
33
+ end
34
+ end
35
+ end
36
+
37
+ context "if the conditions are not matched" do
38
+ it "returns false" do
39
+ conditions = [ { if: proc { true } }, { if: proc { false } } ]
40
+
41
+ expect(described_class.matched?(conditions, kontext)).to be_falsey
42
+ end
43
+
44
+ context "if :unless conditions are not met" do
45
+ it "returns true" do
46
+ conditions = [ { if: proc { true } }, { unless: proc { true } } ]
47
+
48
+ expect(described_class.matched?(conditions, kontext)).to be_falsey
49
+ end
50
+ end
51
+ end
52
+
53
+ context "if the conditions are passed in an array" do
54
+ it "runs each condition" do
55
+ condition1 = lambda { true }
56
+ condition2 = lambda { true }
57
+ conditions = [ { if: [condition1, condition2] } ]
58
+
59
+ expect(condition1).to receive(:call).once
60
+ expect(condition2).to receive(:call).once
61
+ described_class.matched?(conditions, kontext)
62
+ end
63
+ end
64
+
65
+ context "if the condition type is unknown" do
66
+ it "raises an exception ::PoroValidator::InvalidCondition" do
67
+ conditions = [ { hello: true } ]
68
+
69
+ expect do
70
+ described_class.matched?(conditions, kontext)
71
+ end.to raise_error(::PoroValidator::InvalidCondition)
72
+ end
73
+ end
74
+
75
+ context "if the condition is a String" do
76
+ it "evaluates the string based on the context's entity" do
77
+ conditions = [ { if: 'name.nil?' } ]
78
+
79
+ expect(entity).to receive(:instance_eval).with(conditions.first[:if]).once
80
+ described_class.matched?(conditions, kontext)
81
+ end
82
+ end
83
+
84
+ context "if the condition is a Symbol" do
85
+ it "calls the lambda for that condition type" do
86
+ conditions = [ { if: :name } ]
87
+
88
+ expect(kontext).to receive(:send).with(conditions.first[:if], entity).once
89
+ described_class.matched?(conditions, kontext)
90
+ end
91
+ end
92
+
93
+ context "if the condition is a Proc" do
94
+ it "calls the lambda/proc for that condition type" do
95
+ conditions = [ { if: proc { true } } ]
96
+
97
+ expect(conditions.first[:if]).to receive(:call).once
98
+ described_class.matched?(conditions, kontext)
99
+ end
100
+ end
101
+
102
+ context "if the condition is neither of the cases" do
103
+ it "raises an exception ::PoroValidator::ValidatorException" do
104
+ conditions = [ { if: true } ]
105
+
106
+ expect do
107
+ described_class.matched?(conditions, kontext)
108
+ end.to raise_error(::PoroValidator::InvalidCondition)
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe PoroValidator::Validator::Factory::Validators do
4
+ describe "#new" do
5
+ subject { described_class.set_validator(:foo_attr, validator, {}) }
6
+
7
+ context "if it finds the validator class" do
8
+ let(:validator) { :presence }
9
+
10
+ it "does not raise an exception" do
11
+ expect { subject }.to_not raise_error
12
+ end
13
+ end
14
+
15
+ context "if it does not find the validator class" do
16
+ let(:validator) { :foo }
17
+
18
+ it "does raise an exception" do
19
+ expect { subject }.to raise_error(::PoroValidator::ValidatorNotFound)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe PoroValidator::Validator::Validation do
4
+ describe ".build" do
5
+ let(:options) { {} }
6
+ # we are defaulting to presence validator so that it does not error out
7
+ # if we pass in a random non existent validator for this tests
8
+ let(:validator) { :presence }
9
+
10
+ subject { described_class.build(:foo_attr, validator, options) }
11
+
12
+ it "returns a Validators object" do
13
+ expect(subject).to be_a(PoroValidator::Validators::BaseClass)
14
+ end
15
+
16
+ it "sets the validator to the Validator Class" do
17
+ expect(subject.class.name).to eq('PoroValidator::Validators::PresenceValidator')
18
+ end
19
+
20
+
21
+ context "if options is TrueClass" do
22
+ let(:options) { true }
23
+
24
+ it "sets the options to a hash" do
25
+ expect(subject.options).to eq({})
26
+ end
27
+ end
28
+
29
+ context "if options is Range" do
30
+ let(:options) { 1..3 }
31
+
32
+ it "sets options to a hash with an in: key with an range value" do
33
+ expect(subject.options).to eq({ in: 1..3 })
34
+ end
35
+ end
36
+
37
+ context "if options is Array" do
38
+ let(:options) { ['foo', 'faa'] }
39
+
40
+ it "sets options to a hash with an in: key with an array value" do
41
+ expect(subject.options).to eq({ in: ['foo', 'faa'] })
42
+ end
43
+ end
44
+
45
+ context "if options doe not match any case" do
46
+ let(:options) { 55 }
47
+
48
+ it "converts it to a hash with a with: key" do
49
+ expect(subject.options).to eq({ with: 55 })
50
+ end
51
+ end
52
+ end
53
+
54
+ describe "#attr_name" do
55
+ subject { described_class.new(:foo_attr, :foo_validator, {}) }
56
+
57
+ it "returns the attribute name" do
58
+ expect(subject.attr_name).to eq(:foo_attr)
59
+ end
60
+ end
61
+
62
+ describe "#validator" do
63
+ subject { described_class.new(:foo_attr, :foo_validator, {}) }
64
+
65
+ it "returns the validator" do
66
+ expect(subject.validator).to eq(:foo_validator)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe PoroValidator::Validator::Validations do
4
+ describe ".build" do
5
+ subject do
6
+ klass = described_class.new
7
+ klass.build(:foo_attr, presence: true,
8
+ format: /[a-z]/, if: true, unless: true)
9
+ klass
10
+ end
11
+
12
+ it "can build multiple validators" do
13
+ expect(subject.validations.size).to eq(2)
14
+ end
15
+
16
+ context "if there are nested conditions within the validator's options" do
17
+ subject do
18
+ klass = described_class.new
19
+ klass.build(:foo_attr, presence: { message: "hello", if: true })
20
+ klass
21
+ end
22
+
23
+ let(:validation) { subject.validations.first }
24
+
25
+ it "can select then nested condition" do
26
+ expect(validation[:conditions]).to eq([{ if: true }])
27
+ end
28
+
29
+ it "removes the nested condition from the validator options" do
30
+ expect(validation[:validator].options).to_not have_key(:if)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe PoroValidator::Validator do
4
+ describe "#validates" do
5
+ it "allows validations to be set" do
6
+ obj = Class.new do
7
+ include PoroValidator.validator
8
+
9
+ validates :foo, presence: true
10
+ end
11
+
12
+ validations = obj.send(:validations)
13
+
14
+ expect(validations.validations.count).to eq(1)
15
+ expect(validations.validators).to eq({presence: true})
16
+ end
17
+ end
18
+
19
+ describe "#valid?" do
20
+ let(:entity) { OpenStruct.new(name: nil) }
21
+ let(:validator) do
22
+ Class.new do
23
+ include PoroValidator.validator
24
+
25
+ validates :name, presence: true
26
+ end.new
27
+ end
28
+
29
+ context "if the entity is not valid" do
30
+ it "returns false" do
31
+ expect(validator.valid?(entity)).to be_falsey
32
+ end
33
+
34
+ it "returns the errors for related to the entity" do
35
+ validator.valid?(entity)
36
+ expect(validator.errors.full_messages).to eq(["name is not present"])
37
+ end
38
+ end
39
+
40
+ context "if the entity is valid" do
41
+ before(:each) do
42
+ entity.name = "manbearpig"
43
+ end
44
+
45
+ it "returns true" do
46
+ expect(validator.valid?(entity)).to be_truthy
47
+ end
48
+
49
+ it "returns an empty hash for the #errors" do
50
+ validator.valid?(entity)
51
+ expect(validator.errors).to be_empty
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe PoroValidator::Validators::BaseClass do
4
+ describe "#validate" do
5
+ it "raises NotImplementedEror" do
6
+ klass = described_class.new(:foo)
7
+ expect { klass.validate(nil, nil, nil) }.
8
+ to raise_error(::PoroValidator::OverloadriddenRequired)
9
+ end
10
+ end
11
+ end