Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

« Newer Snippets
Older Snippets »
Showing 1-8 of 8 total  RSS 

Custom validation with rails: words with more than 26 characters

This goes in whatever model you're validating. "Subject" can be any of the symbols in your model and doesn't need the : in front of it.
def validate
	if subject.split.any?{|w| w.length > 26}
		errors.add(:subject, "cannot have words more than 26 consecutive characters")
	end
end

Rspec my validations for models

// This is the plain-vanilla way that I've been using spec for basic model validations.
// There is also a bit at the bottom that checks that I have the right association
// setup.

require File.dirname(__FILE__) + '/../spec_helper'

module CommentSpecHelper
  def valid_comment_attributes
    { :body => 'Some text',
      :commentable_type => 'School',
      :commentable_id => 1 }
  end
end

describe Comment do

  include CommentSpecHelper

  before(:each) do
    @comment = Comment.new
  end

  it "should be valid" do
    @comment.attributes = valid_comment_attributes
    @comment.should be_valid
  end
  
  it "should should not be valid without something to attach to" do
    c = valid_comment_attributes
    c.delete :commentable_id
    c.delete :commentable_type
    @comment.attributes = c
    @comment.should_not be_valid
    @comment.errors.on(:commentable_id).should eql("can't be blank")
    @comment.commentable_id = 1
    @comment.should_not be_valid
    @comment.errors.on(:commentable_type).should eql("can't be blank")
    @comment.commentable_type = "School"
    @comment.should be_valid
  end
  
  it "should require body" do
    @comment.attributes = valid_comment_attributes.except(:body)
    @comment.should_not be_valid
    @comment.errors.on(:body).should eql("can't be blank")
    @comment.body = "Some text"
    @comment.should be_valid
  end

  it "should relate to commentable" do
    Comment.reflect_on_association(:commentable).should_not be_nil
  end
  
  it "should relate to user" do
    Comment.reflect_on_association(:user).should_not be_nil
  end
end

URL/URI Validations in Rails

Here is a quick/basic URI validation library for your Rails app:

require 'net/http'

# Original credits: http://blog.inquirylabs.com/2006/04/13/simple-uri-validation/
# HTTP Codes: http://www.ruby-doc.org/stdlib/libdoc/net/http/rdoc/classes/Net/HTTPResponse.html

class ActiveRecord::Base
  def self.validates_uri_existence_of(*attr_names)
    configuration = { :message => "is not valid or not responding", :on => :save, :with => nil }
    configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
    
    raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp)
    
    validates_each(attr_names, configuration) do |r, a, v|
        if v.to_s =~ configuration[:with] # check RegExp
              begin # check header response
                  case Net::HTTP.get_response(URI.parse(v))
                    when Net::HTTPSuccess then true
                    else r.errors.add(a, configuration[:message]) and false
                  end 
              rescue # Recover on DNS failures.. 
                  r.errors.add(a, configuration[:message]) and false
              end
        else
          r.errors.add(a, configuration[:message]) and false
        end 
    end
  end
end


Save the code above into a file in your 'lib' directory. ex: validates_uri_existence_of.rb. Include the file in your environment.rb (ex: require 'validates_uri_existence_of').

And now you can use the validator in any model by calling:
validates_uri_existence_of :url, :with =>
        /(^$)|(^(http|https)://[a-z0-9] ([-.]{1}[a-z0-9] )*.[a-z]{2,5}(([0-9]{1,5})?/.*)?$)/ix


Above code validates the URI/URL against a regular expression first, and then does a HEAD query to confirm that the page is alive (HTTPSuccess code is returned). You could easily modify the code to extend the functionality as you wish.

More info in this post.

Date Validator

The date_validator.rb snippet adds a validates_dates date validator to Rails that accepts a range of user friendly date formats.

Dates are particularly vexing in a global web environment as there are many acceptable abbreviated and locale dependent formats. Rather than try to support multiple locale dependent formats I side-stepped the issue and choose two unambiguous formats along with abbreviations and date intervals.

These formats coupled with a date picker calendar control result in fairly painless date input.

# This code replumbs a few classes so that ActiveRecord accepts a wider range
# of user friendly date formats. Change Date.parse_date if you need different
# date input formats. To use: drop this file into ./lib, require it into
# your model and then validate dates with the validates_dates validator.
#
# Written by Stuart Rackham <srackham@methods.co.nz>
# Current version tested on Rails 1.0.0
#

# Add date string parser class method to Date class.
#
class Date

  # Parse date string with one of the following formats:
  #
  # * ISO date format: yyyy-mm-dd, for example '2001-12-25'
  # * d[ mmm[ yy[yy]]]: examples: '22', '22 feb', '22 feb 2003',
  #   '22 feb 03', '22 February 2003'
  # * +n[units] or -n[units]: Date from today: examples: '+22', '+22 days',
  #   '+22d', '-4 weeks', '-4w', '-4week', '+6 months', '+6m', '+6month',
  #   '-2 years', '-2y'
  #
  # The string argument is first converted to a string with #to_s.
  # Returns nil if passed nil or an empty string.
  # Raises ArgumentError if string can't be parsed.
  #
  def self.parse_date(string)
    string = string.to_s.strip.downcase
    return nil if string.empty?
    today = Date.today
    if string =~ /^(\d{4})-(\d{2})-(\d{2})$/
      # ISO date format.
      date_array = ParseDate.parsedate(string, true)
      begin
        result = Date.new(date_array[0], date_array[1], date_array[2])
      rescue
        raise ArgumentError
      end
    elsif string =~ /^(\d{1,2})(?:(?:\s+|-)([a-zA-Z]{3,8})(?:(?:\s+|-)(\d{2}(?:\d{2})?))?)?$/
      # 'd mmmm yyyy' format and abbreviations.
      day = $1
      month = $2 || Date::ABBR_MONTHNAMES[today.month]
      year = $3 || today.year
      date_array = ParseDate.parsedate("#{day} #{month} #{year}", true)
      begin
        result = Date.new(date_array[0], date_array[1], date_array[2])
      rescue
        raise ArgumentError
      end
    elsif string =~ /^([+-]\d+)(?:\s*(d|days?|w|weeks?|m|months?|y|years?))?$/
      # Date intervals.
      n = $1.to_i
      units = $2 || 'days'
      case units.first
      when 'd'
        result = today + n
      when 'w'
        result = today + n*7
      when 'm'
        sign = n <=> 0
        month = today.month + sign * (n.abs % 12)
        year = today.year + sign * (n.abs / 12)
        if month <1
          month += 12
          year -= 1
        elsif month > 12
          month -= 12
          year += 1
        end
        result = Date.new(year, month, today.day)
      when 'y'
        result = Date.new(today.year + n, today.month, today.day)
      end
    else
      raise ArgumentError
    end
    result
  end

end

ActiveRecord::Validations::ClassMethods.class_eval do

  # Validates date values, these can be dates or any formats accepted by
  # Date.parse_date.
  # 
  # For example:
  #
  #   class Person < ActiveRecord::Base
  #     require_dependency 'date_validator'
  #     validates_dates :birthday,
  #                     :from => '1 Jan 1920',
  #                     :to => Date.today,
  #                     :allow_nil => true
  #   end
  #
  # Options:
  # * from - Minium allowed date. May be a date or a string recognized
  #   by Date.parse_date.
  # * to - Maxumum allowed date. May be a date or a string recognized
  #   by Date.parse_date.
  # * allow_nil - Attribute may be nil; skip validation.
  #
  def validates_dates(*attr_names)
    configuration =
      { :message => 'is an invalid date ' \
                    '(here are some valid examples: 23, 23 feb, 23 feb 06, ' \
                    '6 feb 2006, 2006-02-23, +6days, +6d, +6, +2w, -6m, +1y)',
        :on => :save,
      }
    configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
    # Don't let validates_each handle allow_nils, it checks the cast value.
    allow_nil = configuration.delete(:allow_nil)
    from = Date.parse_date(configuration.delete(:from))
    to = Date.parse_date(configuration.delete(:to))
    validates_each(attr_names, configuration) do |record, attr_name, value|
      before_cast = record.send("#{attr_name}_before_type_cast")
      next if allow_nil and (before_cast.nil? or before_cast == '')
      begin
        date = Date.parse_date(before_cast)
      rescue
        record.errors.add(attr_name, configuration[:message])
      else
        if from and date < from
          record.errors.add(attr_name,
                            "cannot be less than #{from.strftime('%e-%b-%Y')}")
        end
        if to and date > to
          record.errors.add(attr_name,
                            "cannot be greater than #{to.strftime('%e-%b-%Y')}")
        end
      end
    end
  end

end

# Override default date type cast class method to handle Date.parse_date
# formats(the default implementation returns nil if passed an unrecognized date
# format).
#
class ActiveRecord::ConnectionAdapters::Column
  def self.string_to_date(string)
    return string unless string.is_a?(String)
    Date.parse_date(string) rescue nil
  end
end

Testing Rails Validations

#
# Useful for testing Rails ActiveRecord validations. For more information see:
# http://wiseheartdesign.com/articles/2006/01/16/testing-rails-validations/
#
module ValidationTestHelper
  def assert_valid(field, message, *values)
    __model_check__
    values.each do |value|
      o = __setup_model__(field, value)
      if o.valid?
        assert_block { true }
      else
        messages = [o.errors[field]].flatten
        assert_block("unexpected invalid field <#{o.class}##{field}>, value: <#{value.inspect}>, errors: <#{o.errors[field].inspect}>.") { false }
      end
    end
  end
  
  def assert_invalid(field, message, *values)
    __model_check__
    values.each do |value|
      o = __setup_model__(field, value)
      if o.valid?
        assert_block("field <#{o.class}##{field}> should be invalid for value <#{value.inspect}> with message <#{message.inspect}>") { false }
      else
        messages = [o.errors[field]].flatten
        assert_block("field <#{o.class}##{field}> with value <#{value.inspect}> expected validation error <#{message.inspect}>, but got errors <#{messages.inspect}>") { messages.include?(message) }
      end
    end
  end
  
  def __model_check__
    raise "@model must be assigned in order to use validation assertions" if @model.nil?
    
    o = @model.dup
    raise "@model must be valid before calling a validation assertion, instead @model contained the following errors #{o.errors.instance_variable_get('@errors').inspect}" unless o.valid?
  end
  
  def __setup_model__(field, value)
    o = @model.dup
    attributes = o.instance_variable_get('@attributes')
    o.instance_variable_set('@attributes', attributes.dup)
    o.send("#{field}=", value)
    o
  end
end

validates italian CF

This function validates italian CF (fiscal code) by:
- checking for valid characters
- verifying checksum

def valid_cf?(cf)
  def ascii_displacement(ch)
   (?0..?9) === ch[0] ? (ch[0] - ?0) : (ch[0] - ?A)
  end

  return false if cf !~ %r{^[a-z]{6}\d{2}[abcdehlmprst]\d{2}[a-z]\d{3}[a-z]$}i
  sum = 0
  cf[0..-2].upcase.scan(/(.)(.)?/) do |a, b|
    sum += [ 1, 0, 5, 7, 9, 13, 15, 17, 19, 21, 2, 4, 18, 20,
            11, 3, 6, 8, 12, 14, 16, 10, 22, 25, 24, 23
           ][ascii_displacement(a)]
    sum += ascii_displacement(b) unless b.nil?
  end
  (sum % 26 + ?A) == cf[-1,1].upcase[0]
end

Validations for Non-ActiveRecord Objects

I needed to find a way to use active record validations on non active record items so i went searching and found this blog which is useful for me..
Totally ripped off Peter Donald's blog :P
http://www.realityforge.org/articles/2005/12/02/validations-for-non-activerecord-model-objects

To get this working grab the active_form.rb file and place it in the app/models directory. You can then make your model objects extend ActiveForm and use them like regular ActiveRecord objects.

active_form.rb
# Note ".valid?" method  must occur on object for validates_associated
class ActiveForm
  
  def initialize(attributes = nil)
    if attributes
      attributes.each do |key,value|
        send(key.to_s + '=', value)
      end
    end
    yield self if block_given?
  end

  def [](key)
    instance_variable_get("@#{key}")
  end

  def method_missing( method_id, *args )
    if md = /_before_type_cast$/.match(method_id.to_s)
      attr_name = md.pre_match
      return self[attr_name] if self.respond_to?(attr_name)
    end
    super
  end

protected 
  def raise_not_implemented_error(*params)
    ValidatingModel.raise_not_implemented_error(*params)
  end

  def self.human_attribute_name(attribute_key_name)
    attribute_key_name.humanize
  end

  def new_record?
    true
  end

  # these methods must be defined before include
  alias save raise_not_implemented_error
  alias update_attribute raise_not_implemented_error

public
  include ActiveRecord::Validations

protected 

  # the following methods must be defined after include so that they overide
  # methods previously included
  alias save! raise_not_implemented_error

  class << self
    def raise_not_implemented_error(*params)
      raise NotImplementedError
    end

    alias validates_uniqueness_of raise_not_implemented_error
    alias create! raise_not_implemented_error
    alias validate_on_create raise_not_implemented_error
    alias validate_on_update raise_not_implemented_error
    alias save_with_validation raise_not_implemented_error    
  end
end

require 'dispatcher'
class Dispatcher
  class << self
    if ! method_defined?(:form_original_reset_application!) 
      alias :form_original_reset_application! :reset_application!
      def reset_application!
        form_original_reset_application!
        Dependencies.remove_subclasses_for(ActiveForm) if defined?(ActiveForm)
      end
    end
  end
end

Defining a custom validates in rails

in the model:

class User < ActiveRecord::Base
  require 'validations'

  validates_positive_or_zero :number
  
end


in /lib/validations.rb:

def validates_positive_or_zero(*attr_names)
  configuration = { :message => "Cannot be negative" }
  configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
  validates_each attr_names do |m, a, v| m.errors.add(a, configuration[:message]) if v<0 end
end
« Newer Snippets
Older Snippets »
Showing 1-8 of 8 total  RSS