Select Distinct in Rails with Active Record
User.find :all, :select => "DISTINCT occupation"
Programming Ruby on Rails: active record classify constants initialize load models rake Ruby on Rails
by bseanvt
leave a comment
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 => :environment do
models = ActiveRecord::Base.send(:subclasses)
Dir["#{RAILS_ROOT}/app/models/*"].each do |file|
model = File.basename(file, ".*").classify
models << 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()"
Ruby on Rails: active record override password validations
by bseanvt
leave a comment
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 < 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
Ruby on Rails: active record migration model plugins tags
by bseanvt
leave a comment
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 < ApplicationController
def tag_cloud
@tags = Post.tag_counts
end
end
# and in view...
<style>
.css1 { font-size: 1.0em; }
.css2 { font-size: 1.2em; }
.css3 { font-size: 1.4em; }
.css4 { font-size: 1.6em; }
</style>
<% tag_cloud @tags, %w(css1 css2 css3 css4) do |tag, css_class| %>
<%= link_to tag.name, { :action => :tag, :id => tag.name }, :class => css_class %>
<% end %>
*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.
Ruby on Rails: active record array created_at sort_by
by bseanvt
leave a comment
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}
Ruby on Rails: active record console development logging stdout
by bseanvt
leave a comment
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!
Acts_as_versioned Rails Plugin
Versioning models with the acts_as_versioned plugin
cd rails/app ./script/plugin install git://github.com/technoweenie/acts_as_versioned.git ./script/generate model post title:string body:text
In your model
class Post < ActiveRecord::Base acts_as_versioned end
In db/migrate/****_create_posts.rb
def self.up
create_table :posts do |t|
t.string :title
t.text :body
t.timestamps
end
Post.create_versioned_table
end
def self.down
drop_table :posts
Post.drop_versioned_table
end
Migrate your db
rake db:migrate
Usage
p = Post.create :body => "hello world" p.body = "HELLO WORLD" p.save p.versions.size p.versions.last p.revert_to(p.versions.first) p.body # => hello world
*Quick Note If you want to revert to an older version in a controller or something, don't do
@post = @post.revert_to(2)
Revert_to method will return a TrueClass, Boolean type. Instead use
@post.revert_to(2)
This method will update the attributes for you and when you call them you'll get that version.
More information is available here http://ar-versioned.rubyforge.org/ and http://www.urbanhonking.com/ideasfordozens/archives/2006/02/learns_to_use_a_1.html