whyvalidationssuckin96 1.5.5 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.5.5
1
+ 1.6.0
@@ -1,44 +1,79 @@
1
1
  module WhyValidationsSuckIn96
2
- # A mixin to help handle the most common case of validating a single attribute on an object. This module has a
2
+ # A mixin to help handle the most common case of validating a single attribute on an object. This module has a
3
3
  # dependency on SkippableValidation that will most likely be removed in future releases, but is something to be
4
4
  # aware of currently.
5
5
  module AttributeBasedValidation
6
-
6
+
7
7
  # An initializer for a validation that checks to see if the required options have been passed for
8
8
  # attribute based validation to work as expected.
9
9
  # @param [Object] validatable An object to be validated
10
10
  # @param [Hash] options The options to set up the validation with
11
11
  # @option options [Symbol] :attribute The attribute on the validatable object to validate against
12
+ # @option options [true, false] :array Specifies the attribute is an array of values to validate individually
12
13
  # @option options [true, false] :allow_nil If true, skips validation of the value of the attribute is #nil?
13
14
  # @option options [true, false] :allow_blank If true, skips validation of the value of the attribute is #blank?
15
+ # @option options [true, false] :allow_empty If true, along with :array, skips validation if the array is empty
14
16
  def initialize(validatable, options = {})
15
17
  raise(ArgumentError, "The attribute to validate must be specified as :attribute") unless options[:attribute]
18
+ @pos = 0 if options[:array]
16
19
  super
17
20
  end
18
-
21
+
19
22
  # The attribute to validate against
20
23
  def attribute
21
24
  options[:attribute]
22
25
  end
23
-
26
+
24
27
  # The value of the attribute to validate against
25
28
  def attribute_value
26
- validatable.send(options[:attribute])
29
+ if options[:array]
30
+ validatable.send(options[:attribute])[@pos]
31
+ else
32
+ validatable.send(options[:attribute])
33
+ end
27
34
  end
28
-
35
+
29
36
  # A default validate implementation that skips on #nil?/#blank? attribute values if :allow_nil or :allow_blank
30
37
  # have been set.
31
38
  def validate
32
- skip if skip_on_blank? || skip_on_nil?
39
+ skip if skip_on_empty? || skip_on_blank? || skip_on_nil?
33
40
  super
34
41
  end
35
-
42
+
43
+ # Performs the validation, returning true or false if the validation passes or fails,
44
+ # or nil if the validation will not run.
45
+ # @return [true, false, nil]
46
+ def validates?
47
+ if options[:array]
48
+ return @passed = nil if skip_on_empty?
49
+ reset
50
+ @pos = 0
51
+ statuses = []
52
+ while validating?
53
+ status = super
54
+ @pos += 1
55
+ statuses << status
56
+ end
57
+ @passed = statuses.all?
58
+ else
59
+ super
60
+ end
61
+ end
62
+
36
63
  private
37
64
 
65
+ def validating?
66
+ (@pos + 1) <= Array(validatable.send(options[:attribute])).size
67
+ end
68
+
69
+ def skip_on_empty?
70
+ options[:array] && options[:allow_empty] && Array(validatable.send(options[:attribute])).empty?
71
+ end
72
+
38
73
  def skip_on_nil?
39
74
  options[:allow_nil] && attribute_value.nil?
40
75
  end
41
-
76
+
42
77
  def skip_on_blank?
43
78
  options[:allow_blank] && attribute_value.blank?
44
79
  end
@@ -1,41 +1,41 @@
1
1
  module WhyValidationsSuckIn96
2
-
2
+
3
3
  # Base class to use when implementing validations.
4
4
  class Validation
5
-
5
+
6
6
  # A hash of default options for the validation to use.
7
7
  DefaultOptions = {}
8
-
8
+
9
9
  # The options the validation was initialized with
10
10
  attr_accessor :options
11
-
11
+
12
12
  # The object the validation is validating
13
13
  attr_reader :validatable
14
-
14
+
15
15
  class << self
16
16
  attr_accessor :name
17
17
  end
18
-
18
+
19
19
  # @param [Object] validatable An object to be validated
20
20
  # @param [Hash] options The options to set up the validation with
21
21
  def initialize(validatable, options = {})
22
22
  @validatable = validatable
23
23
  @options = self.class::DefaultOptions.merge(options)
24
24
  end
25
-
25
+
26
26
  # Creates a new subclass of this class, used when defining custom validations with a block
27
27
  def self.new_subclass(name, def_block)
28
28
  Class.new(self) do
29
29
  self.name = name.to_sym
30
30
  define_method(:validate, &def_block)
31
31
  private :validate
32
-
32
+
33
33
  def inspect
34
34
  "#<WhyValidationsSuckIn96::Validation subclass for validating '#{self.class.name}'> #{super}"
35
35
  end
36
36
  end
37
37
  end
38
-
38
+
39
39
  # Has this validation passed?
40
40
  # @return [true, false]
41
41
  def passed?
@@ -47,13 +47,13 @@ module WhyValidationsSuckIn96
47
47
  def failed?
48
48
  @passed == false
49
49
  end
50
-
50
+
51
51
  # Has this validation run?
52
52
  # @return [true, false]
53
53
  def has_run?
54
54
  @passed != nil
55
55
  end
56
-
56
+
57
57
  # Performs the validation, returning true or false if the validation passes or fails,
58
58
  # or nil if the validation will not run.
59
59
  # @return [true, false, nil]
@@ -64,25 +64,25 @@ module WhyValidationsSuckIn96
64
64
  pass
65
65
  end
66
66
  end
67
-
67
+
68
68
  # The failure message for this validation.
69
69
  def message
70
70
  @options[:message] || "failed validation"
71
71
  end
72
-
72
+
73
73
  private
74
-
74
+
75
75
  def reset
76
76
  @passed = nil
77
77
  end
78
-
78
+
79
79
  def pass
80
80
  throw :validation_done, true
81
81
  end
82
-
82
+
83
83
  def fail
84
84
  throw :validation_done, false
85
85
  end
86
-
86
+
87
87
  end # Validation
88
88
  end # WhyValidationsSuckIn96
@@ -3,56 +3,102 @@ require 'whyvalidationssuckin96/attribute_based_validation'
3
3
 
4
4
  context "attribute based validation mixin" do
5
5
  context "when mixed into a class" do
6
-
6
+
7
7
  setup do
8
8
  Class.new(WhyValidationsSuckIn96::Validation) do
9
9
  include WhyValidationsSuckIn96::SkippableValidation
10
10
  include WhyValidationsSuckIn96::AttributeBasedValidation
11
-
11
+
12
12
  def validate
13
13
  super
14
14
  pass
15
15
  end
16
-
16
+
17
17
  end # Class.new
18
18
  end # setup
19
-
19
+
20
20
  should "fail if no attribute is specified during construction" do
21
21
  topic.new(Object.new)
22
22
  end.raises(ArgumentError, "The attribute to validate must be specified as :attribute")
23
-
23
+
24
24
  should "add an attribute accessor" do
25
25
  topic.new(Object.new, :attribute => :foo).attribute
26
26
  end.equals(:foo)
27
-
27
+
28
28
  context "when using :allow_nil" do
29
-
29
+
30
30
  should "skip validation if the validatable object is #nil?" do
31
31
  inst = topic.new(OpenStruct.new(:test => nil), :allow_nil => true, :attribute => :test)
32
32
  inst.validates?
33
33
  inst.has_run?
34
34
  end.equals(false)
35
-
35
+
36
36
  should "not skip validation of the validatable object is non-#nil?" do
37
37
  inst = topic.new(OpenStruct.new(:test => Object.new), :allow_nil => true, :attribute => :test)
38
38
  inst.validates?
39
39
  inst.has_run?
40
40
  end
41
41
  end # when using :allow_nil
42
-
42
+
43
43
  context "when using :allow_blank" do
44
44
  should "skip validation if the validatable object is #blank?" do
45
45
  inst = topic.new(OpenStruct.new(:test => ""), :allow_blank => true, :attribute => :test)
46
46
  inst.validates?
47
47
  inst.has_run?
48
48
  end.equals(false)
49
-
49
+
50
50
  should "not skip validation if the validatable object is non-#blank?" do
51
51
  inst = topic.new(OpenStruct.new(:test => "bzzt"), :allow_blank => true, :attribute => :test)
52
52
  inst.validates?
53
53
  inst.has_run?
54
54
  end
55
55
  end # when using :allow_blank
56
-
56
+
57
+ context "when using :array" do
58
+ setup do
59
+ Class.new(WhyValidationsSuckIn96::Validation) do
60
+ include WhyValidationsSuckIn96::SkippableValidation
61
+ include WhyValidationsSuckIn96::AttributeBasedValidation
62
+
63
+ def validate
64
+ super
65
+ attribute_value.even? ? pass : fail
66
+ end
67
+
68
+ end # Class.new
69
+ end
70
+
71
+ should "fail if any item doesnt validate" do
72
+ inst = topic.new(OpenStruct.new(:test => [2,4,1,6]), :attribute => :test, :array => true)
73
+ inst.validates?
74
+ end.equals(false)
75
+
76
+ should "pass if all items validate" do
77
+ inst = topic.new(OpenStruct.new(:test => [2,4,6]), :attribute => :test, :array => true)
78
+ inst.validates?
79
+ end
80
+
81
+ should "still pass if the collection state changes between validation calls" do
82
+ validatable = OpenStruct.new(:test => [1,2,4,6])
83
+ inst = topic.new(validatable, :allow_empty => true, :attribute => :test, :array => true)
84
+ first_failed = !inst.validates?
85
+ validatable.test = [2,4,6]
86
+ first_failed && inst.validates?
87
+ end
88
+
89
+ context "with :allow_empty" do
90
+ should "skip validation if the array is empty" do
91
+ inst = topic.new(OpenStruct.new(:test => []), :allow_empty => true, :attribute => :test, :array => true)
92
+ inst.validates?
93
+ inst.has_run?
94
+ end.equals(false)
95
+
96
+ should "not skip validation of the validatable object is non-#empty?" do
97
+ inst = topic.new(OpenStruct.new(:test => [0]), :allow_empty => true, :attribute => :test, :array => true)
98
+ inst.validates?
99
+ inst.has_run?
100
+ end
101
+ end # with :allow_empty
102
+ end # when using :array
57
103
  end # when mixed into a class
58
104
  end # skippable validation mixin
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{whyvalidationssuckin96}
8
- s.version = "1.5.5"
8
+ s.version = "1.6.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["gabrielg", "douglasmeyer"]
12
- s.date = %q{2010-03-08}
12
+ s.date = %q{2010-03-09}
13
13
  s.description = %q{A library for setting up model validations, such as in ActiveRecord.}
14
14
  s.email = %q{gabriel.gironda@gmail.com}
15
15
  s.extra_rdoc_files = [
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whyvalidationssuckin96
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.5
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - gabrielg
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2010-03-08 00:00:00 -06:00
13
+ date: 2010-03-09 00:00:00 -06:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency