validate 1.0.3 → 1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,3 @@
1
-
2
-
3
1
  require 'kongo'
4
2
  require 'validate'
5
3
 
@@ -18,6 +16,16 @@ module Kongo
18
16
  model = ::Kongo::Model.new(hash, coll)
19
17
  model.validates?
20
18
  end
19
+
20
+ # Returns any failures on `hash` if it were to be inserted.
21
+ #
22
+ def validation_failures(hash)
23
+ hash = hash.dup
24
+ hash['_id'] = BSON::ObjectId.new unless hash.include?('_id')
25
+ model = ::Kongo::Model.new(hash, coll)
26
+ model.validates?
27
+ model.failures
28
+ end
21
29
  end
22
30
 
23
31
  class Model
@@ -32,7 +32,7 @@ module Validate
32
32
 
33
33
  def method_missing(method, *args, &block)
34
34
  raise NoMethodError.new("No method #{method} to call in the context of a validation block.") unless method.to_s =~ /^validates/
35
- raise NoMethodError.new("Undefined validation method: #{method}...") unless ValidationMethods.respond_to?(method)
35
+ raise NoMethodError.new("Undefined validation method: #{method}...") unless ValidationMethods.new.respond_to?(method)
36
36
  opts = args.pop if args.last.is_a?(::Hash)
37
37
  children = if block
38
38
  BlockParsingContext.parse(&block)
@@ -0,0 +1,34 @@
1
+
2
+ # This is the sillyness required to implement the DSL for the validations file.
3
+ #
4
+ # Metaprogramming woooooo, though!
5
+ #
6
+ module Validate
7
+ class ValidationMethods
8
+
9
+ def self.method_added(name)
10
+ return unless @next_reason
11
+ reason = @next_reason
12
+ @next_reason = nil
13
+
14
+ (@reasons ||= {})[name.to_s] = reason
15
+ end
16
+
17
+ def self.reason(name)
18
+ reason = (@reasons || {})[name.to_s] || "did not validate."
19
+ end
20
+
21
+ private
22
+ def self.fails_because_key(reason = nil, &block)
23
+ raise ArgumentError.new('Must provide either a reason string or block.') unless reason || block
24
+ @next_reason = reason || block
25
+ end
26
+
27
+ class ArgumentFailureBlockScope
28
+ def initialize(obj, field, opts, validator)
29
+ @obj, @field, @opts, @validator = obj, field, opts, validator
30
+ end
31
+ attr_reader :obj, :field, :opts, :validator
32
+ end
33
+ end
34
+ end
@@ -1,4 +1,5 @@
1
1
 
2
+ require 'validate/validations/meta'
2
3
  module Validate
3
4
 
4
5
  # Every validation method has four arguments:
@@ -8,7 +9,7 @@ module Validate
8
9
  # opts: the options for the validation
9
10
  # validator: a Validator object that can be used for children
10
11
  #
11
- module ValidationMethods
12
+ class ValidationMethods
12
13
 
13
14
  # Validate a field by executing a block in the context of the field.
14
15
  #
@@ -16,7 +17,8 @@ module Validate
16
17
  #
17
18
  # validates :type, with: -> { is_a?(String) && self =~ /^a/ }
18
19
  #
19
- def self.validates(obj, field, opts, validator)
20
+ fails_because_key 'failed to match a custom validation.'
21
+ def validates(obj, field, opts, validator)
20
22
  true == obj[field].instance_exec(&opts[:with])
21
23
  end
22
24
 
@@ -26,7 +28,8 @@ module Validate
26
28
  #
27
29
  # validates_presence_of :field
28
30
  #
29
- def self.validates_presence_of(obj, field, opts, validator)
31
+ fails_because_key 'was not present.'
32
+ def validates_presence_of(obj, field, opts, validator)
30
33
  obj.include?(field)
31
34
  end
32
35
 
@@ -34,7 +37,8 @@ module Validate
34
37
  #
35
38
  # validates_type_of :name, is: String
36
39
  #
37
- def self.validates_type_of(obj, field, opts, validator)
40
+ fails_because_key { "was not of type #{opts[:is]}." }
41
+ def validates_type_of(obj, field, opts, validator)
38
42
  obj.include?(field) && obj[field].is_a?(opts[:is])
39
43
  end
40
44
 
@@ -42,7 +46,7 @@ module Validate
42
46
  #
43
47
  # validates_inclusion_of :type, in: %w(paid free)
44
48
  #
45
- def self.validates_inclusion_of(obj, field, opts, validator)
49
+ def validates_inclusion_of(obj, field, opts, validator)
46
50
  opts[:in].include?(obj[field])
47
51
  end
48
52
 
@@ -50,7 +54,7 @@ module Validate
50
54
  #
51
55
  # validates_numericality_of :amount
52
56
  #
53
- def self.validates_numericality_of(obj, field, opts, validator)
57
+ def validates_numericality_of(obj, field, opts, validator)
54
58
  obj[field].is_a?(Numeric)
55
59
  end
56
60
 
@@ -58,7 +62,7 @@ module Validate
58
62
  #
59
63
  # validates_value_of :field, is: 'something'
60
64
  #
61
- def self.validates_value_of(obj, field, opts, validator)
65
+ def validates_value_of(obj, field, opts, validator)
62
66
  obj.include?(field) && obj[field] == opts[:is]
63
67
  end
64
68
 
@@ -69,7 +73,7 @@ module Validate
69
73
  # validates_numericality_of :amount
70
74
  # end
71
75
  #
72
- def self.validates_child_hash(obj, field, opts, validator)
76
+ def validates_child_hash(obj, field, opts, validator)
73
77
  return false unless obj[field].respond_to?(:to_hash)
74
78
  hash = obj[field].to_hash
75
79
  validator.validates?(hash)
@@ -85,7 +89,7 @@ module Validate
85
89
  # validates_type_of :self, is: String
86
90
  # end
87
91
  #
88
- def self.validates_array(obj, field, opts, validator)
92
+ def validates_array(obj, field, opts, validator)
89
93
  return false unless obj[field].respond_to?(:to_a)
90
94
  array = obj[field].to_a
91
95
  array.map do |e|
@@ -97,7 +101,7 @@ module Validate
97
101
  #
98
102
  # validates_regex :field, matches: /^hello/
99
103
  #
100
- def self.validates_regex(obj, field, opts, validator)
104
+ def validates_regex(obj, field, opts, validator)
101
105
  return false unless obj[field].respond_to?(:=~)
102
106
  0 == (obj[field] =~ opts[:matches])
103
107
  end
@@ -10,7 +10,7 @@ module Validate
10
10
  end
11
11
 
12
12
  def validates?(context)
13
- bool = @validations
13
+ @failures = @validations
14
14
  .map do |v|
15
15
  # destructure fields
16
16
  v[:fields].map {|f| v.merge(fields: f) }
@@ -23,16 +23,28 @@ module Validate
23
23
  when_opt = -> { self.to_hash.include?(v[:fields]) } if when_opt == :is_set
24
24
  !when_opt.is_a?(Proc) || context.instance_exec(&when_opt)
25
25
  end
26
+ .map do |v|
27
+ # fetch reasons
28
+ {reason: (v[:opts] || {})[:reason] || ValidationMethods.reason(v[:name])}.merge(v)
29
+ end
26
30
  .map do |v|
27
31
  # lastly, execute validation
28
32
  validator = if v[:validations]
29
33
  Validator.new(v[:validations])
30
34
  end
31
- ValidationMethods.send(v[:name], context.to_hash, v[:fields], v[:opts], validator)
32
- end
33
- .reduce {|a,b| a && b }
34
- # return the result as a boolean
35
- bool.nil? ? true : bool
35
+ success = ValidationMethods.new.send(v[:name], context.to_hash, v[:fields], v[:opts], validator)
36
+ unless success
37
+ reason = v[:reason].is_a?(Proc) ?
38
+ ValidationMethods::ArgumentFailureBlockScope.new(context.to_hash, v[:fields], v[:opts], validator).instance_exec(&v[:reason]) :
39
+ v[:reason]
40
+ {v[:fields] => reason}
41
+ end
42
+ end.select {|v| !v.nil? } # discard successes
43
+ @failures.count == 0
44
+ end
45
+
46
+ def failures
47
+ @failures || []
36
48
  end
37
49
  end
38
50
 
@@ -1,4 +1,4 @@
1
1
 
2
2
  module Validate
3
- VERSION = '1.0.3'
3
+ VERSION = '1.1'
4
4
  end
data/lib/validate.rb CHANGED
@@ -24,7 +24,13 @@ module Validate
24
24
  # May throw a nil NoMethodError if validations are not defined properly.
25
25
  #
26
26
  def validates?
27
- self.class.validations.validates?(self)
27
+ @validator = self.class.validations.dup
28
+ @validator.validates?(self)
29
+ end
30
+
31
+ def failures
32
+ return [] unless @validator
33
+ @validator.failures
28
34
  end
29
35
 
30
36
  end
@@ -40,6 +40,54 @@ describe Validate do
40
40
 
41
41
  end
42
42
 
43
+ context 'single failure' do
44
+
45
+ before do
46
+ class TestClass < BaseTestClass
47
+ validations do
48
+ validates_presence_of 'name'
49
+ end
50
+ end
51
+ end
52
+
53
+ it 'should have no failures before running the test' do
54
+ test = TestClass.new({"not_name" => :hello})
55
+ test.failures.should == []
56
+ end
57
+
58
+ it 'should have the correct failure array' do
59
+ test = TestClass.new({"not_name" => :hello})
60
+ test.validates?.should == false
61
+ test.failures.should == [{'name'=>'was not present.'}]
62
+ end
63
+
64
+ it 'should have an empty failure array when test succeeds' do
65
+ test = TestClass.new({"name" => :hello})
66
+ test.validates?.should == true
67
+ test.failures.should == []
68
+ end
69
+
70
+ end
71
+
72
+ context 'multiple failures' do
73
+
74
+ before do
75
+ class TestClass < BaseTestClass
76
+ validations do
77
+ validates_presence_of 'name', reason: -> { 'MUST BE HERE' } # lambda here helps test two things at once
78
+ validates_type_of :something, is: String
79
+ end
80
+ end
81
+ end
82
+
83
+ it 'should have the correct failure array' do
84
+ test = TestClass.new({"not_name" => :hello})
85
+ test.validates?.should == false
86
+ test.failures.should == [{'name'=>'MUST BE HERE'}, {:something=>'was not of type String.'}]
87
+ end
88
+
89
+ end
90
+
43
91
  context 'multiple fields' do
44
92
 
45
93
  before do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: validate
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: '1.1'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-16 00:00:00.000000000 Z
12
+ date: 2013-04-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -43,6 +43,7 @@ files:
43
43
  - lib/validate/kongo.rb
44
44
  - lib/validate/parser.rb
45
45
  - lib/validate/validations.rb
46
+ - lib/validate/validations/meta.rb
46
47
  - lib/validate/validator.rb
47
48
  - lib/validate/version.rb
48
49
  - spec/validate_spec.rb