# decribe_model_attribute(model, attribute, valid_vales = [], invalid_values = []) # {{{
#
# Ensures that an instance of class 'model' with the specified attribute is:
#   -Valid for each of the values in the 'valid_values' array.
#   -Invalid for each of the values in the 'invalid_values' array.
#
# Arguments:
#   -The 'model' arg must be a model class.
#   -The 'attribute' arg must be a symbol named after an attribute of the specified model class.
#   -The '*_values' args must be arrays in this format:
#      [ 'Description', 'value' ]
#
# An exception is raised if an argument is invalid.
#
# Example call:
#   valid_username_values = [
#     'if it has a digit',   'foo4bar',
#     "if it has a '_'",     'foo_bar',
#     ]
#   invalid_username_values = [
#     "if it has an '!'",    'foo!bar',
#     "if it has a ':'",     'f:oobar',
#     ]
#   describe_model_attribute User, :username, valid_username_values, invalid_username_values
#
def describe_model_attribute(model, attribute, valid_values = [], invalid_values = []) 
  raise 'The 1st argument (model) must be a Model class'      unless model.is_a?          Class
  raise 'The 2nd argument (attribute) must be a Symbol'       unless attribute.is_a?      Symbol
  raise 'The 3rd argument (valid_values) must be an array'    unless valid_values.is_a?   Array
  raise 'The 4th argument (invalid_values) must be an array'  unless invalid_values.is_a? Array

  unless valid_values.empty?
    describe model, "attribute '#{attribute}' should be valid" do
      # Ensure that each valid value produces a valid instance of 'model' and no error message.
      valid_values.in_groups_of(2) do |description, value|
        it description do
          p = Object.send "build_#{model.to_s.downcase}", {attribute => value}
          p.save
          p.should be_valid
          p.should have(0).error_on(attribute)
          p.destroy
        end 
      end 
    end 
  end 

  unless invalid_values.empty?
    describe model, "attribute '#{attribute}' should be invalid" do
      # Ensure that each invalid value produces an invalid model and related error message.
      invalid_values.in_groups_of(2) do |description, value|
        it description do
          p = Object.send "build_#{model.to_s.downcase}", {attribute => value}
          p.save
          p.should_not be_valid
          p.should have_at_least(1).error_on(attribute)
        end
      end
    end
  end
end # }}}