whyvalidationssuckin96 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. data/.document +5 -0
  2. data/.gitignore +22 -0
  3. data/LICENSE +20 -0
  4. data/README.md +121 -0
  5. data/Rakefile +45 -0
  6. data/VERSION +1 -0
  7. data/doc/ActiveRecord/RecordInvalid.html +258 -0
  8. data/doc/ActiveRecord.html +93 -0
  9. data/doc/FalseClass.html +87 -0
  10. data/doc/NilClass.html +87 -0
  11. data/doc/Numeric.html +87 -0
  12. data/doc/Object.html +79 -0
  13. data/doc/String.html +87 -0
  14. data/doc/TrueClass.html +87 -0
  15. data/doc/WhyValidationsSuckIn96/ActiveRecord/InstanceMethods.html +156 -0
  16. data/doc/WhyValidationsSuckIn96/ActiveRecord.html +192 -0
  17. data/doc/WhyValidationsSuckIn96/AttributeBasedValidation.html +464 -0
  18. data/doc/WhyValidationsSuckIn96/SkippableValidation.html +194 -0
  19. data/doc/WhyValidationsSuckIn96/ValidatesAcceptance.html +254 -0
  20. data/doc/WhyValidationsSuckIn96/ValidatesAssociated.html +250 -0
  21. data/doc/WhyValidationsSuckIn96/ValidatesConfirmation.html +251 -0
  22. data/doc/WhyValidationsSuckIn96/ValidatesExclusion.html +388 -0
  23. data/doc/WhyValidationsSuckIn96/ValidatesFormat.html +387 -0
  24. data/doc/WhyValidationsSuckIn96/ValidatesInclusion.html +388 -0
  25. data/doc/WhyValidationsSuckIn96/ValidatesLength.html +469 -0
  26. data/doc/WhyValidationsSuckIn96/ValidatesNumericality.html +267 -0
  27. data/doc/WhyValidationsSuckIn96/ValidatesPresence.html +244 -0
  28. data/doc/WhyValidationsSuckIn96/ValidatesUniqueness.html +289 -0
  29. data/doc/WhyValidationsSuckIn96/Validation.html +934 -0
  30. data/doc/WhyValidationsSuckIn96/ValidationBuilder.html +391 -0
  31. data/doc/WhyValidationsSuckIn96/ValidationSupport/ClassMethods.html +249 -0
  32. data/doc/WhyValidationsSuckIn96/ValidationSupport/InstanceMethods.html +484 -0
  33. data/doc/WhyValidationsSuckIn96/ValidationSupport.html +168 -0
  34. data/doc/WhyValidationsSuckIn96.html +97 -0
  35. data/doc/_index.html +346 -0
  36. data/doc/class_list.html +293 -0
  37. data/doc/css/common.css +1 -0
  38. data/doc/css/full_list.css +23 -0
  39. data/doc/css/style.css +263 -0
  40. data/doc/file.README.html +173 -0
  41. data/doc/file_list.html +29 -0
  42. data/doc/index.html +173 -0
  43. data/doc/js/app.js +91 -0
  44. data/doc/js/full_list.js +39 -0
  45. data/doc/js/jquery.js +19 -0
  46. data/doc/method_list.html +462 -0
  47. data/doc/top-level-namespace.html +81 -0
  48. data/lib/whyvalidationssuckin96/attribute_based_validation.rb +46 -0
  49. data/lib/whyvalidationssuckin96/constants.rb +3 -0
  50. data/lib/whyvalidationssuckin96/ext/blank.rb +47 -0
  51. data/lib/whyvalidationssuckin96/macros/validates_acceptance.rb +36 -0
  52. data/lib/whyvalidationssuckin96/macros/validates_associated.rb +33 -0
  53. data/lib/whyvalidationssuckin96/macros/validates_confirmation.rb +40 -0
  54. data/lib/whyvalidationssuckin96/macros/validates_exclusion.rb +38 -0
  55. data/lib/whyvalidationssuckin96/macros/validates_format.rb +38 -0
  56. data/lib/whyvalidationssuckin96/macros/validates_inclusion.rb +38 -0
  57. data/lib/whyvalidationssuckin96/macros/validates_length.rb +98 -0
  58. data/lib/whyvalidationssuckin96/macros/validates_numericality.rb +56 -0
  59. data/lib/whyvalidationssuckin96/macros/validates_presence.rb +30 -0
  60. data/lib/whyvalidationssuckin96/macros.rb +9 -0
  61. data/lib/whyvalidationssuckin96/rails/active_record.rb +94 -0
  62. data/lib/whyvalidationssuckin96/rails/macros/validates_uniqueness.rb +87 -0
  63. data/lib/whyvalidationssuckin96/rails/macros.rb +1 -0
  64. data/lib/whyvalidationssuckin96/skippable_validation.rb +59 -0
  65. data/lib/whyvalidationssuckin96/validation.rb +88 -0
  66. data/lib/whyvalidationssuckin96/validation_builder.rb +56 -0
  67. data/lib/whyvalidationssuckin96/validation_support.rb +74 -0
  68. data/lib/whyvalidationssuckin96.rb +4 -0
  69. data/test/attribute_based_validation_test.rb +58 -0
  70. data/test/macros/validates_acceptance_test.rb +64 -0
  71. data/test/macros/validates_associated_test.rb +60 -0
  72. data/test/macros/validates_confirmation_test.rb +63 -0
  73. data/test/macros/validates_exclusion_test.rb +37 -0
  74. data/test/macros/validates_format_test.rb +43 -0
  75. data/test/macros/validates_inclusion_test.rb +37 -0
  76. data/test/macros/validates_length_test.rb +179 -0
  77. data/test/macros/validates_numericality_test.rb +129 -0
  78. data/test/macros/validates_presence_test.rb +31 -0
  79. data/test/rails/active_record_test.rb +187 -0
  80. data/test/rails/active_record_test_helper.rb +90 -0
  81. data/test/rails/macros/validates_uniqueness_test.rb +153 -0
  82. data/test/skippable_validation_test.rb +102 -0
  83. data/test/teststrap.rb +4 -0
  84. data/test/validation_builder_test.rb +62 -0
  85. data/test/validation_support_test.rb +209 -0
  86. data/test/validation_test.rb +101 -0
  87. data/whyvalidationssuckin96.gemspec +153 -0
  88. metadata +189 -0
@@ -0,0 +1,94 @@
1
+ require 'whyvalidationssuckin96'
2
+ require 'active_record'
3
+ require 'active_record/base'
4
+ require 'active_record/callbacks'
5
+ require 'whyvalidationssuckin96/rails/macros'
6
+
7
+ module WhyValidationsSuckIn96
8
+
9
+ module ActiveRecord
10
+ RemovableInstanceMethods = %w[invalid? validate_on_create validate_on_update validate errors]
11
+ RemovableClassMethods = %w[validate validate_on_create validate_on_update validates_format_of validates_each
12
+ validates_inclusion_of validates_size_of validates_confirmation_of validates_exclusion_of
13
+ validates_uniqueness_of validates_associated validates_acceptance_of
14
+ validates_numericality_of validates_presence_of validates_length_of]
15
+
16
+ def self.included(klass_or_mod)
17
+ remove_active_record_validation_related_methods_from(klass_or_mod)
18
+ klass_or_mod.instance_eval do
19
+ include WhyValidationsSuckIn96::ValidationSupport
20
+ include WhyValidationsSuckIn96::ActiveRecord::InstanceMethods
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ # FIXME - holy mother of god is this a nasty method
27
+ def self.remove_active_record_validation_related_methods_from(klass_or_mod)
28
+ method_map = {klass_or_mod => RemovableInstanceMethods, (class<<klass_or_mod;self;end) => RemovableClassMethods}
29
+ method_map.each do |context, removable_methods|
30
+ context.instance_eval do
31
+ removable_methods.each do |removable_method|
32
+ begin
33
+ remove_method removable_method
34
+ rescue => e
35
+ undef_method removable_method
36
+ end
37
+ end # removable_methods.each
38
+ end # context.instance_eval
39
+ end # method_map.each
40
+ end
41
+
42
+ module InstanceMethods
43
+
44
+ def self.included(klass_or_mod)
45
+ klass_or_mod.module_eval do
46
+ alias_method :valid_without_callbacks?, :valid_with_lifecycle_checking?
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def validations_for_current_lifecycle
53
+ validations_for_save + (new_record? ? validations_for_create : validations_for_update)
54
+ end
55
+
56
+ def valid_with_lifecycle_checking?
57
+ validations_for_current_lifecycle.collect do |validation|
58
+ validation.validates?
59
+ end.all?
60
+ end
61
+
62
+ def validations_for_update
63
+ all_validations.select do |validation|
64
+ validation.options[:on] == :update
65
+ end
66
+ end
67
+
68
+ def validations_for_create
69
+ all_validations.select do |validation|
70
+ validation.options[:on] == :create
71
+ end
72
+ end
73
+
74
+ def validations_for_save
75
+ all_validations.select do |validation|
76
+ validation.options[:on].nil? || validation.options[:on] == :save
77
+ end
78
+ end
79
+
80
+ end # InstanceMethods
81
+ end # ActiveRecord
82
+ end # WhyValidationsSuckIn96
83
+
84
+ module ActiveRecord
85
+ class RecordInvalid < ActiveRecordError
86
+ attr_reader :record
87
+ def initialize(record)
88
+ @record = record
89
+ super
90
+ end
91
+ end # RecordInvalid
92
+ end # ActiveRecord
93
+
94
+ ActiveRecord::Base.instance_eval { include WhyValidationsSuckIn96::ActiveRecord }
@@ -0,0 +1,87 @@
1
+ require 'whyvalidationssuckin96/skippable_validation'
2
+ require 'whyvalidationssuckin96/attribute_based_validation'
3
+
4
+ module WhyValidationsSuckIn96
5
+
6
+ # Checks to see if a given attribute is present in any other records in the databaser
7
+ #
8
+ # @example Default usage
9
+ # setup_validations do
10
+ # validates_uniqueness_of :name
11
+ # end
12
+ #
13
+ # @example Check in a case sensitive fashion
14
+ # setup_validations do
15
+ # validates_uniqueness_of :name, :case_sensitive => true
16
+ # end
17
+ #
18
+ # @example Scope uniqueness to another attribute
19
+ # setup_validations do
20
+ # validates_uniqueness_of :name, :scope => :account_id
21
+ # end
22
+ #
23
+ # @example Scope uniqueness to multiple other attributes
24
+ # setup_validations do
25
+ # validates_uniqueness_of :name, :scope => [:account_id, :domain]
26
+ # end
27
+ #
28
+ # @example When used with STI, check only uniqueness of records of the current type
29
+ # setup_validations do
30
+ # validates_uniqueness_of :name, :base_class_scope => false
31
+ # end
32
+ class ValidatesUniqueness < Validation
33
+ DefaultOptions = {:message => "has already been taken", :case_sensitive => false, :base_class_scope => true}
34
+
35
+ include WhyValidationsSuckIn96::SkippableValidation
36
+ include WhyValidationsSuckIn96::AttributeBasedValidation
37
+
38
+ def validate
39
+ super
40
+ results = find_results
41
+ result_ids = results.collect { |r| r[scope_primary_key] }
42
+ if results.empty? || result_ids.include?(validatable[scope_primary_key])
43
+ pass
44
+ elsif !results.empty? && options[:case_sensitive]
45
+ (results.any? { |r| r[attribute].to_s == attribute_value.to_s }) ? fail : pass
46
+ else
47
+ fail
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def find_results
54
+ scope_class.send(:with_exclusive_scope) do
55
+ scope_class.send(:with_scope, :find => {:conditions => find_conditions}) do
56
+ scope_class.find(:all, :conditions => scope_conditions)
57
+ end
58
+ end
59
+ end
60
+
61
+ def scope_columns
62
+ Array(options[:scope])
63
+ end
64
+
65
+ def find_conditions
66
+ ["LOWER(#{scope_class.connection.quote_column_name(attribute.to_s)}) = LOWER(?)", attribute_value]
67
+ end
68
+
69
+ def scope_conditions
70
+ scope_columns.inject({}) do |conds,col|
71
+ conds[col] = validatable[col]
72
+ conds
73
+ end
74
+ end
75
+
76
+ def scope_primary_key
77
+ scope_class.primary_key
78
+ end
79
+
80
+ def scope_class
81
+ options[:base_class_scope] ? validatable.class.base_class : validatable.class
82
+ end
83
+
84
+ end # Validation
85
+
86
+ ValidationBuilder.register_macro :validates_uniqueness_of, WhyValidationsSuckIn96::ValidatesUniqueness
87
+ end # WhyValidationsSuckIn96
@@ -0,0 +1 @@
1
+ require 'whyvalidationssuckin96/rails/macros/validates_uniqueness'
@@ -0,0 +1,59 @@
1
+ module WhyValidationsSuckIn96
2
+ # A mixin to handle specifying :if and :unless options to check before performing validation.
3
+ #
4
+ # @example Validate if a given block returns true
5
+ # setup_validations do
6
+ # validates_associated :tracks, :if => lambda { !validatable.tracks.empty? }
7
+ # end
8
+ #
9
+ # @example Validate if a given method on the validatable object returns true
10
+ # setup_validations do
11
+ # validates_associated :tracks, :if => :allow_validation
12
+ # end
13
+ #
14
+ # def allow_validation
15
+ # false
16
+ # end
17
+ #
18
+ # @example Validate unless a given block returns true
19
+ # setup_validations do
20
+ # validates_associated :tracks, :unless => lambda { validatable.tracks.empty? }
21
+ # end
22
+ #
23
+ # @example Validate unless a given method on the validatable object returns true
24
+ # setup_validations do
25
+ # validates_associated :tracks, :unless => :disallow_validation
26
+ # end
27
+ #
28
+ # def disallow_validation
29
+ # true
30
+ # end
31
+ module SkippableValidation
32
+
33
+ def validate
34
+ skip if skip_validation?
35
+ end
36
+
37
+ private
38
+
39
+ def skip
40
+ throw :validation_done, nil
41
+ end
42
+
43
+ def skip_validation?
44
+ skip_if? || skip_unless?
45
+ end
46
+
47
+ def skip_unless?
48
+ return false unless options.has_key?(:unless)
49
+ check = options[:unless].is_a?(Proc) ? instance_eval(&options[:unless]) : validatable.send(options[:unless])
50
+ check == true
51
+ end
52
+
53
+ def skip_if?
54
+ return false unless options.has_key?(:if)
55
+ check = options[:if].is_a?(Proc) ? instance_eval(&options[:if]) : validatable.send(options[:if])
56
+ check == false
57
+ end
58
+ end # SkippableValidation
59
+ end # WhyValidationsSuckIn96
@@ -0,0 +1,88 @@
1
+ module WhyValidationsSuckIn96
2
+
3
+ # Base class to use when implementing validations.
4
+ class Validation
5
+
6
+ # A hash of default options for the validation to use.
7
+ DefaultOptions = {}
8
+
9
+ # The options the validation was initialized with
10
+ attr_accessor :options
11
+
12
+ # The object the validation is validating
13
+ attr_reader :validatable
14
+
15
+ class << self
16
+ attr_accessor :name
17
+ end
18
+
19
+ # @param [Object] validatable An object to be validated
20
+ # @param [Hash] options The options to set up the validation with
21
+ def initialize(validatable, options = {})
22
+ @validatable = validatable
23
+ @options = self.class::DefaultOptions.merge(options)
24
+ end
25
+
26
+ # Creates a new subclass of this class, used when defining custom validations with a block
27
+ def self.new_subclass(name, def_block)
28
+ Class.new(self) do
29
+ self.name = name.to_sym
30
+ define_method(:validate, &def_block)
31
+ private :validate
32
+
33
+ def inspect
34
+ "#<WhyValidationsSuckIn96::Validation subclass for validating '#{self.class.name}'> #{super}"
35
+ end
36
+ end
37
+ end
38
+
39
+ # Has this validation passed?
40
+ # @return [true, false]
41
+ def passed?
42
+ @passed == true
43
+ end
44
+
45
+ # Has this validation failed?
46
+ # @return [true, false]
47
+ def failed?
48
+ @passed == false
49
+ end
50
+
51
+ # Has this validation run?
52
+ # @return [true, false]
53
+ def has_run?
54
+ @passed != nil
55
+ end
56
+
57
+ # Performs the validation, returning true or false if the validation passes or fails,
58
+ # or nil if the validation will not run.
59
+ # @return [true, false, nil]
60
+ def validates?
61
+ reset
62
+ @passed = catch :validation_done do
63
+ validate
64
+ pass
65
+ end
66
+ end
67
+
68
+ # The failure message for this validation.
69
+ def message
70
+ @options[:message] || "failed validation"
71
+ end
72
+
73
+ private
74
+
75
+ def reset
76
+ @passed = nil
77
+ end
78
+
79
+ def pass
80
+ throw :validation_done, true
81
+ end
82
+
83
+ def fail
84
+ throw :validation_done, false
85
+ end
86
+
87
+ end # Validation
88
+ end # WhyValidationsSuckIn96
@@ -0,0 +1,56 @@
1
+ require 'whyvalidationssuckin96/validation'
2
+
3
+ module WhyValidationsSuckIn96
4
+ class ValidationBuilder
5
+
6
+ # @param [Module, Class] klass_or_mod The Class or Module to add validations to
7
+ # @param [Proc] definition_block The block to evaluate to define validations
8
+ def initialize(klass_or_mod, definition_block)
9
+ @klass_or_mod = klass_or_mod
10
+ @definition_block = definition_block
11
+ end
12
+
13
+ def create_validations!
14
+ instance_eval(&@definition_block)
15
+ end
16
+
17
+ # Registers a macro to be used in setup_validations blocks
18
+ # @param [Symbol] macro_name The name to register this macro with
19
+ # @param [Class] validation_class The class implementing the validation
20
+ def self.register_macro(macro_name, validation_class)
21
+ define_method(macro_name) do |*args|
22
+ attrs, options = extract_options(args)
23
+ attrs.each do |attr|
24
+ add_validation(validation_class, options.merge(:attribute => attr))
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def add_validation(klass, options)
32
+ @klass_or_mod.validation_collection << [klass, options]
33
+ end
34
+
35
+ def validate(validation_name, options = {}, &def_block)
36
+ existing = @klass_or_mod.validation_collection.detect do |validation, opts|
37
+ validation.name == validation_name
38
+ end
39
+
40
+ if existing
41
+ @klass_or_mod.validation_collection.delete(existing)
42
+ end
43
+
44
+ add_validation(new_validation(validation_name, def_block), options)
45
+ end
46
+
47
+ def extract_options(args_array)
48
+ args, opts = args_array.last.is_a?(Hash) ? [args_array[0..-2], args_array.last] : [args_array, {}]
49
+ end
50
+
51
+ def new_validation(validation_name, def_block)
52
+ WhyValidationsSuckIn96::Validation.new_subclass(validation_name, def_block)
53
+ end
54
+
55
+ end # ValidationBuilder
56
+ end # WhyValidationsSuckIn96
@@ -0,0 +1,74 @@
1
+ require 'whyvalidationssuckin96/validation_builder'
2
+
3
+ module WhyValidationsSuckIn96
4
+ module ValidationSupport
5
+
6
+ def self.included(klass_or_mod)
7
+ klass_or_mod.module_eval do
8
+ extend WhyValidationsSuckIn96::ValidationSupport::ClassMethods
9
+ include WhyValidationsSuckIn96::ValidationSupport::InstanceMethods
10
+ end
11
+ end
12
+
13
+ # Instance methods added to any class or module that mixes in ValidationSupport
14
+ module InstanceMethods
15
+
16
+ # Is this object invalid?
17
+ # @return [true, false]
18
+ def invalid?
19
+ !valid?
20
+ end
21
+
22
+ # Is this object valid?
23
+ # @return [true, false]
24
+ def valid?
25
+ all_validations.collect do |validation|
26
+ validation.validates?
27
+ end.all?
28
+ end
29
+
30
+ # An array of instances of failed validations
31
+ # @return [Array]
32
+ def failed_validations
33
+ all_validations.select { |validation| validation.failed? }
34
+ end
35
+
36
+ # An array of instances of passed validations
37
+ # @return [Array]
38
+ def passed_validations
39
+ all_validations.select { |validation| validation.passed? }
40
+ end
41
+
42
+ # An array of instances of all validations for this object
43
+ # @return [Array]
44
+ def all_validations
45
+ @all_validations ||= self.class.validation_collection.collect do |(vc,opts)|
46
+ vc.new(self, opts)
47
+ end
48
+ end
49
+
50
+ end # InstanceMethods
51
+
52
+ module ClassMethods
53
+
54
+ # An array of arrays, the first element of each being the validation subclass that will be instantiated
55
+ # when validation is performed, the last element being the options the validation will be instantiated
56
+ # with.
57
+ # @return [Array]
58
+ def validation_collection
59
+ @validation_collection ||= begin
60
+ ancestor_with_validations = ancestors[1..-1].detect{|anc| anc.respond_to?(:validation_collection) }
61
+ ancestor_with_validations ? ancestor_with_validations.validation_collection.dup : []
62
+ end
63
+ end
64
+
65
+ # Sets up validations for the class or module this module has been mixed into
66
+ def setup_validations(&definition_block)
67
+ self.validation_collection ||= ancestors.detect{|anc| !anc.validation_collection.nil?}.validation_collection.dup
68
+ builder = ValidationBuilder.new(self, definition_block)
69
+ builder.create_validations!
70
+ end
71
+
72
+ end # ClassMethods
73
+ end # ValidationSupport
74
+ end # WhyValidationsSuckIn96
@@ -0,0 +1,4 @@
1
+ require 'whyvalidationssuckin96/constants'
2
+ require 'whyvalidationssuckin96/ext/blank'
3
+ require 'whyvalidationssuckin96/validation_support'
4
+ require 'whyvalidationssuckin96/macros'
@@ -0,0 +1,58 @@
1
+ require 'teststrap'
2
+ require 'whyvalidationssuckin96/attribute_based_validation'
3
+
4
+ context "attribute based validation mixin" do
5
+ context "when mixed into a class" do
6
+
7
+ setup do
8
+ Class.new(WhyValidationsSuckIn96::Validation) do
9
+ include WhyValidationsSuckIn96::SkippableValidation
10
+ include WhyValidationsSuckIn96::AttributeBasedValidation
11
+
12
+ def validate
13
+ super
14
+ pass
15
+ end
16
+
17
+ end # Class.new
18
+ end # setup
19
+
20
+ should "fail if no attribute is specified during construction" do
21
+ topic.new(Object.new)
22
+ end.raises(ArgumentError, "The attribute to validate must be specified as :attribute")
23
+
24
+ should "add an attribute accessor" do
25
+ topic.new(Object.new, :attribute => :foo).attribute
26
+ end.equals(:foo)
27
+
28
+ context "when using :allow_nil" do
29
+
30
+ should "skip validation if the validatable object is #nil?" do
31
+ inst = topic.new(OpenStruct.new(:test => nil), :allow_nil => true, :attribute => :test)
32
+ inst.validates?
33
+ inst.has_run?
34
+ end.equals(false)
35
+
36
+ should "not skip validation of the validatable object is non-#nil?" do
37
+ inst = topic.new(OpenStruct.new(:test => Object.new), :allow_nil => true, :attribute => :test)
38
+ inst.validates?
39
+ inst.has_run?
40
+ end
41
+ end # when using :allow_nil
42
+
43
+ context "when using :allow_blank" do
44
+ should "skip validation if the validatable object is #blank?" do
45
+ inst = topic.new(OpenStruct.new(:test => ""), :allow_blank => true, :attribute => :test)
46
+ inst.validates?
47
+ inst.has_run?
48
+ end.equals(false)
49
+
50
+ should "not skip validation if the validatable object is non-#blank?" do
51
+ inst = topic.new(OpenStruct.new(:test => "bzzt"), :allow_blank => true, :attribute => :test)
52
+ inst.validates?
53
+ inst.has_run?
54
+ end
55
+ end # when using :allow_blank
56
+
57
+ end # when mixed into a class
58
+ end # skippable validation mixin
@@ -0,0 +1,64 @@
1
+ require 'teststrap'
2
+
3
+ context "validates acceptance" do
4
+
5
+ should "add a validation macro" do
6
+ WhyValidationsSuckIn96::ValidationBuilder.instance_methods
7
+ end.includes('validates_acceptance_of')
8
+
9
+ context "with some default options" do
10
+ setup do
11
+ WhyValidationsSuckIn96::ValidatesAcceptance.new(Object.new, :attribute => :accepted)
12
+ end
13
+
14
+ should "have a message accessor with a default message" do
15
+ topic.message
16
+ end.equals("must be accepted")
17
+
18
+ should "default the :allow_nil option to true" do
19
+ topic.options[:allow_nil]
20
+ end
21
+
22
+ should "default the :accept option to allow the string '1'" do
23
+ topic.options[:accept]
24
+ end.equals("1")
25
+ end # with some default options
26
+
27
+ context "validating an object" do
28
+ validatable = OpenStruct.new(:accepted => false)
29
+
30
+ setup do
31
+ WhyValidationsSuckIn96::ValidatesAcceptance.new(validatable, :attribute => :accepted)
32
+ end
33
+
34
+ should "fail if the return value of the given attribute doesnt match the :accept option" do
35
+ validatable.accepted = false
36
+ topic.validates?
37
+ end.equals(false)
38
+
39
+ should "pass if the return value of the given attribute matches the :accept option" do
40
+ validatable.accepted = topic.options[:accept]
41
+ topic.validates?
42
+ end
43
+
44
+ context "with some non-default options" do
45
+ setup do
46
+ WhyValidationsSuckIn96::ValidatesAcceptance.new(validatable, :attribute => :accepted, :accept => :what, :message => "bleh")
47
+ end
48
+
49
+ should "have a different message" do
50
+ topic.message
51
+ end.equals("bleh")
52
+
53
+ should "fail if the return value of the given attribute doesnt match the :accept option" do
54
+ validatable.accepted = false
55
+ topic.validates?
56
+ end.equals(false)
57
+
58
+ should "pass if the return value of the given attribute matches the :accept option" do
59
+ validatable.accepted = :what
60
+ topic.validates?
61
+ end
62
+ end # with some non-default options
63
+ end # validating an object
64
+ end # validates acceptance
@@ -0,0 +1,60 @@
1
+ require 'teststrap'
2
+
3
+ context "validates associated" do
4
+
5
+ should "add a validation macro" do
6
+ WhyValidationsSuckIn96::ValidationBuilder.instance_methods
7
+ end.includes('validates_associated')
8
+
9
+ context "with some default options" do
10
+ setup do
11
+ WhyValidationsSuckIn96::ValidatesAssociated.new(Object.new, :attribute => :things)
12
+ end
13
+
14
+ should "have a message accessor with a default message" do
15
+ topic.message
16
+ end.equals("is invalid")
17
+ end # with some default options
18
+
19
+ context "validating a singular association" do
20
+ associated = OpenStruct.new(:valid? => true)
21
+ validatable = OpenStruct.new(:thing => associated)
22
+
23
+ setup do
24
+ WhyValidationsSuckIn96::ValidatesAssociated.new(validatable, :attribute => :thing)
25
+ end
26
+
27
+ should "be valid if associated object is valid" do
28
+ def associated.valid?; true; end
29
+ topic.validates?
30
+ end
31
+
32
+ should "be invalid if associated object is invalid" do
33
+ def associated.valid?; false; end
34
+ topic.validates?
35
+ end.equals(false)
36
+ end # validating a singular association
37
+
38
+ context "validating a collection association" do
39
+ associated = [OpenStruct.new(:valid? => true), OpenStruct.new(:valid? => false)]
40
+ validatable = OpenStruct.new(:things => associated)
41
+
42
+ setup do
43
+ WhyValidationsSuckIn96::ValidatesAssociated.new(validatable, :attribute => :things)
44
+ end
45
+
46
+ should "be valid if all associated objects are valid" do
47
+ associated.each do |assoc|
48
+ def assoc.valid?; true; end
49
+ end
50
+ topic.validates?
51
+ end
52
+
53
+ should "be invalid if any associated objects are invalid" do
54
+ associated.each do |assoc|
55
+ def assoc.valid?; false; end
56
+ end
57
+ topic.validates?
58
+ end.equals(false)
59
+ end # validating a collection association
60
+ end # validates associated