subvalid 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CODE_OF_CONDUCT.md +15 -0
- data/README.md +115 -6
- data/lib/subvalid/validator.rb +24 -10
- data/lib/subvalid/validator_registry.rb +1 -1
- data/lib/subvalid/version.rb +1 -1
- data/spec/subvalid/validator_registry_spec.rb +1 -1
- data/spec/subvalid/validator_spec.rb +27 -4
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: edca18185857cfbbc93dbb3314d3d09083c5f200
|
4
|
+
data.tar.gz: af80dce164c5388a450adaba42db36d4f414ca73
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9875098c16bcc27ce0acafb0248684536dca09adabfc199c554cd2b4c71d8a7452b4b9b3ecb9e76764e90167cfe98fa01b2fec6d86db6f62839f070b924bf9fb
|
7
|
+
data.tar.gz: e9099ec87e31f154edd3fa4448efe3e2ff7c034cc6538aa77def84fc98d02775850a1782d7ceff0936cc14cf1a438da6c0c8b5d5a57032f5748cc0996549cfa3
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
|
12
|
+
|
13
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
14
|
+
|
15
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/)
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@ you can define it in a separate class - so it's _"subjective"_. (as in **Sub**je
|
|
7
7
|
**valid**ation).
|
8
8
|
|
9
9
|
Subvalid was extracted from a project at [Envato](http://envato.com) which
|
10
|
-
|
10
|
+
requires complex validation logic at each stage of an object's life cycle:
|
11
11
|
- Users upload videos. The videos are validated to make sure an actual
|
12
12
|
video was uploaded (and not someone's university Powerpoint slides), that
|
13
13
|
framerate is good, resolution and codec is acceptable etc. Failure here would
|
@@ -21,6 +21,10 @@ just leave it as _"incomplete"_, and allow the user to come back later and
|
|
21
21
|
complete it. Once this passes, the item is ready, and we submit it for review to
|
22
22
|
our internal review team.
|
23
23
|
|
24
|
+
All these steps are done asynchronously, so we need to capture the errors, save
|
25
|
+
them to a different field on the item, and carry on to report results back to
|
26
|
+
the user.
|
27
|
+
|
24
28
|
While
|
25
29
|
[ActiveModel::Validations](http://api.rubyonrails.org/classes/ActiveModel/Validations.html)
|
26
30
|
is great if you've got simple validation logic, it doesn't cut it for something
|
@@ -54,6 +58,8 @@ anything. A key design goal is to **not** pollute the objects being validated at
|
|
54
58
|
all
|
55
59
|
- Supports nested validation on nested object structures - and nicely handles
|
56
60
|
nested errors.
|
61
|
+
- DSL and API inspired by ActiveModel::Validations - just simplified and more
|
62
|
+
consistent.
|
57
63
|
|
58
64
|
## Development Status [![travis ci build](https://api.travis-ci.org/envato/subvalid.svg)](https://travis-ci.org/envato/subvalid)
|
59
65
|
|
@@ -77,12 +83,115 @@ Or install it yourself as:
|
|
77
83
|
|
78
84
|
## Usage
|
79
85
|
|
80
|
-
|
86
|
+
Say you've got some object:
|
87
|
+
```ruby
|
88
|
+
Person = Struct.new(:name)
|
89
|
+
madlep = Person.new("madlep")
|
90
|
+
```
|
91
|
+
|
92
|
+
You can validate it with Subvalid like this:
|
93
|
+
```ruby
|
94
|
+
require 'subvalid'
|
95
|
+
|
96
|
+
class PersonValidator
|
97
|
+
include Subvalid::Validator
|
98
|
+
|
99
|
+
validates :name, presence: true
|
100
|
+
end
|
101
|
+
|
102
|
+
PersonValidator.validate(madlep).valid? # => true
|
103
|
+
```
|
104
|
+
|
105
|
+
`validate` returns a validation result. You can check if it is `#valid?` or if
|
106
|
+
it has `errors` on an attribute
|
107
|
+
```ruby
|
108
|
+
result = PersonValidator.validate(Person.new(nil))
|
109
|
+
result.valid? # => false
|
110
|
+
result.errors[:name] # => ["is not present"]
|
111
|
+
```
|
112
|
+
|
113
|
+
Of course, because Subvalid only cares about duck-types, and not any particular
|
114
|
+
modelling framework, this validator works equally well with any type of object -
|
115
|
+
so long as it responds to `name`
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
class Person < ActiveRecord::Base
|
119
|
+
end
|
120
|
+
|
121
|
+
madlepAR = Person.create(name: "madlep")
|
122
|
+
|
123
|
+
PersonValidator.validate(madlepAR).valid? # => true
|
124
|
+
```
|
125
|
+
|
126
|
+
And you can validate nested data structures
|
127
|
+
```ruby
|
128
|
+
Video = Struct.new(:title, :length, :author)
|
129
|
+
|
130
|
+
class VideoValidator
|
131
|
+
include Subvalid::Validator
|
132
|
+
|
133
|
+
validates :title, presence: true
|
134
|
+
validates :length, presence: true
|
135
|
+
validates :author do
|
136
|
+
validates :name, presence: true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
invalid_video = Video.new(nil, nil, Person.new(nil))
|
141
|
+
result = VideoValidator.validate(video)
|
142
|
+
result.to_h # => {:title=>{:errors=>["is not present"]}, :length=>{:errors=>["is not present"]}, :author=>{:name=>{:errors=>["is not present"]}}}
|
143
|
+
```
|
144
|
+
|
145
|
+
|
146
|
+
Or you can DRY up your validation code by composing validators together
|
147
|
+
```ruby
|
148
|
+
class VideoValidator
|
149
|
+
include Subvalid::Validator
|
150
|
+
|
151
|
+
validates :title, presence: true
|
152
|
+
validates :length, presence: true
|
153
|
+
validates :author, with: PersonValidator
|
154
|
+
end
|
155
|
+
```
|
156
|
+
|
157
|
+
Validator execution on specific fields can be run or skipped at validation time
|
158
|
+
by passing an `if` validator proc, which decides if the validation should run
|
159
|
+
```ruby
|
160
|
+
class PersonValidator
|
161
|
+
include Subvalid::Validator
|
162
|
+
|
163
|
+
validates :postcode, presence: true, if: -> (person) { person.country == "US" }
|
164
|
+
end
|
165
|
+
```
|
166
|
+
|
167
|
+
## Contact
|
168
|
+
|
169
|
+
- [github project](https://github.com/envato/subvalid)
|
170
|
+
- Bug reports and feature requests are via [github issues](https://github.com/envato/subvalid/issues)
|
171
|
+
|
172
|
+
## Maintainers
|
173
|
+
|
174
|
+
- [Julian Doherty](https://github.com/madlep)
|
175
|
+
|
176
|
+
## License
|
177
|
+
|
178
|
+
`Subvalid` uses MIT license. See
|
179
|
+
[`LICENSE.txt`](https://github.com/envato/subvalid/blob/master/LICENSE.txt) for
|
180
|
+
details.
|
181
|
+
|
182
|
+
## Code of conduct
|
183
|
+
|
184
|
+
We welcome contribution from everyone. Read more about it in
|
185
|
+
[`CODE_OF_CONDUCT.md`](https://github.com/envato/subvalid/blob/master/CODE_OF_CONDUCT.md)
|
81
186
|
|
82
187
|
## Contributing
|
83
188
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
189
|
+
For bug fixes, documentation changes, and small features:
|
190
|
+
|
191
|
+
1. Fork it ( https://github.com/subvalid/subvalid/fork )
|
192
|
+
2. Create your feature branch (git checkout -b my-new-feature)
|
193
|
+
3. Commit your changes (git commit -am 'Add some feature')
|
194
|
+
4. Push to the branch (git push origin my-new-feature)
|
88
195
|
5. Create a new Pull Request
|
196
|
+
|
197
|
+
For larger new features: Do everything as above, but first also make contact with the project maintainers to be sure your change fits with the project direction and you won't be wasting effort going in the wrong direction
|
data/lib/subvalid/validator.rb
CHANGED
@@ -9,6 +9,8 @@ module Subvalid
|
|
9
9
|
|
10
10
|
module DSL
|
11
11
|
|
12
|
+
MODIFIERS = [:if]
|
13
|
+
|
12
14
|
def validates(*attributes, **validators, &block)
|
13
15
|
if validators.empty? && !block
|
14
16
|
raise "no validations or block specified"
|
@@ -20,20 +22,30 @@ module Subvalid
|
|
20
22
|
end
|
21
23
|
|
22
24
|
private
|
23
|
-
ValidatorEntry = Struct.new(:validator, :args)
|
25
|
+
ValidatorEntry = Struct.new(:validator, :modifiers, :args)
|
24
26
|
def validations
|
25
27
|
@validations ||= Hash.new{|vals,attribute| vals[attribute] = [] }
|
26
28
|
end
|
27
29
|
|
28
30
|
def add_validations(attributes, validators, block)
|
31
|
+
modifiers, validators = extract_modifiers(validators)
|
29
32
|
attributes.each do |attribute|
|
30
33
|
validators.each do |validator_key, args|
|
31
34
|
validator = ValidatorRegistry[validator_key]
|
32
|
-
validations[attribute] << ValidatorEntry.new(validator, args)
|
35
|
+
validations[attribute] << ValidatorEntry.new(validator, modifiers, args)
|
33
36
|
end
|
34
|
-
validations[attribute] << ValidatorEntry.new(BlockValidator, block) if block
|
37
|
+
validations[attribute] << ValidatorEntry.new(BlockValidator, modifiers, block) if block
|
35
38
|
end
|
36
39
|
end
|
40
|
+
|
41
|
+
def extract_modifiers(validators)
|
42
|
+
modifiers = MODIFIERS.inject([]){|acc, mod_key|
|
43
|
+
modifier_proc = validators.delete(mod_key)
|
44
|
+
acc << modifier_proc if modifier_proc
|
45
|
+
acc
|
46
|
+
}
|
47
|
+
[modifiers, validators]
|
48
|
+
end
|
37
49
|
end
|
38
50
|
|
39
51
|
module API
|
@@ -46,13 +58,15 @@ module Subvalid
|
|
46
58
|
end
|
47
59
|
|
48
60
|
validators.each do |validator_entry|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
61
|
+
if validator_entry.modifiers.map{|m| m.call(object)}.all?
|
62
|
+
validator = validator_entry.validator
|
63
|
+
if attribute == :base
|
64
|
+
validator.validate(object, attribute_result, *validator_entry.args)
|
65
|
+
elsif object
|
66
|
+
validator.validate(object.send(attribute), attribute_result, *validator_entry.args)
|
67
|
+
else
|
68
|
+
# 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
|
69
|
+
end
|
56
70
|
end
|
57
71
|
end
|
58
72
|
|
data/lib/subvalid/version.rb
CHANGED
@@ -13,7 +13,7 @@ describe Subvalid::ValidatorRegistry do
|
|
13
13
|
|
14
14
|
context "when validator doesn't exist" do
|
15
15
|
it "raises an error" do
|
16
|
-
expect { described_class[:bad_key] }.to raise_error
|
16
|
+
expect { described_class[:bad_key] }.to raise_error(ArgumentError)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -11,7 +11,7 @@ describe Subvalid::Validator do
|
|
11
11
|
|
12
12
|
|
13
13
|
validates with: STUB_VALIDATOR
|
14
|
-
validates :foo, with: STUB_VALIDATOR
|
14
|
+
validates :foo, with: STUB_VALIDATOR, if: -> (record) { record.some_predicate? }
|
15
15
|
validates :child do
|
16
16
|
validates :boz, with: STUB_VALIDATOR
|
17
17
|
end
|
@@ -24,10 +24,14 @@ describe Subvalid::Validator do
|
|
24
24
|
end
|
25
25
|
Subvalid::ValidatorRegistry.register(:test, TestValidator)
|
26
26
|
|
27
|
-
Poro = Struct.new(:foo, :bar, :child) do
|
27
|
+
Poro = Struct.new(:foo, :bar, :child, :some_predicate) do
|
28
28
|
def to_s
|
29
29
|
"I'M A PORO"
|
30
30
|
end
|
31
|
+
|
32
|
+
def some_predicate?
|
33
|
+
some_predicate
|
34
|
+
end
|
31
35
|
end
|
32
36
|
|
33
37
|
PoroChild = Struct.new(:baz, :boz) do
|
@@ -36,8 +40,16 @@ describe Subvalid::Validator do
|
|
36
40
|
end
|
37
41
|
end
|
38
42
|
|
39
|
-
let(:poro) {
|
40
|
-
|
43
|
+
let(:poro) {
|
44
|
+
Poro.new(
|
45
|
+
"foo",
|
46
|
+
"bar",
|
47
|
+
PoroChild.new("baz", "boz"),
|
48
|
+
some_predicate
|
49
|
+
)
|
50
|
+
}
|
51
|
+
|
52
|
+
let(:some_predicate) { true }
|
41
53
|
|
42
54
|
describe "#validate" do
|
43
55
|
subject { FooValidator.validate(poro) }
|
@@ -56,6 +68,17 @@ describe Subvalid::Validator do
|
|
56
68
|
it "validates child" do
|
57
69
|
expect(subject[:child][:boz].errors).to eq(["testing boz"])
|
58
70
|
end
|
71
|
+
|
72
|
+
context "when `if` modifier returns false" do
|
73
|
+
let(:some_predicate) { false }
|
74
|
+
it "doesn't execute validator" do
|
75
|
+
expect(subject[:foo].errors).to eq([])
|
76
|
+
end
|
77
|
+
|
78
|
+
it "executes other validators" do
|
79
|
+
expect(subject[:child][:boz].errors).to eq(["testing boz"])
|
80
|
+
end
|
81
|
+
end
|
59
82
|
end
|
60
83
|
|
61
84
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: subvalid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julian Doherty
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -79,6 +79,7 @@ files:
|
|
79
79
|
- ".gitignore"
|
80
80
|
- ".rspec"
|
81
81
|
- ".travis.yml"
|
82
|
+
- CODE_OF_CONDUCT.md
|
82
83
|
- Gemfile
|
83
84
|
- LICENSE.txt
|
84
85
|
- README.md
|
@@ -121,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
121
122
|
version: '0'
|
122
123
|
requirements: []
|
123
124
|
rubyforge_project:
|
124
|
-
rubygems_version: 2.
|
125
|
+
rubygems_version: 2.5.1
|
125
126
|
signing_key:
|
126
127
|
specification_version: 4
|
127
128
|
summary: Subjective validation for Plain Old Ruby Objects
|