Active Record tips and tricks
Published over 4 years ago
Just a small collection of tips/tricks which I use a lot ( or try to ), that others might find helpful.
In most of the Rails applications that I work with, the primary model ( User model for example ) ends up being at least 1000 lines long. Thanks to Rick’s quick/awesome solution, we can easily split a model into different “concerns”.
RAILS_ROOT/config/initializers/concerns.rb
class << ActiveRecord::Base def concerned_with(*concerns) concerns.each do |concern| require_dependency "#{name.underscore}/#{concern}" end end end
class << ActiveRecord::Base
def concerned_with(*concerns)
concerns.each do |concern|
require_dependency "#{name.underscore}/#{concern}"
end
end
end
Using concerned_with, lets split the User model into 2 different concerns and 3 different files :
RAILS_ROOT/app/models/user.rb
class User < ActiveRecord::Base concerned_with :validations, :authentication end
class User < ActiveRecord::Base
concerned_with :validations, :authentication
end
RAILS_ROOT/app/models/user/validations.rb
class User < ActiveRecord::Base validates_presence_of :name end
class User < ActiveRecord::Base
validates_presence_of :name
end
RAILS_ROOT/app/models/user/authentication.rb
class User < ActiveRecord::Base def self.authenticate(name, password) find_by_name_and_password(name, password) end end
class User < ActiveRecord::Base
def self.authenticate(name, password)
find_by_name_and_password(name, password)
end
end
_Pay close attention to the directory structure and how concerns just open the existing class definition, make sure you don’t re-inherit the class from AR::Base inside concerns UPDATE : See comment by Clifford Heath
I’ve always used "_log_to_ ":http://weblog.jamisbuck.org/2007/1/31/more-on-watching-activerecord for monitoring query output if irb ( script/console ). But with the recent connection pool changes, it stopped working. So here’s a new version :
def log_to(stream=$stdout) ActiveRecord::Base.logger = Logger.new(stream) ActiveRecord::Base.connection_pool.clear_reloadable_connections! end
As you might already know, you could use association names while constructing a join query with ActiveRecord::Base.find. For example :
class User < ActiveRecord::Base has_many :items end
Now, if you want to find all the users who have a black item, you could query like :
User.all :joins => :items, :conditions => { :"items.color" => 'black' }
Now the black magic part. Not many people know that you can supply the same join key more than once too. So :
User.all :joins => [:items, :items]
will produce a query like :
SELECT `users`.* FROM `users` INNER JOIN `items` ON items.user_id = users.id INNER JOIN `items` items_users ON items_users.user_id = users.id
This is very useful when you need to make complex sql queries. For example, if you want find all the users who have at least one “black” AND at least one “red” item :
User.all :joins => [:items, :items], :conditions => {:"items.color" => "red", :"items_users.color" => 'black'}
Of course, you’d want to be a little careful with joining too many tables if your tables are very large or if/when performance becomes a problem, etc. YMMV.
If you want to save an object even if the validations fail ( like, if your boss forces you to ) :
object.save(false)This is new in edge. You can now use dynamic finders with a bang(!). If there is no result found, RecordNotFound exception will be raised :
User.find_by_name!('lifo')