subvalid 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 19c0fec940d79b907b072f1df373e0b6add70f91
4
+ data.tar.gz: f2993841ad0edc5f065280f9518ed9e0df8d1a19
5
+ SHA512:
6
+ metadata.gz: f41942e32113e9101e847c787e14fee98ec9b8a50baa6ddaba10d5e25b6ae73bf20b18d6df5fcdcdfae27cda032d24641d19f2452c9323fcbf5ec5e64f628c38
7
+ data.tar.gz: 4ecd389d43b363882c2378b89b649be4baf2e0dde5ff5df31812b13a8b28bd8b0a813462867df94ce2e2a80fb42d9c8b93c775bddec60cc7d9a59f83cb2aad81
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
4
+ - 2.1.6
5
+ - 2.0.0
6
+ - ruby-head
7
+ - jruby-9.0.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in subvalid.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Envato
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # Subvalid
2
+
3
+ Subvalid decouples your validation logic from your object structure. With
4
+ Subvalid you can define different validation rules for different contexts. So
5
+ rather than defining validation on the object, and having it be _"objective"_,
6
+ you can define it in a separate class - so it's _"subjective"_. (as in **Sub**jective
7
+ **valid**ation).
8
+
9
+ Subvalid was extracted from a project at [Envato](http://envato.com) which
10
+ required complex validation logic at each stage of the object's life cycle:
11
+ - Users upload videos. The videos are validated to make sure an actual
12
+ video was uploaded (and not someone's university Powerpoint slides), that
13
+ framerate is good, resolution and codec is acceptable etc. Failure here would
14
+ reject the file straight away, and tell the user to try again with a new file.
15
+ - Next we generate thumbnails, resized preview videos etc. If anything fails
16
+ validation here, it's a bug (wrong preview video size etc) - and we want to
17
+ alert developers.
18
+ - After the video is uploaded and processed, users would enter metadata: title,
19
+ description, tags etc. If that fails - we still want to save the item, but
20
+ just leave it as _"incomplete"_, and allow the user to come back later and
21
+ complete it. Once this passes, the item is ready, and we submit it for review to
22
+ our internal review team.
23
+
24
+ While
25
+ [ActiveModel::Validations](http://api.rubyonrails.org/classes/ActiveModel/Validations.html)
26
+ is great if you've got simple validation logic, it doesn't cut it for something
27
+ complex like this. When you have different validation for the same object at
28
+ each point in it's life cycle, you need something more flexible.
29
+
30
+ ActiveModel also hooks in pretty deep into
31
+ [ActiveRecord](http://guides.rubyonrails.org/active_record_validations.html).
32
+ It's main use case assume you're just wanting to prevent bad data hitting your
33
+ database - which isn't necessarily always the case.
34
+
35
+ We needed something more. So Subvalid was born.
36
+
37
+ And you can have the best of both worlds. Subvalid can exist alongside
38
+ ActiveModel. ActiveModel::Validations is great for ensuring data consistency,
39
+ and you can add it to your model classes as normal - and then write Subvalid
40
+ validator classes in addition to handle more complex nuanced validation logic.
41
+ Or do it all in Subvalid - up to you.
42
+
43
+ ## Features
44
+ - Very simple, consistent API
45
+ - Validation logic is defined in separate _"Validator"_ classes completely
46
+ decoupled from business logic
47
+ - Multiple validators can be defined for each piece of data in your system to be
48
+ executed at different points
49
+ - Caller is in control. No magic happening under the hood
50
+ - Failing validation does not block saving to the database
51
+ - Does not add _anything_ at all to business objects. No including modules, no
52
+ monkey patching, no object extension. Subvalid assumes POROs, but works with
53
+ anything. A key design goal is to **not** pollute the objects being validated at
54
+ all
55
+ - Supports nested validation on nested object structures - and nicely handles
56
+ nested errors.
57
+
58
+ ## Development Status [![travis ci build](https://api.travis-ci.org/envato/subvalid.svg)](https://travis-ci.org/envato/subvalid)
59
+
60
+ Subvalid is extracted from production code in use at Envato. However, it is undergoing early development, and APIs and features are almost certain to be in flux.
61
+
62
+ ## Getting Started
63
+
64
+ Add this line to your application's Gemfile:
65
+
66
+ ```ruby
67
+ gem 'subvalid'
68
+ ```
69
+
70
+ And then execute:
71
+
72
+ $ bundle
73
+
74
+ Or install it yourself as:
75
+
76
+ $ gem install subvalid
77
+
78
+ ## Usage
79
+
80
+ TODO: Write usage instructions here
81
+
82
+ ## Contributing
83
+
84
+ 1. Fork it ( https://github.com/[my-github-username]/subvalid/fork )
85
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
86
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
87
+ 4. Push to the branch (`git push origin my-new-feature`)
88
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
@@ -0,0 +1,65 @@
1
+ module Subvalid
2
+ class ValidationResult
3
+ attr_reader :errors, :children
4
+
5
+ def initialize
6
+ @errors = []
7
+ @children = {}
8
+ end
9
+
10
+ def valid?
11
+ errors.empty? && children.values.all?(&:valid?)
12
+ end
13
+
14
+ def add_error(error)
15
+ errors << error
16
+ end
17
+
18
+ def [](attribute)
19
+ children[attribute]
20
+ end
21
+
22
+ def merge_child!(attribute, result)
23
+ child = children[attribute]
24
+ if child
25
+ child.merge!(result)
26
+ else
27
+ children[attribute] = result
28
+ end
29
+ end
30
+
31
+ def to_h
32
+ hash = {}
33
+ hash[:errors] = errors.dup unless errors.empty?
34
+ children.each do |attribute, child|
35
+ hash[attribute] = child.to_h unless child.valid?
36
+ end
37
+ hash
38
+ end
39
+
40
+ def flatten(parent_attributes=[])
41
+ # TODO make this more flexible than just hardcoded format
42
+ flat_errors = errors.map{|error|
43
+ if parent_attributes.empty?
44
+ error
45
+ else
46
+ human_keys = parent_attributes.map{|a| a.to_s.gsub('_', ' ')}
47
+ [human_keys.join(", "), error].join(": ")
48
+ end
49
+ }
50
+ children.each do |attribute, child|
51
+ flat_errors += child.flatten(parent_attributes + [attribute])
52
+ end
53
+ flat_errors
54
+ end
55
+
56
+ protected
57
+ def merge!(result)
58
+ @errors += result.errors
59
+ children.merge!(result.children){|key, old_child, new_child|
60
+ old_child.merge!(new_child)
61
+ old_child
62
+ }
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,81 @@
1
+ module Subvalid
2
+ module Validator
3
+ def self.included(base)
4
+ base.class_eval do
5
+ extend DSL
6
+ end
7
+ base.extend API
8
+ end
9
+
10
+ module DSL
11
+
12
+ def validates(*attributes, **validators, &block)
13
+ if validators.empty? && !block
14
+ raise "no validations or block specified"
15
+ end
16
+
17
+ attributes = [:base] if attributes.empty?
18
+
19
+ add_validations(attributes, validators, block)
20
+ end
21
+
22
+ private
23
+ ValidatorEntry = Struct.new(:validator, :args)
24
+ def validations
25
+ @validations ||= Hash.new{|vals,attribute| vals[attribute] = [] }
26
+ end
27
+
28
+ def add_validations(attributes, validators, block)
29
+ attributes.each do |attribute|
30
+ validators.each do |validator_key, args|
31
+ validator = ValidatorRegistry[validator_key]
32
+ validations[attribute] << ValidatorEntry.new(validator, args)
33
+ end
34
+ validations[attribute] << ValidatorEntry.new(BlockValidator, block) if block
35
+ end
36
+ end
37
+ end
38
+
39
+ module API
40
+ def validate(object, validation_result=ValidationResult.new, *args)
41
+ validations.each do |attribute, validators|
42
+ attribute_result = if attribute == :base
43
+ validation_result
44
+ else
45
+ ValidationResult.new
46
+ end
47
+
48
+ validators.each do |validator_entry|
49
+ validator = validator_entry.validator
50
+ if attribute == :base
51
+ validator.validate(object, attribute_result, *validator_entry.args)
52
+ elsif object
53
+ validator.validate(object.send(attribute), attribute_result, *validator_entry.args)
54
+ else
55
+ # no-op if we 're asked to validate an attribute for a nil value - that needs to be caught by a user defined `presence` validation instead
56
+ end
57
+ end
58
+
59
+ validation_result.merge_child!(attribute, attribute_result) unless attribute == :base
60
+ end
61
+ validation_result
62
+ end
63
+ end
64
+
65
+ class BlockValidator
66
+ class Context
67
+ include Subvalid::Validator::DSL
68
+ include Subvalid::Validator::API
69
+ end
70
+
71
+ def self.validate(object, validation_result=ValidationResult.new, *args)
72
+ #return unless object # don't pass nil object into block - this should be handled with a PresenceValidator if it needs to be flagged as a validation error
73
+ block = args[0]
74
+
75
+ context = Context.new
76
+ context.instance_exec(&block)
77
+ context.validate(object, validation_result, args)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,18 @@
1
+ module Subvalid
2
+ class ValidatorRegistry
3
+ class << self
4
+ def [](key)
5
+ validators[key] or raise "no validator with key=#{key}"
6
+ end
7
+
8
+ def register(key, validator)
9
+ validators[key] = validator
10
+ end
11
+
12
+ private
13
+ def validators
14
+ @validators ||= {}
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ module Subvalid
2
+ module Validators
3
+ class FormatValidator
4
+ def self.validate(object, validation_result=ValidationResult.new, *args)
5
+ options = args.to_h rescue args
6
+ with = nil
7
+ message = "is invalid"
8
+ case options
9
+ when Regexp
10
+ with = options
11
+ when Hash
12
+ with = options.fetch(:with)
13
+ message = options[:message] || message
14
+ else
15
+ raise "don't know what to do with #{options}"
16
+ end
17
+ validation_result.add_error(message) unless with.match(object)
18
+ end
19
+ end
20
+ ValidatorRegistry.register(:format, FormatValidator)
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module Subvalid
2
+ module Validators
3
+ class InValidator
4
+ def self.validate(object, validation_result=ValidationResult.new, *args)
5
+ options = args.to_h rescue args
6
+ within = nil
7
+ message = "is not included in the list"
8
+ case options
9
+ when Hash
10
+ within = options.fetch(:within)
11
+ message = options[:message] || message
12
+ when Array
13
+ within = options
14
+ else
15
+ raise "don't know what to do with #{options}"
16
+ end
17
+ validation_result.add_error(message) unless within.include?(object)
18
+ end
19
+ end
20
+ ValidatorRegistry.register(:in, InValidator)
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ module Subvalid
2
+ module Validators
3
+ class LengthValidator
4
+ def self.validate(object, validation_result=ValidationResult.new, *args)
5
+ return unless object
6
+ args = args.to_h
7
+ args.each do |operator, value|
8
+ case operator
9
+ when :maximum
10
+ validation_result.add_error("is too long, maximum is #{value}") if object.size > value
11
+ # TODO ALL the other operators from http://guides.rubyonrails.org/active_record_validations.html#length
12
+ else
13
+ raise "don't know what to do with operator=#{operator}"
14
+ end
15
+ end
16
+ end
17
+ end
18
+ ValidatorRegistry.register(:length, LengthValidator)
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ module Subvalid
2
+ module Validators
3
+ class NumericalityValidator
4
+ def self.validate(object, validation_result=ValidationResult.new, *args)
5
+ args = args.to_h
6
+ args.each do |operator, value|
7
+ case operator
8
+ when :greater_than_or_equal_to
9
+ validation_result.add_error("must be greater than or equal to #{value}" ) unless object >= value
10
+ # TODO ALL the other operators from http://guides.rubyonrails.org/active_record_validations.html#numericality
11
+ else
12
+ raise "don't know what to do with operator=#{operator}"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ ValidatorRegistry.register(:numericality, NumericalityValidator)
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ module Subvalid
2
+ module Validators
3
+ class PresenceValidator
4
+ def self.validate(object, validation_result=ValidationResult.new, *args)
5
+ present = if object
6
+ if object.respond_to?(:present?)
7
+ object.present?
8
+ else
9
+ object
10
+ end
11
+ end
12
+
13
+ validation_result.add_error("is not present") unless present
14
+ end
15
+ end
16
+ ValidatorRegistry.register(:presence, PresenceValidator)
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ module Subvalid
2
+ module Validators
3
+ class WithValidator
4
+ def self.validate(object, validation_result=ValidationResult.new, *args)
5
+ case args[0]
6
+ when Class
7
+ klass = args[0]
8
+ klass.validate(object, validation_result)
9
+ when Proc
10
+ prok = args[0]
11
+ prok.(object, validation_result)
12
+ end
13
+ end
14
+ end
15
+ ValidatorRegistry.register(:with, WithValidator)
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ require "subvalid/validators/format_validator"
2
+ require "subvalid/validators/in_validator"
3
+ require "subvalid/validators/length_validator"
4
+ require "subvalid/validators/numericality_validator"
5
+ require "subvalid/validators/presence_validator"
6
+ require "subvalid/validators/with_validator"
7
+ module Subvalid
8
+ module Validators
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module Subvalid
2
+ VERSION = "0.0.1"
3
+ end
data/lib/subvalid.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "subvalid/version"
2
+ require "subvalid/validation_result"
3
+ require "subvalid/validator"
4
+ require "subvalid/validator_registry"
5
+ require "subvalid/validators"
6
+
7
+ module Subvalid
8
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
2
+ require "subvalid"
@@ -0,0 +1,159 @@
1
+ require "spec_helper"
2
+
3
+ describe Subvalid::ValidationResult do
4
+ describe "#valid?" do
5
+ context "when there are no errors" do
6
+ context "when children have no errors" do
7
+ before do
8
+ child = Subvalid::ValidationResult.new
9
+ subject.children[:foo] = child
10
+ end
11
+ it { is_expected.to be_valid }
12
+ end
13
+
14
+ context "when children have errors" do
15
+ before do
16
+ child = Subvalid::ValidationResult.new
17
+ child.add_error("this is an error")
18
+ subject.children[:foo] = child
19
+ end
20
+ it { is_expected.to_not be_valid }
21
+ end
22
+ end
23
+
24
+ context "when there are errors" do
25
+ before do
26
+ subject.add_error("this is an error")
27
+ end
28
+ it { is_expected.to_not be_valid }
29
+ end
30
+ end
31
+
32
+ describe "#add_error" do
33
+ it "adds the error" do
34
+ subject.add_error("duh")
35
+ subject.add_error("doh")
36
+ expect(subject.errors).to eq(["duh", "doh"])
37
+ end
38
+ end
39
+
40
+ describe "#[]" do
41
+ context "when child attribute exists" do
42
+ let(:child) { Subvalid::ValidationResult.new }
43
+ before do
44
+ subject.children[:foo] = child
45
+ end
46
+ it "returns the child validation result" do
47
+ expect(subject[:foo]).to be(child)
48
+ end
49
+ end
50
+
51
+ context "when child attribute does not exist" do
52
+ it "returns nil" do
53
+ expect(subject[:blah]).to be_nil
54
+ end
55
+ end
56
+ end
57
+
58
+ describe "merge_child!" do
59
+ context "when child attribute already exists" do
60
+ let(:child1) {
61
+ result = Subvalid::ValidationResult.new
62
+ result.add_error("Insufficient cheese")
63
+ grandchild = Subvalid::ValidationResult.new
64
+ grandchild.add_error("not tasty")
65
+ result.merge_child!(:tastiness, grandchild)
66
+ grandchild2 = Subvalid::ValidationResult.new
67
+ grandchild2.add_error("it's not OK")
68
+ result.merge_child!(:ok, grandchild2)
69
+ result
70
+ }
71
+ let(:child2) {
72
+ result = Subvalid::ValidationResult.new
73
+ result.add_error("blue cheese is awful")
74
+ grandchild = Subvalid::ValidationResult.new
75
+ grandchild.add_error("more smelly than tasty")
76
+ result.merge_child!(:tastiness, grandchild)
77
+ result
78
+ }
79
+ it "merges results together recursively" do
80
+ subject.merge_child!(:cheese, child1)
81
+ subject.merge_child!(:cheese, child2)
82
+ expect(subject[:cheese].errors).to eq(["Insufficient cheese", "blue cheese is awful"])
83
+ expect(subject[:cheese][:tastiness].errors).to eq(["not tasty", "more smelly than tasty"])
84
+ expect(subject[:cheese][:ok].errors).to eq(["it's not OK"])
85
+ end
86
+ end
87
+
88
+ context "when child attribute doesn't exist" do
89
+ let(:child) {
90
+ result = Subvalid::ValidationResult.new
91
+ result.add_error("Insufficient cheese")
92
+ result
93
+ }
94
+ it "sets the child result from the passed in result" do
95
+ subject.merge_child!(:cheese, child)
96
+ expect(subject[:cheese].errors).to eq(["Insufficient cheese"])
97
+ end
98
+ end
99
+ end
100
+
101
+ describe "#to_h" do
102
+ before do
103
+ child = Subvalid::ValidationResult.new
104
+ child.add_error("this is an error")
105
+ grandchild1 = Subvalid::ValidationResult.new
106
+ grandchild1.add_error("this is another error")
107
+ grandchild2 = Subvalid::ValidationResult.new # no errors
108
+ child.children[:bar] = grandchild1
109
+ child.children[:baz] = grandchild2
110
+ subject.children[:foo] = child
111
+ subject.add_error("this is a base error")
112
+ end
113
+
114
+ it "generates a hash of attributes with errors" do
115
+ expect(subject.to_h).to eq({
116
+ errors: ["this is a base error"],
117
+ foo: {
118
+ errors: ["this is an error"],
119
+ bar: {
120
+ errors: ["this is another error"]
121
+ }
122
+ }
123
+ })
124
+ end
125
+
126
+ it "has errors on attribute itself in special :errors key" do
127
+ hash = subject.to_h
128
+ expect(hash[:errors]).to eq(["this is a base error"])
129
+ expect(hash[:foo][:errors]).to eq(["this is an error"])
130
+ expect(hash[:foo][:bar][:errors]).to eq(["this is another error"])
131
+ end
132
+
133
+ it "doesn't include anything for attributes that are valid" do
134
+ expect(subject.to_h[:foo]).to_not have_key(:baz)
135
+ end
136
+ end
137
+
138
+ describe "#flatten" do
139
+ before do
140
+ child = Subvalid::ValidationResult.new
141
+ child.add_error("this is an error")
142
+ grandchild1 = Subvalid::ValidationResult.new
143
+ grandchild1.add_error("this is another error")
144
+ grandchild2 = Subvalid::ValidationResult.new # no errors
145
+ child.children[:bar] = grandchild1
146
+ child.children[:baz] = grandchild2
147
+ subject.children[:foo] = child
148
+ subject.add_error("this is a base error")
149
+ end
150
+
151
+ it "generates flat list of error messages recursively" do
152
+ expect(subject.flatten).to eq([
153
+ "this is a base error",
154
+ "foo: this is an error",
155
+ "foo, bar: this is another error"
156
+ ])
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,28 @@
1
+ require "spec_helper"
2
+
3
+ describe Subvalid::ValidatorRegistry do
4
+ describe "#[]" do
5
+ before do
6
+ described_class.register(:accessor_test, "a validator")
7
+ end
8
+ context "when validator with key is registered" do
9
+ it "returns the validator" do
10
+ expect(described_class[:accessor_test]).to eq("a validator")
11
+ end
12
+ end
13
+
14
+ context "when validator doesn't exist" do
15
+ it "raises an error" do
16
+ expect { described_class[:bad_key] }.to raise_error
17
+ end
18
+ end
19
+ end
20
+
21
+ describe "#register" do
22
+ it "add the validator with the key" do
23
+ # yes, this is a duplicat of the #[] spec, but it demonstrates the publicly API, so we'll have it
24
+ described_class.register(:register_test, "registered validator")
25
+ expect(described_class[:register_test]).to eq("registered validator")
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,61 @@
1
+ require "spec_helper"
2
+
3
+ describe Subvalid::Validator do
4
+ let(:stub_validator) {
5
+ -> (object, result) { "testing #{object} " }
6
+ }
7
+ class FooValidator
8
+ include Subvalid::Validator
9
+
10
+ STUB_VALIDATOR = -> (object, result) { result.add_error("testing #{object}") }
11
+
12
+
13
+ validates with: STUB_VALIDATOR
14
+ validates :foo, with: STUB_VALIDATOR
15
+ validates :child do
16
+ validates :boz, with: STUB_VALIDATOR
17
+ end
18
+ end
19
+
20
+ class TestValidator
21
+ def self.validate(object, validation_result=ValidationResult.new, *args)
22
+ validation_result.add_error("testing #{object}")
23
+ end
24
+ end
25
+ Subvalid::ValidatorRegistry.register(:test, TestValidator)
26
+
27
+ Poro = Struct.new(:foo, :bar, :child) do
28
+ def to_s
29
+ "I'M A PORO"
30
+ end
31
+ end
32
+
33
+ PoroChild = Struct.new(:baz, :boz) do
34
+ def to_s
35
+ "I'M A PORO CHILD"
36
+ end
37
+ end
38
+
39
+ let(:poro) { Poro.new("foo", "bar",
40
+ PoroChild.new("baz", "boz")) }
41
+
42
+ describe "#validate" do
43
+ subject { FooValidator.validate(poro) }
44
+ it "returns a validation result" do
45
+ expect(subject).to be_a(Subvalid::ValidationResult)
46
+ end
47
+
48
+ it "validates base object" do
49
+ expect(subject.errors).to eq(["testing I'M A PORO"])
50
+ end
51
+
52
+ it "validates attribute on object" do
53
+ expect(subject[:foo].errors).to eq(["testing foo"])
54
+ end
55
+
56
+ it "validates child" do
57
+ expect(subject[:child][:boz].errors).to eq(["testing boz"])
58
+ end
59
+ end
60
+
61
+ end
@@ -0,0 +1,4 @@
1
+ require "spec_helper"
2
+
3
+ describe Subvalid do
4
+ end
data/subvalid.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "subvalid/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "subvalid"
8
+ spec.version = Subvalid::VERSION
9
+ spec.authors = ["Julian Doherty"]
10
+ spec.email = ["madlep@madlep.com"]
11
+ spec.summary = %q{Subjective validation for Plain Old Ruby Objects}
12
+ spec.description = %q{Subvalid allows you to use a familiar syntax to define validator classes to validate plain objects. Rather than hard coding single validation logic into the objects themselves, different validation logic can be used depending on the context.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "pry"
25
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: subvalid
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Julian Doherty
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Subvalid allows you to use a familiar syntax to define validator classes
70
+ to validate plain objects. Rather than hard coding single validation logic into
71
+ the objects themselves, different validation logic can be used depending on the
72
+ context.
73
+ email:
74
+ - madlep@madlep.com
75
+ executables: []
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - ".gitignore"
80
+ - ".rspec"
81
+ - ".travis.yml"
82
+ - Gemfile
83
+ - LICENSE.txt
84
+ - README.md
85
+ - Rakefile
86
+ - lib/subvalid.rb
87
+ - lib/subvalid/validation_result.rb
88
+ - lib/subvalid/validator.rb
89
+ - lib/subvalid/validator_registry.rb
90
+ - lib/subvalid/validators.rb
91
+ - lib/subvalid/validators/format_validator.rb
92
+ - lib/subvalid/validators/in_validator.rb
93
+ - lib/subvalid/validators/length_validator.rb
94
+ - lib/subvalid/validators/numericality_validator.rb
95
+ - lib/subvalid/validators/presence_validator.rb
96
+ - lib/subvalid/validators/with_validator.rb
97
+ - lib/subvalid/version.rb
98
+ - spec/spec_helper.rb
99
+ - spec/subvalid/validation_result_spec.rb
100
+ - spec/subvalid/validator_registry_spec.rb
101
+ - spec/subvalid/validator_spec.rb
102
+ - spec/subvalid_spec.rb
103
+ - subvalid.gemspec
104
+ homepage: ''
105
+ licenses:
106
+ - MIT
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.4.5
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: Subjective validation for Plain Old Ruby Objects
128
+ test_files:
129
+ - spec/spec_helper.rb
130
+ - spec/subvalid/validation_result_spec.rb
131
+ - spec/subvalid/validator_registry_spec.rb
132
+ - spec/subvalid/validator_spec.rb
133
+ - spec/subvalid_spec.rb