yema 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +70 -0
- data/Rakefile +1 -0
- data/lib/yema.rb +33 -0
- data/lib/yema/error.rb +11 -0
- data/lib/yema/exceptions.rb +3 -0
- data/lib/yema/registry/rule.rb +26 -0
- data/lib/yema/rule.rb +32 -0
- data/lib/yema/rule/format.rb +29 -0
- data/lib/yema/rule/inclusion.rb +29 -0
- data/lib/yema/rule/length.rb +34 -0
- data/lib/yema/rule/required.rb +17 -0
- data/lib/yema/rule/strong_type.rb +58 -0
- data/lib/yema/validations.rb +30 -0
- data/lib/yema/validator.rb +25 -0
- data/lib/yema/version.rb +3 -0
- data/lib/yema/virtus/attribute.rb +48 -0
- data/lib/yema/virtus/attribute/array.rb +12 -0
- data/lib/yema/virtus/attribute/numeric.rb +12 -0
- data/lib/yema/virtus/attribute/string.rb +16 -0
- data/lib/yema/virtus/builder.rb +17 -0
- data/lib/yema/virtus/builder/format.rb +13 -0
- data/lib/yema/virtus/builder/inclusion.rb +13 -0
- data/lib/yema/virtus/builder/length.rb +13 -0
- data/lib/yema/virtus/builder/required.rb +13 -0
- data/lib/yema/virtus/builder/strong_type.rb +18 -0
- data/lib/yema/virtus/validations.rb +28 -0
- data/spec/integration/rule/format_spec.rb +23 -0
- data/spec/integration/rule/inclusion_spec.rb +28 -0
- data/spec/integration/rule/length_spec.rb +40 -0
- data/spec/integration/rule/required_spec.rb +22 -0
- data/spec/integration/rule/strong_type_spec.rb +27 -0
- data/spec/integration/virtus/combination_spec.rb +58 -0
- data/spec/integration/virtus/format_spec.rb +22 -0
- data/spec/integration/virtus/inclusion_spec.rb +26 -0
- data/spec/integration/virtus/length_spec.rb +36 -0
- data/spec/integration/virtus/required_spec.rb +21 -0
- data/spec/integration/virtus/strong_type_spec.rb +40 -0
- data/spec/shared/invalid_options.rb +6 -0
- data/spec/shared/invalid_resource.rb +6 -0
- data/spec/shared/valid_options.rb +6 -0
- data/spec/shared/valid_resource.rb +6 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/unit/rule/format/valid_options_spec.rb +13 -0
- data/spec/unit/rule/inclusion/valid_options_spec.rb +15 -0
- data/spec/unit/rule/length/valid_options_spec.rb +16 -0
- data/spec/unit/rule/required_options_spec.rb +18 -0
- data/spec/unit/rule/strong_type/valid_options_spec.rb +13 -0
- data/spec/unit/validations/rules_spec.rb +15 -0
- data/spec/unit/validations/valid_spec.rb +41 -0
- data/spec/unit/validator/errors_spec.rb +26 -0
- data/spec/unit/validator/valid_predicate_spec.rb +31 -0
- data/spec/unit/virtus/not_supported_type_spec.rb +11 -0
- data/yema.gemspec +26 -0
- metadata +210 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 TODO: Write your name
|
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,70 @@
|
|
1
|
+
# Yema
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/handiwiguna/yema.png?branch=master)](https://travis-ci.org/handiwiguna/yema)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/handiwiguna/yema.png)](https://codeclimate.com/github/handiwiguna/yema)
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'yema'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install yema
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
Standalone:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
class User
|
26
|
+
include Yema::Validations
|
27
|
+
attr_accessor :age
|
28
|
+
end
|
29
|
+
|
30
|
+
User.rules.add(Yema::Rule::StrongType, Integer)
|
31
|
+
|
32
|
+
user = User.new
|
33
|
+
user.age = 3
|
34
|
+
user.valid? # => true
|
35
|
+
|
36
|
+
user.age = '3'
|
37
|
+
user.valid? # => true
|
38
|
+
|
39
|
+
user.age = 'abc'
|
40
|
+
user.valid? # => false
|
41
|
+
```
|
42
|
+
|
43
|
+
Virtus Integration:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
class UserParam
|
47
|
+
include Yema::Virtus::Validations
|
48
|
+
attribute :age, Integer, required: true
|
49
|
+
end
|
50
|
+
|
51
|
+
user = UserParam.new(age: 2)
|
52
|
+
user.valid? # => true
|
53
|
+
|
54
|
+
user = UserParam.new(age: '2')
|
55
|
+
user.valid? # => true
|
56
|
+
|
57
|
+
user = UserParam.new(age: 'abc')
|
58
|
+
user.valid? # => false
|
59
|
+
|
60
|
+
user = UserParam.new
|
61
|
+
user.valid? # => false
|
62
|
+
```
|
63
|
+
|
64
|
+
## Contributing
|
65
|
+
|
66
|
+
1. Fork it
|
67
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
68
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
69
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
70
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/yema.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'equalizer'
|
2
|
+
require 'virtus'
|
3
|
+
|
4
|
+
require "yema/version"
|
5
|
+
require 'yema/error'
|
6
|
+
require 'yema/exceptions'
|
7
|
+
require 'yema/validations'
|
8
|
+
require 'yema/validator'
|
9
|
+
require 'yema/registry/rule'
|
10
|
+
|
11
|
+
require 'yema/rule'
|
12
|
+
require 'yema/rule/required'
|
13
|
+
require 'yema/rule/format'
|
14
|
+
require 'yema/rule/length'
|
15
|
+
require 'yema/rule/inclusion'
|
16
|
+
require 'yema/rule/strong_type'
|
17
|
+
|
18
|
+
require 'yema/virtus/validations'
|
19
|
+
require 'yema/virtus/builder'
|
20
|
+
require 'yema/virtus/builder/format'
|
21
|
+
require 'yema/virtus/builder/inclusion'
|
22
|
+
require 'yema/virtus/builder/length'
|
23
|
+
require 'yema/virtus/builder/required'
|
24
|
+
require 'yema/virtus/builder/strong_type'
|
25
|
+
|
26
|
+
require 'yema/virtus/attribute'
|
27
|
+
require 'yema/virtus/attribute/numeric'
|
28
|
+
require 'yema/virtus/attribute/array'
|
29
|
+
require 'yema/virtus/attribute/string'
|
30
|
+
|
31
|
+
module Yema
|
32
|
+
Undefined = Object.new.freeze
|
33
|
+
end
|
data/lib/yema/error.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Yema
|
2
|
+
module Registry
|
3
|
+
class Rule
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
attr_reader :rules
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@rules = Set.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def add(rule)
|
13
|
+
@rules << rule if rule
|
14
|
+
end
|
15
|
+
|
16
|
+
def merge(rule_set)
|
17
|
+
@rules.merge(rule_set)
|
18
|
+
end
|
19
|
+
|
20
|
+
def each
|
21
|
+
return enum_for :each unless block_given?
|
22
|
+
@rules.each{|rule| yield rule}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/yema/rule.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Yema
|
2
|
+
class Rule
|
3
|
+
include Equalizer.new(:attribute_name)
|
4
|
+
|
5
|
+
attr_reader :attribute_name
|
6
|
+
|
7
|
+
def initialize(attribute_name, options={})
|
8
|
+
assert_required_options(options)
|
9
|
+
@attribute_name = attribute_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.required_options
|
13
|
+
[]
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate(resource)
|
17
|
+
Validator.new(self, resource)
|
18
|
+
end
|
19
|
+
|
20
|
+
def errors(resource)
|
21
|
+
validate(resource).errors
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def assert_required_options(options)
|
27
|
+
shortage = self.class.required_options - options.keys
|
28
|
+
raise InvalidOptionError, "Missing options: #{shortage.inspect}" unless shortage.empty?
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Yema
|
2
|
+
class Rule
|
3
|
+
class Format < self
|
4
|
+
|
5
|
+
attr_reader :format
|
6
|
+
|
7
|
+
def initialize(attribute_name, options={})
|
8
|
+
super
|
9
|
+
extract_options(options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.required_options
|
13
|
+
[:format]
|
14
|
+
end
|
15
|
+
|
16
|
+
def matches?(value)
|
17
|
+
!!(format =~ value)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def extract_options(options)
|
23
|
+
@format = options.fetch(:format)
|
24
|
+
raise InvalidOptionError, "Format option only accept Regexp" unless format.kind_of?(Regexp)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Yema
|
2
|
+
class Rule
|
3
|
+
class Inclusion < self
|
4
|
+
|
5
|
+
attr_reader :within
|
6
|
+
|
7
|
+
def initialize(attribute_name, options={})
|
8
|
+
super
|
9
|
+
extract_options(options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.required_options
|
13
|
+
[:within]
|
14
|
+
end
|
15
|
+
|
16
|
+
def matches?(value)
|
17
|
+
within.include?(value)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def extract_options(options)
|
23
|
+
@within = options.fetch(:within)
|
24
|
+
raise InvalidOptionError, "Within option only accept Array or Range" unless [Array, Range].include?(within.class)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Yema
|
2
|
+
class Rule
|
3
|
+
class Length < self
|
4
|
+
|
5
|
+
attr_reader :length
|
6
|
+
|
7
|
+
def initialize(attribute_name, options={})
|
8
|
+
super
|
9
|
+
extract_options(options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.required_options
|
13
|
+
[:length]
|
14
|
+
end
|
15
|
+
|
16
|
+
def matches?(value)
|
17
|
+
raise ArgumentError unless value.respond_to?(:length)
|
18
|
+
|
19
|
+
case length
|
20
|
+
when Range; length.include?(value.length)
|
21
|
+
when Fixnum; length == value.length
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def extract_options(options)
|
28
|
+
@length = options.fetch(:length)
|
29
|
+
raise InvalidOptionError, "Length option only accept Fixnum or Range" unless [Fixnum, Range].include?(length.class)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Yema
|
2
|
+
class Rule
|
3
|
+
class StrongType < self
|
4
|
+
|
5
|
+
attr_reader :type, :strict, :member_type
|
6
|
+
|
7
|
+
def initialize(attribute_name, options={})
|
8
|
+
super
|
9
|
+
extract_options(options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.required_options
|
13
|
+
[:type]
|
14
|
+
end
|
15
|
+
|
16
|
+
def matches?(value)
|
17
|
+
return allow_nil_rule(value) if strict == :allow_nil
|
18
|
+
if type == TrueClass || type == FalseClass
|
19
|
+
boolean_matches?(value)
|
20
|
+
elsif value.kind_of?(Array)
|
21
|
+
array_matches?(value)
|
22
|
+
else
|
23
|
+
value.kind_of?(type)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def allow_nil_rule(value)
|
30
|
+
return true if value.nil?
|
31
|
+
value.kind_of?(type)
|
32
|
+
end
|
33
|
+
|
34
|
+
def extract_options(options)
|
35
|
+
@type = options.fetch(:type)
|
36
|
+
@member_type = options.fetch(:member_type, nil)
|
37
|
+
@strict = options.fetch(:strict, :high)
|
38
|
+
assert_options_value
|
39
|
+
end
|
40
|
+
|
41
|
+
def assert_options_value
|
42
|
+
raise InvalidOptionError, "Strict option only accept [:high, :allow_nil]" unless [:high, :allow_nil].include?(strict)
|
43
|
+
end
|
44
|
+
|
45
|
+
def boolean_matches?(value)
|
46
|
+
value.equal?(true) || value.equal?(false)
|
47
|
+
end
|
48
|
+
|
49
|
+
def array_matches?(value)
|
50
|
+
if member_type
|
51
|
+
value.kind_of?(Array) && value.all? { |v| v.kind_of?(member_type) }
|
52
|
+
else
|
53
|
+
value.kind_of?(Array)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Yema
|
2
|
+
module Validations
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
super
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid?
|
10
|
+
errors.empty?
|
11
|
+
end
|
12
|
+
|
13
|
+
def rules
|
14
|
+
self.class.rules
|
15
|
+
end
|
16
|
+
|
17
|
+
def errors
|
18
|
+
Set.new.tap do |errors|
|
19
|
+
rules.each { |rule| errors.merge(rule.errors(self)) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def rules
|
25
|
+
@rules ||= Registry::Rule.new
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|