whyvalidationssuckin96 1.0.0

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.
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