yema 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +70 -0
  7. data/Rakefile +1 -0
  8. data/lib/yema.rb +33 -0
  9. data/lib/yema/error.rb +11 -0
  10. data/lib/yema/exceptions.rb +3 -0
  11. data/lib/yema/registry/rule.rb +26 -0
  12. data/lib/yema/rule.rb +32 -0
  13. data/lib/yema/rule/format.rb +29 -0
  14. data/lib/yema/rule/inclusion.rb +29 -0
  15. data/lib/yema/rule/length.rb +34 -0
  16. data/lib/yema/rule/required.rb +17 -0
  17. data/lib/yema/rule/strong_type.rb +58 -0
  18. data/lib/yema/validations.rb +30 -0
  19. data/lib/yema/validator.rb +25 -0
  20. data/lib/yema/version.rb +3 -0
  21. data/lib/yema/virtus/attribute.rb +48 -0
  22. data/lib/yema/virtus/attribute/array.rb +12 -0
  23. data/lib/yema/virtus/attribute/numeric.rb +12 -0
  24. data/lib/yema/virtus/attribute/string.rb +16 -0
  25. data/lib/yema/virtus/builder.rb +17 -0
  26. data/lib/yema/virtus/builder/format.rb +13 -0
  27. data/lib/yema/virtus/builder/inclusion.rb +13 -0
  28. data/lib/yema/virtus/builder/length.rb +13 -0
  29. data/lib/yema/virtus/builder/required.rb +13 -0
  30. data/lib/yema/virtus/builder/strong_type.rb +18 -0
  31. data/lib/yema/virtus/validations.rb +28 -0
  32. data/spec/integration/rule/format_spec.rb +23 -0
  33. data/spec/integration/rule/inclusion_spec.rb +28 -0
  34. data/spec/integration/rule/length_spec.rb +40 -0
  35. data/spec/integration/rule/required_spec.rb +22 -0
  36. data/spec/integration/rule/strong_type_spec.rb +27 -0
  37. data/spec/integration/virtus/combination_spec.rb +58 -0
  38. data/spec/integration/virtus/format_spec.rb +22 -0
  39. data/spec/integration/virtus/inclusion_spec.rb +26 -0
  40. data/spec/integration/virtus/length_spec.rb +36 -0
  41. data/spec/integration/virtus/required_spec.rb +21 -0
  42. data/spec/integration/virtus/strong_type_spec.rb +40 -0
  43. data/spec/shared/invalid_options.rb +6 -0
  44. data/spec/shared/invalid_resource.rb +6 -0
  45. data/spec/shared/valid_options.rb +6 -0
  46. data/spec/shared/valid_resource.rb +6 -0
  47. data/spec/spec_helper.rb +9 -0
  48. data/spec/unit/rule/format/valid_options_spec.rb +13 -0
  49. data/spec/unit/rule/inclusion/valid_options_spec.rb +15 -0
  50. data/spec/unit/rule/length/valid_options_spec.rb +16 -0
  51. data/spec/unit/rule/required_options_spec.rb +18 -0
  52. data/spec/unit/rule/strong_type/valid_options_spec.rb +13 -0
  53. data/spec/unit/validations/rules_spec.rb +15 -0
  54. data/spec/unit/validations/valid_spec.rb +41 -0
  55. data/spec/unit/validator/errors_spec.rb +26 -0
  56. data/spec/unit/validator/valid_predicate_spec.rb +31 -0
  57. data/spec/unit/virtus/not_supported_type_spec.rb +11 -0
  58. data/yema.gemspec +26 -0
  59. metadata +210 -0
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ bin/
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --order random
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ script: "bundle exec rspec"
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ notifications:
7
+ email:
8
+ - handi_wiguna@yahoo.com
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in yema.gemspec
4
+ gemspec
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,11 @@
1
+ module Yema
2
+ class Error
3
+ include Equalizer.new(:rule, :resource)
4
+
5
+ attr_reader :rule, :resource
6
+
7
+ def initialize(rule, resource)
8
+ @rule, @resource = rule, resource
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module Yema
2
+ class InvalidOptionError < StandardError; end
3
+ end
@@ -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,17 @@
1
+ module Yema
2
+ class Rule
3
+ class Required < self
4
+
5
+ def matches?(value)
6
+ !blank?(value)
7
+ end
8
+
9
+ private
10
+
11
+ def blank?(value)
12
+ value.nil? || (value.respond_to?(:empty?) && value.empty?)
13
+ end
14
+
15
+ end
16
+ end
17
+ 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