How can I list DataMapper errors that aren't tied to one property?

230 views Asked by At

DataMapper models allow custom validations of two forms: those specific to a property, and overall object validations. For example:

 # Validates the `name` property with the `check_name` method;
 # any errors will be under `object.errors[:name]`
 validates_with_method :name, method: :check_name

 # Validates the object overall with the `overall_soundness` method;
 # any errors will be under `object.errors[:overall_soundness]`
 validates_with_method :overall_soundness

The second type makes sense for validations that involve multiple properties, but it also presents a problem: displaying errors to the user.

I'd like to display all errors that aren't attached to a particular property at the top of the form's page, but I don't see any easy way to list them.

How can I get a list of non-property-specific errors?

(I'm using DataMapper 1.2.0)

2

There are 2 answers

3
Nathan Long On

A hacky way

I'm hoping there's a more native way than this. I've added this method to my model:

# Validation errors not tied to a specific property. For instance, 
# "end date must be on or before start date" (neither property is 
# wrong individually, but the combination makes the model invalid)
# @return [Array] of error message strings
def general_validation_errors
  general_errors = []

  general_error_keys = errors.keys.reject do |error|
    # Throw away any errors that match property names
    self.send(:properties).map(&:name).include?(error) || error == :error
  end

  general_error_keys.each do |key|
    general_errors << self.errors[key]
  end

  general_errors.flatten
end

At the top of a form, I can do this:

- if @my_model.general_validation_errors.any?
  .errors
    %ul
      - @my_model.general_validation_errors.each do |error_message|
        %li= error_message

Or, a monkey patch to Formtastic to allow f.general_validation_errors would be:

# Let Formtastic forms use f.general_validation_errors to display these (if any)
module Formtastic
  module Helpers
    module ErrorsHelper
      def general_validation_errors
        unless @object.respond_to?(:general_validation_errors)
          raise ArgumentError.new(
            "#{@object.class} doesn't have a general_validation_errors method for Formtastic to call (did you include the module?)"
          )
        end

        if @object.general_validation_errors.any?
          template.content_tag(:div, class: 'errors') do
            template.content_tag(:ul) do
              content = ''
              @object.general_validation_errors.each do |error|
                content << template.content_tag(:li) do
                  error
                end
              end
              content.html_safe
            end
          end
        end
      end
    end
  end
end
0
Tam Borine On

For displaying... you could use flash?

Since you haven't tagged a language I'll just put the Ruby and Sinatra stuff and maybe you can find the DSL equivalent.

flash.now[:errors] in your view with relevant conditional statements

and in route flash[:errors] = User.errors