validate 1.0.3 → 1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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