Rails Find All by Birthday: How to Find Upcoming Birthdays with ActiveRecord

There are a few ways to solve this problem. However, I think the easiest is to cache the day of the year that the user is born on as an integer. If stored alongside the timestamp we can quickly get a list but properly handle the full birthday elsewhere, such as in the view. You don’t want to rely on just the cached day of year because leap year is not accounted for.

The model will need both born_at and birthday columns.

create_table :users do |t|
  t.timestamp :born_at  # full timestamp
  t.integer :birthday 	# just the day of year
end

The user model also needs a callback (before_save) to set and or update the cached birthday column based on the full timestamp. For convenience, a named scope can be added to the model which will let you call User.birthdays.

class User
  # User.birthdays
  scope :birthdays, lambda { where('birthday in(?)', 7.times.map{|i| Time.now.yday + i}) }

  def before_save
  	self.birthday = born_at.yday
  end
end

You could also use the week in year (1 – 52) for the cache. Using the day you can look an arbitrary number of days ahead.

Reusing Scopes (Formerly Named_scope) In Rails 3

You can easily chain scopes together in your models.

class Article < ActiveRecord::Base
scope :ordered, order('position ASC')
scope :published, ordered.where('published = ?', true)
scope :for_homepage, published.limit(3)
end


Article.for_homepage.to_sql
# => SELECT \"articles\".* FROM \"articles\" WHERE (published = 't') ORDER BY position LIMIT 3

Select Distinct in Rails with Active Record

User.find :all, :select => "DISTINCT occupation"

Load All ActiveRecord::Base Model Classes in Rails Application

Here is a simple rake task which will instantiate all of your Active Record models, provided that they are located in the RAILS_ROOT/app/models directory. Interestingly, all plugin models are instantiated by default when you run the task, for instance, if you are using the Acts As Taggable On plugin, you have access to Tag, Tagging without having to include the plugin models directory path to the task.

namespace :load_ar do
    desc "load up all active record models"
    task :models =&gt; :environment do
      models = ActiveRecord::Base.send(:subclasses)
      Dir["#{RAILS_ROOT}/app/models/*"].each do |file|
         model = File.basename(file, ".*").classify
         models &lt;&lt; model unless models.include?(model)
      end
   end
end

If your’re in the console, you can get all the load paths for your Active Record models with the following from the API.

Rails.configuration.load_paths.each do |path|
   p path
end

Rake DB Everything, Dump, Destroy, Create, Load

I’m a big fan of the yaml_db plugin. But I don’t like running rake db:data:load, only to find that my db columns mismatch my model attributes, thus aborting the data import task. To quickly add/remove columns/attributes from a model and rebuild the database with previous db info, I wrote this simple rake task. It saves model records as yaml in db/seandb.yml, and reloads them in the same task but with the db rebuilt. I nest the save method in begin/rescue so that if there are any conflicts the task will continue.

This biggest challenge was to get a list of all the active record models in the Rails app. This problem has been posted a lot and I didn’t find an easy solution. For now, I just look in the app/models directory and ‘classify’ and ‘constantize’ the file name. This will load the model so that I can iterate over all the active record subclasses and call the appropriate Model.find(:all) method. I could maybe do the same w/ all files in the Rails.configuration.load_paths, but my models are, at least for now, under app/models. Plugin models are loaded and available from the rake task if you require the :environment.

It’s as simple as running

rake db:everything
SAVEDB = "#{RAILS_ROOT}/db/seandb.yml"
namespace :db do
   task :everything => [:environment, :spit, :drop, :create, :migrate, :populate] do
     desc "spit out model records as yaml, rebuild the database and repopulate the db"
   end

   task :spit do
    Dir["#{RAILS_ROOT}/app/models/*"].each {|file| (File.basename(file,".*").classify.constantize)}
    File.open(SAVEDB, "w") do |f|
      ActiveRecord::Base.send(:subclasses).each do |model|
        begin
          model.find(:all).each do |record|
              f.write(record.to_yaml)
              print "\twrote #{record.class}\n"
          end
        rescue
        end
      end
    end
   end

   task :populate do
    File.open( SAVEDB ) do |yf|
      YAML.each_document( yf ) do |ydoc|
        begin
          m = ydoc.clone
          m.save
          p "...saved #{m.id}"
        rescue
        end
      end
    end
   end
end

Active Record Find Methods

Active Record find methods for selecting range from http://charlesmaxwood.com/notes-from-reading-activerecordbase/

Student.find(:all, :conditions => { :grade => 9..12 })
return a range
Student.find(:all, :conditions => { :grade => [9,11,12] })
will return an "in()"

Change database column names for form validations in Rails

When you use validations in Rails, db column names are used as ‘keys’ for error messages. This is usually the preferred way to go about it because this maps nicely to the form fields. However, if you use a virtual attribute this may not be the case. For example, I have a ‘password_crypted’ field in my users table that I don’t want my user to see if they fail to complete the field. Instead of returning “Password crypted cannot be blank” I just want to tell them that a password can’t be blank. If you provide a custom ‘:message’ on the validation this won’t replace the column name. The solution is to override the “human_attribute_name” class method and map specific column names to the string you want to use instead.

class User &lt; ActiveRecord::Base
  validates_presence_of :password_crypted
  ATTR_NAMES = {:password_crypted => "Password"}
  def self.human_attribute_name(attr)
     ATTR_NAMES[attr.to_sym] || super
  end
end

I found these resources helpful while I was in search for a solution to this problem.

http://stackoverflow.com/questions/808547/fully-custom-validation-error-message-with-rails

http://henrik.nyh.se/2007/12/change-displayed-column-name-in-rails-validation-messages

Rails Plugin Acts as Taggable on Steriods

You can download it here http://github.com/suitmymind/acts-as-taggable-on-steroids as well as read usage info (which is for the most part reprinted here).

./script/plugin install http://svn.viney.net.nz/things/rails/plugins/acts_as_taggable_on_steroids
./script/generate acts_as_taggable_migration
rake db:migrate

Then in your model

class Post < ActiveRecord::Base
    acts_as_taggable
 end

And usage is as follows

p = Post.find(:first)
p.tag_list # []
p.tag_list = "Funny, Silly"
p.save
p.tag_list # ["Funny", "Silly"]
p.tag_list.add("Great", "Awful")
p.tag_list.remove("Funny")

#to find...
Post.find_tagged_with('Funny, Silly')
Post.find_tagged_with('Funny, Silly', :match_all => true)

To use this in a form and let users enter a comma separated list of tag names…

form_for @post do |f|
f.text_field :tag_list

And to get a tag cloud

  #controller
  class PostController &lt; ApplicationController
    def tag_cloud
      @tags = Post.tag_counts
    end
  end

  # and in view...
  &lt;style&gt;
  .css1 { font-size: 1.0em; }
  .css2 { font-size: 1.2em; }
  .css3 { font-size: 1.4em; }
  .css4 { font-size: 1.6em; }
  &lt;/style&gt;

  &lt;% tag_cloud @tags, %w(css1 css2 css3 css4) do |tag, css_class| %&gt;
    &lt;%= link_to tag.name, { :action =&gt; :tag, :id =&gt; tag.name }, :class =&gt; css_class %&gt;
  &lt;% end %&gt;

*Note. If you have a controller “tags_controller.rb” and the auto generated (if you used ./script/generate) helper file “tags_helper.rb” you’ll need to make sure to copy the contents of the plugin lib module of the same name, into the helper file. You’ll get an error otherwise.

Descending Sort By in Model For Active Record Hash on Created_at attribute

If you have a couple collections from the database and you want to sort it without the help of Active Record, take a look at the sort_by method on Array type. I’ve used this before when I have a couple of collections which are slightly different but I need them in a chronological order.


  @posts_group_a = Post.find :all, :conditions => ["user_id = ?", current_user.id]
  @posts_group_b = Post.find :all, :conditions => ["user_id = ?", friend_user.id]

  #merge the two arrays here
  @posts = @posts_group_a + @posts_group_b

  # notice the "-" is for descending order and the "to_i" casts the date time to an integer (required)
  @posts.sort_by {|post| - post.created_at.to_i}

Output Logger and SQL to the Rails Console in Development Mode

If you want to take a look at the SQL being generated by active record while your using the console, you can either type this into the console when it loads

ActiveRecord::Base.logger = Logger.new(STDOUT)

Or you can add it to your environment so that it’ll be the default behavior
rails_root/config/environments/development.rb

#...
ActiveRecord::Base.logger = Logger.new(STDOUT)

It’s a nice way to keep you away of any expensive queries you may unknowingly be writing!