module Padrino module Helpers module FormHelpers ## # Helpers to generate form errors. # module Errors ## # Constructs list HTML for the errors for a given symbol. # # @overload error_messages_for(*objects, options = {}) # @param [Array] object Splat of objects to display errors for. # @param [Hash] options Error message display options. # @option options [String] :header_tag ("h2") # Used for the header of the error div. # @option options [String] :id ("field-errors") # The id of the error div. # @option options [String] :class ("field-errors") # The class of the error div. # @option options [Array] :object # The object (or array of objects) for which to display errors, # if you need to escape the instance variable convention. # @option options [String] :object_name # The object name to use in the header, or any text that you prefer. # If +:object_name+ is not set, the name of the first object will be used. # @option options [String] :header_message ("X errors prohibited this object from being saved") # The message in the header of the error div. Pass +nil+ or an empty string # to avoid the header message altogether. # @option options [String] :message ("There were problems with the following fields:") # The explanation message after the header message and before # the error list. Pass +nil+ or an empty string to avoid the explanation message # altogether. # # @return [String] The html section with all errors for the specified +objects+ # # @example # error_messages_for :user # def error_messages_for(*objects) options = objects.extract_options!.symbolize_keys objects = objects.map{ |obj| resolve_object(obj) }.compact count = objects.inject(0){ |sum, object| sum + object.errors.count } return ActiveSupport::SafeBuffer.new if count.zero? content_tag(:div, error_contents(objects, count, options), error_html_attributes(options)) end ## # Returns a string containing the error message attached to the # +method+ on the +object+ if one exists. # # @param [Object] object # The object to display the error for. # @param [Symbol] field # The field on the +object+ to display the error for. # @param [Hash] options # The options to control the error display. # @option options [String] :tag ("span") # The tag that encloses the error. # @option options [String] :prepend ("") # The text to prepend before the field error. # @option options [String] :append ("") # The text to append after the field error. # # @example # # => can't be blank # error_message_on :post, :title # error_message_on @post, :title # # # =>
can't be blank
# error_message_on :post, :title, :tag => :id, :class => :custom, :style => "border:1px solid red" # # # =>
This title can't be blank (or it won't work)
# error_message_on :post, :title, :prepend => "This title", :append => "(or it won't work)" # # @return [String] The html display of an error for a particular +object+ and +field+. # # @api public def error_message_on(object, field, options={}) error = Array(resolve_object(object).errors[field]).first return ActiveSupport::SafeBuffer.new unless error options = { :tag => :span, :class => :error }.update(options) tag = options.delete(:tag) error = [options.delete(:prepend), error, options.delete(:append)].compact.join(" ") content_tag(tag, error, options) end private def error_contents(objects, count, options) object_name = options[:object_name] || objects.first.class.to_s.underscore.gsub(/\//, ' ') contents = ActiveSupport::SafeBuffer.new contents << error_header_tag(options, object_name, count) contents << error_body_tag(options) contents << error_list_tag(objects, object_name) end def error_list_tag(objects, object_name) errors = objects.inject({}){ |all,object| all.update(object.errors) } error_messages = errors.inject(ActiveSupport::SafeBuffer.new) do |all, (field, message)| field_name = I18n.t(field, :default => field.to_s.humanize, :scope => [:models, object_name, :attributes]) all << content_tag(:li, "#{field_name} #{message}") end content_tag(:ul, error_messages) end def error_header_tag(options, object_name, count) header_message = options[:header_message] || begin model_name = I18n.t(:name, :default => object_name.humanize, :scope => [:models, object_name], :count => 1) I18n.t :header, :count => count, :model => model_name, :locale => options[:locale], :scope => [:models, :errors, :template] end content_tag(options[:header_tag] || :h2, header_message) if header_message.present? end def error_body_tag(options) body_message = options[:message] || I18n.t(:body, :locale => options[:locale], :scope => [:models, :errors, :template]) content_tag(:p, body_message) if body_message.present? end def error_html_attributes(options) [:id, :class, :style].each_with_object({}) do |key,all| if options.include?(key) value = options[key] all[key] = value unless value.blank? else all[key] = 'field-errors' unless key == :style end end end def resolve_object(object) object.is_a?(Symbol) ? instance_variable_get("@#{object}") : object end end end end end