Topic: How to be more DRY

Hi,

I have a method that I want to use in several different models (identical code), what is the best way to extract this method into it's own file and have the models reference that code?

I know I solved this problem with my controllers by creating a module in my lib folder, and then in my controller actions doing
send("module_name")

.. So I am wondering how to do this with models...

Thank you.

-patrick

Re: How to be more DRY

patrick99e99 wrote:

Hi,

I have a method that I want to use in several different models (identical code), what is the best way to extract this method into it's own file and have the models reference that code?

I know I solved this problem with my controllers by creating a module in my lib folder, and then in my controller actions doing
send("module_name")

.. So I am wondering how to do this with models...

Thank you.

-patrick

Do the same thing and include the module in the relevant models:

class Foo < ActiveRecord::Base
  include YourModule
...
end

That's the normal way to do it,  i never saw the 'send("module name")' approach before.

###########################################
#If i've helped you then please recommend me at Working With Rails:
#http://www.workingwithrails.com/person/ … i-williams

Re: How to be more DRY

Thanks for the reply...

I actually tried that before I posted, but it isn't working...

NoMethodError (undefined method `get_page_number' for #<Class:0x3625c98>):
  app/models/contact.rb:45:in `paginate_results'
  app/models/contact.rb:33:in `search'
  app/controllers/contacts_controller.rb:31:in `index'

Rendered rescues/_trace (55.9ms)
Rendered rescues/_request_and_response (2.6ms)
Rendering rescues/layout (internal_server_error)

... Here's my relevant code:

# contact.rb
class Contact < ActiveRecord::Base

  include PageFinder

  def self.search
    @search_results = # do stuff to search...
    paginate_results
  end

  def self.paginate_results
    page = get_page_number(:object => @search_results, :id => @first_contact_id, :per_page => @per_page)
   
    @search_results.paginate :per_page => @per_page, :page => page
  end


# environment.rb

require "page_finder.rb"


# page_finder.rb
module PageFinder
 
  def self.get_page_number(options)

    options[:object].each_with_index do |item, idx|
      page = (page ||= 0) + 1 if idx % options[:per_page] == 0
      return page if item.id == options[:id].to_i
    end
   
    # return first page if nothing is found.. just to be safe
    1
  end
 
end

Last edited by patrick99e99 (2009-07-22 15:45:05)

Re: How to be more DRY

nevermind..  I figured out I need to do:

module PageFinder
 
   def self.included(base)
     base.extend ClassMethods
   end
   
   module ClassMethods
     
     def get_page_number(options)

       options[:object].each_with_index do |item, idx|
         page = (page ||= 0) + 1 if idx % options[:per_page] == 0
         return page if item.id == options[:id].to_i
       end

       # return first page if nothing is found.. just to be safe
       1
     end
         
  end

Re: How to be more DRY

yeah - another way to mix in class methods is to have one module with class methods and another with instance methods and do (eg)

  include SomeInstanceMethods
  class << self
    include SomeClassMethods
  end

###########################################
#If i've helped you then please recommend me at Working With Rails:
#http://www.workingwithrails.com/person/ … i-williams

Re: How to be more DRY

Max Williams wrote:

yeah - another way to mix in class methods is to have one module with class methods and another with instance methods and do (eg)

  include SomeInstanceMethods
  class << self
    include SomeClassMethods
  end

Whilst that does work, the extend ClassMethods way you did is the standard way people do that, not more correct or anything like that, but people are used to seeing that, so probably the way to go.

Re: How to be more DRY

Cool thank you...

So I have this working, but now the problem I am facing is that when I edit the code in my external file-- the changes don't show up unless I quit the server and restart it.

I tried adding a 'require page_finder.rb' at the top of my model, but that didn't seem to make a difference.

Is there a way I can get rails to automatically refresh my lib directory without having to quit the server?

-patrick

Re: How to be more DRY

Is there a way I can get rails to automatically refresh my lib directory without having to quit the server?

Nope, this is where I miss multiple inheritance and it's a problem that you are going to have to live with.

You could of course add your code to the active record class but that would mean that all models would get this code AND the code would dissapear the next time you updated Rails gems AND you would HAVE to freeze your gems for production deployment so this is in no way a recommended approach but rather a reason why you should not do this if you thought about it at all.

Mixing in modules is as close to multiple inheritance as you are going to get.

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: How to be more DRY

jamesw wrote:

Is there a way I can get rails to automatically refresh my lib directory without having to quit the server?

Nope, this is where I miss multiple inheritance and it's a problem that you are going to have to live with.

You could of course add your code to the active record class but that would mean that all models would get this code AND the code would dissapear the next time you updated Rails gems AND you would HAVE to freeze your gems for production deployment so this is in no way a recommended approach but rather a reason why you should not do this if you thought about it at all.

Mixing in modules is as close to multiple inheritance as you are going to get.

I think that it's better this way, it's not hard to restart a server in development, and if you develop your module with some TDD/BDD you should be able to get it working well with minimal restarting anyway.

I don't know that Multiple Inheritance is a better solution. Inheritance can be abused and by being able to link up to a different string of classes together really could lead to a mish mash of confusion if abused.