Introspecting validates_presence_of

Hello people,

I’d like to detect whether an attribute of a model has
vaildates_presence_of applied to it so I can automatically apply a
mandatory (*) to the field…it doesn’t look easy…any ideas?

Cheers,

Dan W. wrote the following on 27.07.2006 02:28 :

Hello people,

I’d like to detect whether an attribute of a model has
vaildates_presence_of applied to it so I can automatically apply a
mandatory (*) to the field…it doesn’t look easy…any ideas?

Cheers,

My first reflex would be to override
ActiveRecord::Base#validates_presence_of to populate a Hash in the model
to keep track of which attribute is validated or not :

module ActiveRecord
class Base
alias_method :no_trancking_validates_presence_of,
:validates_presence_of
def validates_presence_of(*attr_names)
@attr_presence_validated = Hash.new unless
@attr_presence_validated
attr_names.each {|a| @attr_presence_validated[a]=true}
no_tracking_validates_presence_of(*attr_names)
end
def attr_presence_validated
@attr_presence_validated
end
end
end

Put this in a lib and include it in environment.rb

Then in your view you can test with:
your_model.attr_presence_validated.include?(attr_name)

Lionel.

On Thursday 27 July 2006 02:28, Dan W. wrote:

I’d like to detect whether an attribute of a model has
vaildates_presence_of applied to it so I can automatically apply a
mandatory (*) to the field…it doesn’t look easy…any ideas?

Dan, below is some I’ve been using with a somewhat monkeypatched version
of Rails pre-1.0. I’ve adapted it a bit so that it works with Rails
1.1.4, but haven’t done any testing. All the code is under LGPL. When
time permits, I hope to make this stuff into a plugin.

Michael

[Sorry for the mangled line breaks]

require ‘active_record/reflection’

Based on code by Sebastian K.

See http://dev.rubyonrails.org/ticket/861

module BoilerPlate
module RailsExtensions # :nodoc:
module ValidationReflection # :nodoc:
VALIDATIONS = %w(
validates_acceptance_of
validates_associated
validates_confirmation_of
validates_exclusion_of
validates_format_of
validates_inclusion_of
validates_length_of
validates_numericality_of
validates_presence_of
validates_uniqueness_of
).freeze

  def self.included(base)
    base.extend(ClassMethods)

    for validation_type in VALIDATIONS
      base.module_eval <<-"end_eval"
        class << self

alias_method :#{validation_type}_without_reflection, :#{validation_type}

          def #{validation_type}_with_reflection(*attr_names)
            #{validation_type}_without_reflection(*attr_names)
            configuration = attr_names.last.is_a?(Hash) ?

attr_names.pop : nil
for attr_name in attr_names
write_inheritable_array “validations”, [
ActiveRecord::Reflection::MacroReflection.new(:#{validation_type},
attr_name, configuration, self) ]
end
end

alias_method :#{validation_type}, :#{validation_type}_with_reflection
end
end_eval
end
end

  module ClassMethods

    # Returns an array of MacroReflection objects for all

validations in the class
def reflect_on_all_validations
read_inheritable_attribute(“validations”) || []
end

    # Returns an array of MacroReflection objects for all

validations defined for the field +attr_name+ (expects a symbol)
def reflect_on_validations_for(attr_name)
reflect_on_all_validations.find_all do |reflection|
reflection.name == attr_name
end
end

  end

end

end
end

require ‘active_record/validations’

module BoilerPlate # :nodoc:
module RailsExtensions # :nodoc:
module Validations # :nodoc:
def self.included(base)
base.extend(ClassMethods)
end

  module ClassMethods

    # Validates the presence of attributes and associations
    # where, according to database metadata, the corresponding
    # column must not be +null+.
    # Options:
    # <tt>:exclude</tt> - properties (as symbols) to be excluded
    def validates_presence_of_mandatory_content_columns(options =

{})
columns = mandatory_content_columns - Array(options[:exclude])
validates_presence_of(*columns)
end

    # Validates the lengths of string attributes against the
    # limit defined by their database type.
    # +Nil+ values are explicitly allowed as they are checked
    # with +validates_presence_of+
    # Options:
    # <tt>:exclude</tt> - attributes (as symbols) to be excluded
    def validates_lengths_of_string_attributes(options = {})
      columns = content_columns - Array(options[:exclude])
      columns.select { |col| col.type == :string and

col.limit }.each do |col|
validates_length_of(col.name.to_sym, :maximum =>
col.limit, :allow_nil => true)
end
end

    # Validates all associated objects.
    # +Nil+ values are explicitly allowed as they are checked
    # with +validates_presence_of+
    # Options:
    # <tt>:exclude</tt> - associations (as symbols) to be excluded
    def validates_all_associated(options = {})
      associations = reflect_on_all_associations.map { |a|

a.name } - Array(options[:exclude])
validates_associated(*associations)
end

    protected

    def mandatory_content_columns
      return @mandatory_content_columns if

@mandatory_content_columns
@mandatory_content_columns = []
content_columns.find_all { |col| !col.null }.each do |col|
@mandatory_content_columns << col.name
end
@mandatory_content_columns
end

  end
end

end
end

require ‘cgi’
require ‘action_view/helpers/form_helper’

module BoilerPlate # :nodoc:
module RailsExtensions # :nodoc:

module InstanceTagValidationsSupport
  def self.included(instance_tag)
    instance_tag.class_eval do
      alias_method :tag_without_validation, :tag
      alias_method :content_tag_without_validation, :content_tag
      alias_method :tag, :tag_with_validations
      alias_method :content_tag, :content_tag_with_validations
    end
  end

  def tag_with_validations(name, options = {})
    options = merge_validations!(options)
    tag_without_validation(name, options)
  end

  def content_tag_with_validations(name, content, options = {})
    options = merge_validations!(options)
    content_tag_without_validation(name, content, options)
  end


  private

  def merge_validations!(options)
    if options.delete('ignore_validations')
      return options
    end
    unless method_name && object.class.respond_to?

(:reflect_on_validations_for)
return options
end
validations =
object.class.reflect_on_validations_for(method_name.without_suffix(‘_id’).to_sym)
validations.each do |validation|
case validation.macro
when :validates_presence_of
add_class_name!(options, ‘mandatory’)
when :validates_length_of
if minlength = validation_minlength(validation.options)
add_class_name!(options, encode_validation(‘minlength’,
minlength))
end
if maxlength = validation_maxlength(validation.options)
add_class_name!(options, encode_validation(‘maxlength’,
maxlength))
end
when :validates_numericality_of
add_class_name!(options, ‘numeric’)
when :validates_inclusion_of
values = validation.options[:in]
if values.kind_of?(Range)

FIXME

          RAILS_DEFAULT_LOGGER.debug("Only inclusion validations for

inclusive Ranges are supported") if values.exclude_end?
add_class_name!(options, encode_validation(‘range’,
values.begin, values.end))
else

FIXME

          RAILS_DEFAULT_LOGGER.debug("Only inclusion validations for

inclusive Ranges are supported")
end
when :validates_format_of
add_class_name!(options,
encode_validation(‘format’, validation.options[:with]))
end
end
options
end

  def add_class_name!(options, class_name)
    return options unless class_name
    options.stringify_keys! ### REMOVE when possible
    class_names = options['class']
    if class_names.blank?
      class_names = class_name
    else
      class_names += ' ' + class_name unless class_names.include?

(class_name)
end
options[‘class’] = class_names
options
end

  def encode_validation(name, *values)
    values = values.map {|v|

CGI.escape(v.inspect).gsub(‘+’, ‘%20’)}.join('')
"validate
#{name}_#{values}"
end

  def validation_minlength(options)
    minlength = options[:minimum]
    if minlength.nil?
      if range = options[:within]
        minlength = range.begin
      end
    end
    minlength
  end

  def validation_maxlength(options)
    maxlength = options[:maximum]
    if maxlength.nil?
      if range = options[:within]
        maxlength = range.exclude_end? ? range.end - 1 : range.end
      end
    end
    maxlength
  end

end

end
end

Then, say, in your environment.rb

ActiveRecord::Base.class_eval do
include BoilerPlate::RailsExtensions::ValidationReflection
include BoilerPlate::RailsExtensions::Validations
end

ActionView::Helpers::InstanceTag.class_eval do
include BoilerPlate::RailsExtensions::InstanceTagValidationsSupport
end


Michael S.
mailto:[email protected]
http://www.schuerig.de/michael/

Then in your view you can test with:
your_model.attr_presence_validated.include?(attr_name)

Lionel.

Yeah, I like it. Nice and simple. Thanks Lionel.

Is this really enough? What if you have:

validates_length_of :foo, :minimum => 10

or any other number of validations that may not allow nil but can be
used as
a more restrictive validator for presence of data.

View this message in context:
http://www.nabble.com/Introspecting-validates_presence_of-tf2008440.html#a5526552
Sent from the RubyOnRails Users forum at Nabble.com.

I’d recommend using Google’s new SVN hosting (http://code.google.com/
hosting/) and then posting it at http://www.agilewebdevelopment.com/
plugins/.


Benjamin C.
http://www.bencurtis.com/
http://www.tesly.com/ – Collaborative test case management
http://www.agilewebdevelopment.com/ – Resources for the Rails community

On Thursday 27 July 2006 19:56, Michael S. wrote:

I hope to make this stuff into a plugin.

And so I did. A first, rough version. What I don’t understand is how to
publish it, although I had a look at the RailsPluginPackageTask[*]. For
the time being I just want to upload all that’s needed to my personal
web site where I don’t have SVN support.

Michael

[*]


Michael S.
mailto:[email protected]
http://www.schuerig.de/michael/

Oh, no reason, I assumed since you were asking the question that
either rubyforge or sourceforge weren’t appealing to you.


Benjamin C.
http://www.bencurtis.com/
http://www.tesly.com/ – Collaborative test case management
http://www.agilewebdevelopment.com/ – Resources for the Rails community

On Friday 28 July 2006 18:00, Benjamin C. wrote:

I’d recommend using Google’s new SVN hosting (http://code.google.com/
hosting/) and then posting it at http://www.agilewebdevelopment.com/
plugins/.

Why Google over RubyForge?

Michael


Michael S.
mailto:[email protected]
http://www.schuerig.de/michael/

On Friday 28 July 2006 19:10, Benjamin C. wrote:

Oh, no reason, I assumed since you were asking the question that
either rubyforge or sourceforge weren’t appealing to you.

Ah, that’s been a misunderstanding, then. I just don’t want to go to the
trouble of publishing a plugin on any of these sites right now. I’m
just trying to put it on my own website. I’ve finally figured out what
the index listings ought to look like after reading the source code of
the plugin installer.

Michael


Michael S.
mailto:[email protected]
http://www.schuerig.de/michael/