has_many :conditions =>

Now that this is working I wish to get the code out of the association and put it in one place where I can easily reuse it across models and associations. Is this possible?

One thought that I had earlier envisaged creating a virtual attribute on the Client model called active? and putting the code in there but, this fails on two counts: 1, the presence of a ? in the method name causes problems with the SQL engine in sqlite3 ( I have not tested this on PostgreSQL); and 2, virtual attributes cannot of course be evaluated during the SQL call (although one could argue that the association finders should in fact go through the dependent class and do exactly that because this sort of logic belongs in the model directly providing the where parameters).

It seems that the tidiest solution from the standpoint of rails coding and maintenance is to simply add an column named active of type boolean to the model and set it to true or false in accordance with the values in effective_date and superseded_date, which seems a bit redundant but easier to check.

Comments?

Assuming that you've been consistent in the begin/end date column names, you could/should create a module that you can include in the models that need this functionality. It'd look something like this:

module ActivatedObject   def self.included(base)     base.extend ClassMethods   end

  module ClassMethods     def sanitize_sql_here(array)       sanitize_sql array     end

    def has_activated_object(class_name)       self.has_one "active_#{class_name.to_s}",                    :class_name => class_name.to_s.classify,                    :conditions => '#{self.class.sanitize_sql_here ... }'     end

    def has_activated_collection(collection_name)       self.has_many "active_#{collection_name.to_s}",                    :class_name => collection_name.to_s.classify,                    :conditions => '#{self.class.sanitize_sql_here ... }'     end   end end

With that in place you would:

class ClassWithActivatedAssociation < ARec::Base   include ActivatedObject   has_activated_object :client   has_activated_collection :payment_to_group_helpers # :slight_smile:

  ... end

Sometimes, you just have to change tools:

  @active = '( effective_from <= current_date                AND              ( superseded_after IS NULL OR superseded_after >= current_date ))'

...

  has_one :buying_client, :class_name => 'Client',     :conditions => @active

"current_date", being a standard SQL function evaluated on each call, works perfectly for this situation.

Now the question is: Is there a place to put this so that all models can use it without having to specifically include a module or redefine the instance variable in each class?

James,

The pattern that I posted above is one that's often used in ARec 'acts' extensions. You could go the plugin route with the code (adding what's necessary to create the instance variable). Alternately you could keep it in lib and add

ActiveRecord::Base.send :include, 'ActivatedObject'

at the end of the file to inject the has_active_xxx methods to ActiveRecord. A

HTH, AndyV

AndyV wrote:

James,

The pattern that I posted above is one that's often used in ARec 'acts' extensions. You could go the plugin route with the code (adding what's necessary to create the instance variable). Alternately you could keep it in lib and add

ActiveRecord::Base.send :include, 'ActivatedObject'

at the end of the file to inject the has_active_xxx methods to ActiveRecord. A

This helps very much. Thank you.

My thinking regarding implementation of this leans towards the AR::BASE injection technique you outline above. It has the following attractions for me:

1. The code is kept in a readily identifiable file. 2. The implementation details are invisible to coders working with the models; mainly, if not solely, me ;-).

What would you favour?

I take it that the module file in lib would need to be explicitly loaded in config/enviorinment.rb or somewhere similar, correct?

You'd probably do that in config/initializers these days

Fred

Hi --

I take it that the module file in lib would need to be explicitly loaded in config/enviorinment.rb or somewhere similar, correct?

It depends how it's used. First of all, if you're require'ing things in environment.rb, they should probably be in config/initializers, which is where (as of Rails 2.0) you put things that are one-time, application-specific loads to be loaded when the server starts. If your library doesn't fall into that category, then lib is a likely choice. If, however, it's a non-ActiveRecord model, then app/models is the place.

I know I'm generalizing past your case, but bear with me as it might be helpful.

Rails has a mechanism for resolving unknown constants. If you refer to, say, MyConstant, Rails will look in its load-path for a file called my_constant.rb, and will load it. The assumption is that MyConstant will be defined in my_constant.rb. If it isn't, you get an error message. (Likewise if my_constant.rb doesn't exist, though a different error message: unknown constant.)

If your constant is MyClass::MyConstant, Rails will look for my_class/my_constant.rb somewhere in the load path.

All of this means that if you use this mechanism, naming your file appropriately and letting Rails automatically load it the first time you use the name of your class or module, you don't have to load it explicitly.

By the way, here's a nice demo of the constant-resolving mechanism, which Rails also uses for its own purposes. I have a model class called Container, in models/container.rb.

$ ./script/console Loading development environment (Rails 2.0.2)

Object.constants.grep(/Container/)

=> # No matching constants.

Container # I refer to Container;

                                        # this prompts Rails to                                         # go load the model file                                         # in an attempt to resolve                                         # the constant, which succeeds. => Container(id: integer, name: string, created_at: datetime, updated_at: datetime)

Object.constants.grep(/Container/) # And here it is.

=> ["Container"]

David

David A. Black wrote:

I know I'm generalizing past your case, but bear with me as it might be helpful.

Sad to say perhaps, but my ignorance is so vast I that I am grateful for every snippet of information that I can fit into my existing understanding. Your help is greatly appreciated.

I tried out something similar in console which, in its own way, is equally illuminating.

$ ruby script/console Loading development environment (Rails 2.0.2)

Application

LoadError: Expected ./app/controllers/application.rb to define Application

The module I am contemplating will probably have a "terminate", "terminate_row" or perhaps "deactivate_row" method to set the superseded_after attribute as well as setting the active_row attribute to contain the sql code that passes through :conditions. There may be other related helper methods that belong in there as well. Given that there likely will be more than one method I will probably put the resulting file (active_row.rb ?) into config/initializers.

I learned a lot from this. Many thanks.

James Byrne wrote:

David A. Black wrote:

I know I'm generalizing past your case, but bear with me as it might be helpful.

Sad to say perhaps, but my ignorance is so vast I that I am grateful for every snippet of information that I can fit into my existing understanding. Your help is greatly appreciated.

This applies to everyone, especially Fred and Andy, lest the context imply otherwise.