And here comes the trouble - Rails preloads all model classes on startup, and any changes that your controller actions do to the model classes stay cached for all further requests. So on production system, modifying an association in run-time would affect all future references through that association (and that's in fact what has happened to me). We better restore the conditions back after modifying them in with_conditions method. But if your Rails app is anything complicated, it probably uses delayed loading when rendering the views, so you can not reset association conditions right away in the model. Let's delay it until the next request:
class ActiveRecord::Base @@saved_conds = Hash.new # dynamically modify conditions as there is no other way in Rails # to specify run-time conditions on joins... def self.with_conditions(assoc, conditions) @@saved_conds[self.to_s] ||= Hash.new # only save if that's the first call during this request @@saved_conds[self.to_s][assoc] ||= reflect_on_association(assoc).options[:conditions] reflect_on_association(assoc).options[:conditions] = conditions yield end # reset association conditions if any has been modified by our # with_conditions calls in the previous request def self.reset_conditions return if @@saved_conds.empty? @@saved_conds.each { |klass,associations| associations.each { |assoc,saved| model = klass.constantize model.reflect_on_association(assoc).options[:conditions] = saved } } @@saved_conds = Hash.new end endThis solution I came up with is pretty hacky - but it works. If you know of a better way to mark a class for reload in Rails, let me know. This code saves modified conditions in a hash by class and association and then you would need to add code to restore them to a mint condition in a before_ or around_ filter in ApplicationController (call like Magazine.reset_conditions). Using class name as a Hash parameter since class variable behavior in Ruby is strange to say the least - it is shared in all inherited classes and the parent class :)