Just a small collection of tips/tricks which I use a lot ( or try to ), that others might find helpful.
concerned_with
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.rb1 2 3 4 5 6 7 |
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 :
- app/models/user.rb – Main model
- app/models/user/validations.rb – User validations concern
- app/models/user/authentication.rb – User authentication concern
1 2 3 |
class User < ActiveRecord::Base concerned_with :validations, :authentication end |
1 2 3 |
class User < ActiveRecord::Base validates_presence_of :name end |
1 2 3 4 5 |
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
log_to
I’ve always used log_to for monitoring query output if irb ( script/console ). But with the recent connection pool changes, it stopped working. So here’s a new version :
1 2 3 4 |
def log_to(stream=$stdout) ActiveRecord::Base.logger = Logger.new(stream) ActiveRecord::Base.connection_pool.clear_reloadable_connections! end |
So.Many.Joins
As you might already know, you could use association names while constructing a join query with ActiveRecord::Base.find. For example :
1 2 3 |
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.
Force save
If you want to save an object even if the validations fail ( like, if your boss forces you to ) :
object.save(false) |
Find By Bang!
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') |





find_by_bang! – Hurrah! I’ve been handrolling methods like these for ages.
Great tips! Thanks for sharing
Concerned with is great – but can trip up when preloading /app/models, which is by default enabled for Edge when cache_classes is true.
app/models/payment/integrity.rb app/models/payment/payment.rb
The first file would load before Payment, and not inherit Payment from AR::Base.
Temp. solution being require_dependency to the parent in each concern to ensure a proper load order.
Thoughts ?
@Lourens: Do you have `app/models/payment.rb` and then the other two? It looks like Pratik’s example used a models/model_name.rb class as the one which inherited AR::Base and then the concerns were kept under models/model_name/ so surely Payment would have already inherited AR::Base. Course I could be horribly horribly wrong as I’ve just read this and not had a chance to play yet.
@Pratik: Many thanks for your tips
Normally, to re-declare the inheritance when re-opening a class isn’t a problem. Why should it be in the case of AR::Base? Is there something I’m missing here? I think you should declare the superclass inheritance in every concern.
Clifford,
You’re right. I wanted to make sure user.rb is loaded first. But I guess that doesn’t make much of a difference now that everything gets preloaded.
I’ve modified the post.
Thanks!
Hey Lourens, I think Clifford’s suggestion should solve that problem. If not, we need to change Rails initializer so that it sorts files properly before preloading them.
The problem with declaring the superclass comes up if you use a different superclass. So, obviously don’t do that :)
I feel like leaving out the superclass is more dry, but perhaps harder to understand. YMMV.
And now on GitHub as a plugin: http://github.com/jakehow/concerned_with