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-10 of 13 total  RSS 

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

Refactoring of simple Model test for Rails

# =============================================================================
# Purpose: Refactored TestCase for simple Rails ActiveRecord::Base and 
#          ActiveForm classes.
# Author:  Manuel Holtgrewe <purestorm at ggnore dot net>
# License: Public Domain
#
# Feel free to flesh out the tests further and make it a more poweful library.
# Drop me a line if you find the module useful or if you created something more
# powerful on top of it.
# =============================================================================
#
# Include this module in your test classes. Then, you define some valid data,
# the model class to test and some "patches" to the valid data to make it invalid.
#
# Sure, this will only work for very simple models but you want to spend as few
# time on those as possible, right?
#
# Assuming you have the following model:
#
#   class MyModel < ActiveRecord::Base
#     validates_length_of :title, :in => 10..100
#     validates_numericality_of :number
#   end
#
# You can then write the following TestCase:
#
#  class MyModelTest < Test::Unit::TestCase
#    include SimpleMethodTests # The basic tests are defined in this module.
#
#    fixtures :my_models
#
#    def setup
#      @model_class = MyModel
#      @valid_data = { :title => 'Abracabra!', :number => 10 }
#      @invalid_patches =
#        [
#          [ :title, nil ], [ :title, 'a' * 9 ], [ :title, 'a' * 101 ],
#          [ :number, nil ], [ :number, 'a' ], [ :title, '123a' ],
#        ]
#    end
#
#    def test_a_nonbasic_test
#      flunk, "Wheee!"
#    end
#
#    # Overwrite a test from the mixin.
#    def test_valid_should_return_false_and_add_errors_with_invalid_data
#      assert true, "Removing this test!"
#    end
#  end
#
# You can both test ActiveRecord::Base and ActiveForm classes (CRUD is not tested
# on ActiveForm classes). If you test ActiveRecord::Base classes then you have to
# provide at least one fixture.
module SimpleModelTests
  def test_valid_should_return_true_with_valid_data
    # Do not use Model.new(attributes) since some might be marked as protected.
    model = @model_class.new
    @valid_data.each { |key, value| model.send("#{key}=".to_sym, value) }

    assert model.valid?
    assert_equal 0, model.errors.count
  end
  
  def test_valid_should_return_false_and_add_errors_with_invalid_data
    @invalid_patches.each do |key, value|
      invalid_data = @valid_data.dup
      invalid_data[key] = value
    
      # Do not use Model.new(attributes) since some might be marked as protected.
      model = @model_class.new
      invalid_data.each { |invalid_key, invalid_value| model.send("#{invalid_key}=".to_sym, invalid_value) }

      assert !model.valid?, "invalid_data[#{key.inspect}] == #{value.inspect}, errors: #{model.errors.full_messages.join('; ')}"
      
      # If the property we set was a foreign key named NAME_id then the error 
      # gets added to the property NAME of the object if the object is an
      # ActiveRecord::Base object.
      if key.to_s =~ /^(.*)_id$/ and model.respond_to?($1.to_sym) then
        assert_not_nil model.errors.on($1.to_sym), "invalid_data[#{$1.to_sym.inspect}] == #{value.inspect}"
      else
        assert_not_nil model.errors.on(key), "invalid_data[#{key.inspect}] == #{value.inspect}"
      end
    end
  end
  
  def test_create_should_work_correctly
    # We only want to test this if we test an ActiveRecord::Base class.
    return if not @model_class.new.respond_to?(:save)
    
    old_count = @model_class.count
    
    # Do not use Model.new(attributes) since some might be marked as protected.
    model = @model_class.new
    @valid_data.each { |key, value| model.send("#{key}=".to_sym, value) }
    
    assert model.save, "errors: #{model.errors.full_messages.join('; ')}"
    assert model.reload
    
    assert_equal old_count+1, @model_class.count
  end
  
  def test_update_should_work_correctly
    # We only want to test update if the model objects have a method "save".
    return if not @model_class.new.respond_to?(:update)
    
    # We assume that there is at least one valid record in the database with
    # different data than the @valid_data you specified above.
    #
    # We order by id to make the result predictable.
    model = @model_class.find(:first, :order => 'id ASC')
    
    @valid_data.each { |key, value| model.send("#{key}=".to_sym, value) }
  
    assert model.save, "errors: #{model.errors.full_messages.join('; ')}"
    assert model.reload
    
    @valid_data.each do |key, value|
      case value
      when Float then
        assert_in_delta value, model.send(key), 0.01
      else
        assert_equal value, model.send(key)
      end
    end
  end
  
  def test_destroy_should_work_correctly
    # We only want to test this if we test an ActiveRecord::Base class.
    return if not @model_class.new.respond_to?(:destroy)
    
    old_count = @model_class.count
    
    model = @model_class.find(:first)
    
    id = model.id
    
    model.destroy
    
    assert_raises(ActiveRecord::RecordNotFound) { @model_class.find(id) }
    
    assert_equal old_count-1, @model_class.count
  end
end

Simulating a foreign key constraint in ActiveRecord (Ruby on Rails)

Say for example we have products and a business rule that every product must have a category. Before ActiveRecord we would have done this with a NOT NULL foreign key constraint at the database level.

It wasn't entirely obvious (to me) how to achieve this in an ActiveRecord model - you need *both* of the following two lines:

  validates_presence_of :category, :message => " must be specified" 
  validates_associated :category


Hope this helps someone.

Model from Table Name

Get the Rails model based on the table name. Assumes standard table name conventions.

def model_for_table(table_name)
  table_name.classify.constantize
end

rails generator syntax

// generate application
rails projectname


// generate migration.
ruby script/generate migration migration_name
ruby script/generate migration add_price


// generate model
// we can give a list of columns and types
// :string, :text, :integer, :decimal, :float, :date, ...
ruby script/generate model model_name
ruby script/generate model user name:string hashed_password:string salt:string


// generate controller
ruby script/generate controller controller_name method_name(s)
ruby script/generate controller store index


// generate scaffold
ruby script/generate scaffold model_name controller_name
ruby script/generate scaffold product admin

rake remigrate

From http://errtheblog.com/post/3
Drops your database, recreates it, runs all migrations, then loads fixtures. Heroic.

desc "Drop then recreate the dev database, migrate up, and load fixtures" 
task :remigrate => :environment do
  return unless %w[development test staging].include? RAILS_ENV
  ActiveRecord::Base.connection.tables.each { |t| ActiveRecord::Base.connection.drop_table t }
  Rake::Task[:migrate].invoke
  Rake::Task["db:fixtures:load"].invoke
end

Rails Credit Card Model

A great example of a 'Credit Card' model I found on Pastie ( http://pastie.caboo.se/1157 ). There's no name or attribution.. so great work stranger!

class CreditCard < ActiveRecord::Base
  belongs_to :legal_entity
  validates_numericality_of :number, :only_integer => true
  validates_presence_of :number, :expiration_date, :card_type
  validates_uniqueness_of :number, :scope => 'account_id', :message => 'matches a credit card already on your account'
  belongs_to :account

  def validate
    errors.add("number", "is not a #{card_type} or is invalid") unless number_valid? && number_matches_type?
  end

  def self.card_types
    { 'Visa'             => 'visa',
      'MasterCard'       => 'mastercard',
      'Discover'         => 'discover',
      'American Express' => 'amex' }
  end

  def readable_card_type
    (@@card_types ||= self.class.card_types.invert)[card_type]
  end

  def digits
    @digits ||= number.gsub(/[^0-9]/, '')
  end

  def last_digits
    digits.sub(/^([0-9]+)([0-9]{4})$/) { '*' * $1.length + $2 }
  end

  protected
  def number_valid?
    total = 0
    digits.reverse.scan(/(\d)(\d){0,1}/) do |ud,ad|
      (ad.to_i*2).to_s.each {|d| total = total + d.to_i} if ad
      total = total + ud.to_i
    end
    total % 10 != 0
  end

  def number_matches_type?
    digit_length = digits.length
    case card_type
    when 'visa'
      [13,16].include?(digit_length) and number[0,1] == "4"
    when 'mastercard'
      digit_length == 16 and ("51" .. "55").include?(number[0,2])
    when 'amex'
      digit_length == 15 and %w(34 37).include?(number[0,2])
    when 'discover'
      digit_length == 16 and number[0,4] == "6011"
    end
  end
end

pretty tables for rails models

From http://www.rubyinside.com/columnized-text-datasets-in-rails-71.html:
Inspired by: http://blog.caboo.se/articles/2006/06/10/pretty-tables-for-ruby-objects

It gives a MySQL-command-line-client style textual view of data stored in your Rails database. The syntax worked like this:
Something.find(:all, :conditions => β€˜whateverβ€˜).pretty_print


class Array

  protected

    def columnized_row(fields, sized)
      r = []
      fields.each_with_index do |f, i|
        r << sprintf(β€?%0-#{sized[i]}sβ€œ, f.to_s.gsub(/\n|\r/, β€˜β€™).slice(0, sized[i]))
      end
      r.join(’ | β€˜)
    end

  public

  def columnized(options = {})
    sized = {}
    self.each do |row|
      row.attributes.values.each_with_index do |value, i|
        sized[i] = [sized[i].to_i, row.attributes.keys[i].length, value.to_s.length].max
        sized[i] = [options[:max_width], sized[i].to_i].min if options[:max_width]
      end
    end

    table = []
    table << header = columnized_row(self.first.attributes.keys, sized)
    table << header.gsub(/./, β€˜-β€˜)
    self.each { |row| table << columnized_row(row.attributes.values, sized) }
    table.join(β€?\nβ€œ)
  end
end

class ActiveRecord::Base
  def columnized(options = {})
    [*self].columnized(options)
  end
end



To use:
>> puts Post.find(:all).columnized(:max_width => 10)
updated_at | title      | private | url | thumb      | metadata | movie      | id  | views | content    | user_id | created_at
β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
Wed May 31 | tetwer     | 0       |     |            |          |            | 909 | 0     | video:xyzz | 1       | Wed May 31
Wed May 31 | bbbb       | 0       |     |            |          |            | 1   | 15    | // descrip | 1       | Tue May 23
Wed May 31 | cxzcxzx    | 0       |     |            |          |            | 906 | 19    | // descrip | 1       | Tue May 23
Wed May 31 | jklklkl;   | 0       |     |            |          |            | 907 | 35    | // descrip | 1       | Tue May 23


If you want to use it with your project, put the code into lib/columnized.rb, use require β€˜columnized’, and you’re ready to roll. Unlike courtenay’s version, mine only supports max_width, but I didn’t consider changing the column separator too important.

go from model to associated table name and back

Given a table object, it returns the related string object; e.g. SubAttribute => 'sub-attribute'. Useful if you want to make a list of all your tables with perhaps their fields listed out to the side.

def stringify_table( table, replace_char = '-', pluralize = false )
  string = table.to_s.gsub( /([A-Za-z])([A-Z])/, '\1' << replace_char.to_s << '\2' )
  string = string.pluralize if pluralize
  string
end


Given a string akin to the name of a table, it returns the related table object; e.g. 'sub_attributes' => SubAttribute.

def tablify_string( string )
  eval( string.to_s.gsub( /_id/, '' ).singularize.split( '_' ).collect { |word| word.capitalize }.join )
end

Location queries for a model

// These are class functions which are useful for finding locations in a database
// when PostGIS is not available (when using MySQL for example)

def self.deg2rad(deg)
	(deg * Math::PI / 180)
end

def self.rad2deg(rad)
	(rad * 180 / Math::PI)
end

def self.acos(rad)
	Math.atan2(Math.sqrt(1 - rad**2), rad)
end

def self.distance_in_miles(loc1, loc2)
	lat1 = loc1.latitude
	lon1 = loc1.longitude
	lat2 = loc2.latitude
	lon2 = loc2.longitude
	theta = lon1 - lon2
	
	dist = Math.sin(self.deg2rad(lat1)) * Math.sin(deg2rad(lat2)) 
		+ Math.cos(self.deg2rad(lat1)) * Math.cos(self.deg2rad(lat2)) * Math.cos(deg2rad(theta))
	
	dist = self.rad2deg(self.acos(dist))
	
	(dist * 60 * 1.1515).round #distance in miles
end

def miles_to(location)
	Location.distance_in_miles(self, location)
end

def self.locationArea(location, miles)
	radius = miles.to_f
	latR = radius / ((6076 / 5280) * 60)
	lonR = radius / (((Math.cos(location.latitude * Math::PI / 180) * 6076) / 5280) * 60)
	
	{
		:min_latitude => location.latitude - latR,
		:min_longitude => location.longitude - lonR,
		:max_latitude => location.latitude + latR,
		:max_longitude => location.longitude + lonR
	}
end

def self.location_ids_in_range(location, miles)
	la = Location.locationArea(location, miles)
	Location.find(:all,
		:select => "locations.id",
		:conditions => ["locations.latitude <= ? and locations.latitude >= ? " + \
			" AND locations.longitude >= ? and locations.longitude <= ?",
			la[:max_latitude], la[:min_latitude], la[:min_longitude], la[:max_longitude]
		]
	).collect { |l| l.id }
end

def self.locations_in_range(location, miles)
	la = Location.locationArea(location, miles)
	Location.find(:all,
		:conditions => ["locations.latitude <= ? and locations.latitude >= ? " + \
			" AND locations.longitude >= ? and locations.longitude <= ?",
			la[:max_latitude], la[:min_latitude], la[:min_longitude], la[:max_longitude]
		]
	)
end
« Newer Snippets
Older Snippets »
Showing 1-10 of 13 total  RSS