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.

   1  
   2  require File.dirname(__FILE__) + '/../spec_helper'
   3  
   4  module CommentSpecHelper
   5    def valid_comment_attributes
   6      { :body => 'Some text',
   7        :commentable_type => 'School',
   8        :commentable_id => 1 }
   9    end
  10  end
  11  
  12  describe Comment do
  13  
  14    include CommentSpecHelper
  15  
  16    before(:each) do
  17      @comment = Comment.new
  18    end
  19  
  20    it "should be valid" do
  21      @comment.attributes = valid_comment_attributes
  22      @comment.should be_valid
  23    end
  24    
  25    it "should should not be valid without something to attach to" do
  26      c = valid_comment_attributes
  27      c.delete :commentable_id
  28      c.delete :commentable_type
  29      @comment.attributes = c
  30      @comment.should_not be_valid
  31      @comment.errors.on(:commentable_id).should eql("can't be blank")
  32      @comment.commentable_id = 1
  33      @comment.should_not be_valid
  34      @comment.errors.on(:commentable_type).should eql("can't be blank")
  35      @comment.commentable_type = "School"
  36      @comment.should be_valid
  37    end
  38    
  39    it "should require body" do
  40      @comment.attributes = valid_comment_attributes.except(:body)
  41      @comment.should_not be_valid
  42      @comment.errors.on(:body).should eql("can't be blank")
  43      @comment.body = "Some text"
  44      @comment.should be_valid
  45    end
  46  
  47    it "should relate to commentable" do
  48      Comment.reflect_on_association(:commentable).should_not be_nil
  49    end
  50    
  51    it "should relate to user" do
  52      Comment.reflect_on_association(:user).should_not be_nil
  53    end
  54  end

Refactoring of simple Model test for Rails

   1  
   2  # =============================================================================
   3  # Purpose: Refactored TestCase for simple Rails ActiveRecord::Base and 
   4  #          ActiveForm classes.
   5  # Author:  Manuel Holtgrewe <purestorm at ggnore dot net>
   6  # License: Public Domain
   7  #
   8  # Feel free to flesh out the tests further and make it a more poweful library.
   9  # Drop me a line if you find the module useful or if you created something more
  10  # powerful on top of it.
  11  # =============================================================================
  12  #
  13  # Include this module in your test classes. Then, you define some valid data,
  14  # the model class to test and some "patches" to the valid data to make it invalid.
  15  #
  16  # Sure, this will only work for very simple models but you want to spend as few
  17  # time on those as possible, right?
  18  #
  19  # Assuming you have the following model:
  20  #
  21  #   class MyModel < ActiveRecord::Base
  22  #     validates_length_of :title, :in => 10..100
  23  #     validates_numericality_of :number
  24  #   end
  25  #
  26  # You can then write the following TestCase:
  27  #
  28  #  class MyModelTest < Test::Unit::TestCase
  29  #    include SimpleMethodTests # The basic tests are defined in this module.
  30  #
  31  #    fixtures :my_models
  32  #
  33  #    def setup
  34  #      @model_class = MyModel
  35  #      @valid_data = { :title => 'Abracabra!', :number => 10 }
  36  #      @invalid_patches =
  37  #        [
  38  #          [ :title, nil ], [ :title, 'a' * 9 ], [ :title, 'a' * 101 ],
  39  #          [ :number, nil ], [ :number, 'a' ], [ :title, '123a' ],
  40  #        ]
  41  #    end
  42  #
  43  #    def test_a_nonbasic_test
  44  #      flunk, "Wheee!"
  45  #    end
  46  #
  47  #    # Overwrite a test from the mixin.
  48  #    def test_valid_should_return_false_and_add_errors_with_invalid_data
  49  #      assert true, "Removing this test!"
  50  #    end
  51  #  end
  52  #
  53  # You can both test ActiveRecord::Base and ActiveForm classes (CRUD is not tested
  54  # on ActiveForm classes). If you test ActiveRecord::Base classes then you have to
  55  # provide at least one fixture.
  56  module SimpleModelTests
  57    def test_valid_should_return_true_with_valid_data
  58      # Do not use Model.new(attributes) since some might be marked as protected.
  59      model = @model_class.new
  60      @valid_data.each { |key, value| model.send("#{key}=".to_sym, value) }
  61  
  62      assert model.valid?
  63      assert_equal 0, model.errors.count
  64    end
  65    
  66    def test_valid_should_return_false_and_add_errors_with_invalid_data
  67      @invalid_patches.each do |key, value|
  68        invalid_data = @valid_data.dup
  69        invalid_data[key] = value
  70      
  71        # Do not use Model.new(attributes) since some might be marked as protected.
  72        model = @model_class.new
  73        invalid_data.each { |invalid_key, invalid_value| model.send("#{invalid_key}=".to_sym, invalid_value) }
  74  
  75        assert !model.valid?, "invalid_data[#{key.inspect}] == #{value.inspect}, errors: #{model.errors.full_messages.join('; ')}"
  76        
  77        # If the property we set was a foreign key named NAME_id then the error 
  78        # gets added to the property NAME of the object if the object is an
  79        # ActiveRecord::Base object.
  80        if key.to_s =~ /^(.*)_id$/ and model.respond_to?($1.to_sym) then
  81          assert_not_nil model.errors.on($1.to_sym), "invalid_data[#{$1.to_sym.inspect}] == #{value.inspect}"
  82        else
  83          assert_not_nil model.errors.on(key), "invalid_data[#{key.inspect}] == #{value.inspect}"
  84        end
  85      end
  86    end
  87    
  88    def test_create_should_work_correctly
  89      # We only want to test this if we test an ActiveRecord::Base class.
  90      return if not @model_class.new.respond_to?(:save)
  91      
  92      old_count = @model_class.count
  93      
  94      # Do not use Model.new(attributes) since some might be marked as protected.
  95      model = @model_class.new
  96      @valid_data.each { |key, value| model.send("#{key}=".to_sym, value) }
  97      
  98      assert model.save, "errors: #{model.errors.full_messages.join('; ')}"
  99      assert model.reload
 100      
 101      assert_equal old_count+1, @model_class.count
 102    end
 103    
 104    def test_update_should_work_correctly
 105      # We only want to test update if the model objects have a method "save".
 106      return if not @model_class.new.respond_to?(:update)
 107      
 108      # We assume that there is at least one valid record in the database with
 109      # different data than the @valid_data you specified above.
 110      #
 111      # We order by id to make the result predictable.
 112      model = @model_class.find(:first, :order => 'id ASC')
 113      
 114      @valid_data.each { |key, value| model.send("#{key}=".to_sym, value) }
 115    
 116      assert model.save, "errors: #{model.errors.full_messages.join('; ')}"
 117      assert model.reload
 118      
 119      @valid_data.each do |key, value|
 120        case value
 121        when Float then
 122          assert_in_delta value, model.send(key), 0.01
 123        else
 124          assert_equal value, model.send(key)
 125        end
 126      end
 127    end
 128    
 129    def test_destroy_should_work_correctly
 130      # We only want to test this if we test an ActiveRecord::Base class.
 131      return if not @model_class.new.respond_to?(:destroy)
 132      
 133      old_count = @model_class.count
 134      
 135      model = @model_class.find(:first)
 136      
 137      id = model.id
 138      
 139      model.destroy
 140      
 141      assert_raises(ActiveRecord::RecordNotFound) { @model_class.find(id) }
 142      
 143      assert_equal old_count-1, @model_class.count
 144    end
 145  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:

   1  
   2    validates_presence_of :category, :message => " must be specified" 
   3    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.

   1  
   2  def model_for_table(table_name)
   3    table_name.classify.constantize
   4  end

rails generator syntax

// generate application
   1  
   2  rails projectname


// generate migration.
   1  
   2  ruby script/generate migration migration_name
   3  ruby script/generate migration add_price


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


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


// generate scaffold
   1  
   2  ruby script/generate scaffold model_name controller_name
   3  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.

   1  
   2  desc "Drop then recreate the dev database, migrate up, and load fixtures" 
   3  task :remigrate => :environment do
   4    return unless %w[development test staging].include? RAILS_ENV
   5    ActiveRecord::Base.connection.tables.each { |t| ActiveRecord::Base.connection.drop_table t }
   6    Rake::Task[:migrate].invoke
   7    Rake::Task["db:fixtures:load"].invoke
   8  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!

   1  class CreditCard < ActiveRecord::Base
   2    belongs_to :legal_entity
   3    validates_numericality_of :number, :only_integer => true
   4    validates_presence_of :number, :expiration_date, :card_type
   5    validates_uniqueness_of :number, :scope => 'account_id', :message => 'matches a credit card already on your account'
   6    belongs_to :account
   7  
   8    def validate
   9      errors.add("number", "is not a #{card_type} or is invalid") unless number_valid? && number_matches_type?
  10    end
  11  
  12    def self.card_types
  13      { 'Visa'             => 'visa',
  14        'MasterCard'       => 'mastercard',
  15        'Discover'         => 'discover',
  16        'American Express' => 'amex' }
  17    end
  18  
  19    def readable_card_type
  20      (@@card_types ||= self.class.card_types.invert)[card_type]
  21    end
  22  
  23    def digits
  24      @digits ||= number.gsub(/[^0-9]/, '')
  25    end
  26  
  27    def last_digits
  28      digits.sub(/^([0-9]+)([0-9]{4})$/) { '*' * $1.length + $2 }
  29    end
  30  
  31    protected
  32    def number_valid?
  33      total = 0
  34      digits.reverse.scan(/(\d)(\d){0,1}/) do |ud,ad|
  35        (ad.to_i*2).to_s.each {|d| total = total + d.to_i} if ad
  36        total = total + ud.to_i
  37      end
  38      total % 10 != 0
  39    end
  40  
  41    def number_matches_type?
  42      digit_length = digits.length
  43      case card_type
  44      when 'visa'
  45        [13,16].include?(digit_length) and number[0,1] == "4"
  46      when 'mastercard'
  47        digit_length == 16 and ("51" .. "55").include?(number[0,2])
  48      when 'amex'
  49        digit_length == 15 and %w(34 37).include?(number[0,2])
  50      when 'discover'
  51        digit_length == 16 and number[0,4] == "6011"
  52      end
  53    end
  54  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:
   1  
   2  Something.find(:all, :conditions => β€˜whateverβ€˜).pretty_print


   1  
   2  class Array
   3  
   4    protected
   5  
   6      def columnized_row(fields, sized)
   7        r = []
   8        fields.each_with_index do |f, i|
   9          r << sprintf(β€?%0-#{sized[i]}sβ€œ, f.to_s.gsub(/\n|\r/, β€˜β€™).slice(0, sized[i]))
  10        end
  11        r.join(’ | β€˜)
  12      end
  13  
  14    public
  15  
  16    def columnized(options = {})
  17      sized = {}
  18      self.each do |row|
  19        row.attributes.values.each_with_index do |value, i|
  20          sized[i] = [sized[i].to_i, row.attributes.keys[i].length, value.to_s.length].max
  21          sized[i] = [options[:max_width], sized[i].to_i].min if options[:max_width]
  22        end
  23      end
  24  
  25      table = []
  26      table << header = columnized_row(self.first.attributes.keys, sized)
  27      table << header.gsub(/./, β€˜-β€˜)
  28      self.each { |row| table << columnized_row(row.attributes.values, sized) }
  29      table.join(β€?\nβ€œ)
  30    end
  31  end
  32  
  33  class ActiveRecord::Base
  34    def columnized(options = {})
  35      [*self].columnized(options)
  36    end
  37  end
  38  


To use:
   1  
   2  >> puts Post.find(:all).columnized(:max_width => 10)
   3  updated_at | title      | private | url | thumb      | metadata | movie      | id  | views | content    | user_id | created_at
   4  β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
   5  Wed May 31 | tetwer     | 0       |     |            |          |            | 909 | 0     | video:xyzz | 1       | Wed May 31
   6  Wed May 31 | bbbb       | 0       |     |            |          |            | 1   | 15    | // descrip | 1       | Tue May 23
   7  Wed May 31 | cxzcxzx    | 0       |     |            |          |            | 906 | 19    | // descrip | 1       | Tue May 23
   8  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.

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


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

   1  
   2  def tablify_string( string )
   3    eval( string.to_s.gsub( /_id/, '' ).singularize.split( '_' ).collect { |word| word.capitalize }.join )
   4  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)

   1  
   2  def self.deg2rad(deg)
   3  	(deg * Math::PI / 180)
   4  end
   5  
   6  def self.rad2deg(rad)
   7  	(rad * 180 / Math::PI)
   8  end
   9  
  10  def self.acos(rad)
  11  	Math.atan2(Math.sqrt(1 - rad**2), rad)
  12  end
  13  
  14  def self.distance_in_miles(loc1, loc2)
  15  	lat1 = loc1.latitude
  16  	lon1 = loc1.longitude
  17  	lat2 = loc2.latitude
  18  	lon2 = loc2.longitude
  19  	theta = lon1 - lon2
  20  	
  21  	dist = Math.sin(self.deg2rad(lat1)) * Math.sin(deg2rad(lat2)) 
  22  		+ Math.cos(self.deg2rad(lat1)) * Math.cos(self.deg2rad(lat2)) * Math.cos(deg2rad(theta))
  23  	
  24  	dist = self.rad2deg(self.acos(dist))
  25  	
  26  	(dist * 60 * 1.1515).round #distance in miles
  27  end
  28  
  29  def miles_to(location)
  30  	Location.distance_in_miles(self, location)
  31  end
  32  
  33  def self.locationArea(location, miles)
  34  	radius = miles.to_f
  35  	latR = radius / ((6076 / 5280) * 60)
  36  	lonR = radius / (((Math.cos(location.latitude * Math::PI / 180) * 6076) / 5280) * 60)
  37  	
  38  	{
  39  		:min_latitude => location.latitude - latR,
  40  		:min_longitude => location.longitude - lonR,
  41  		:max_latitude => location.latitude + latR,
  42  		:max_longitude => location.longitude + lonR
  43  	}
  44  end
  45  
  46  def self.location_ids_in_range(location, miles)
  47  	la = Location.locationArea(location, miles)
  48  	Location.find(:all,
  49  		:select => "locations.id",
  50  		:conditions => ["locations.latitude <= ? and locations.latitude >= ? " + \
  51  			" AND locations.longitude >= ? and locations.longitude <= ?",
  52  			la[:max_latitude], la[:min_latitude], la[:min_longitude], la[:max_longitude]
  53  		]
  54  	).collect { |l| l.id }
  55  end
  56  
  57  def self.locations_in_range(location, miles)
  58  	la = Location.locationArea(location, miles)
  59  	Location.find(:all,
  60  		:conditions => ["locations.latitude <= ? and locations.latitude >= ? " + \
  61  			" AND locations.longitude >= ? and locations.longitude <= ?",
  62  			la[:max_latitude], la[:min_latitude], la[:min_longitude], la[:max_longitude]
  63  		]
  64  	)
  65  end
« Newer Snippets
Older Snippets »
Showing 1-10 of 13 total  RSS