Topic: Refactoring a method used in more than one model.

Hi, I'm a Rails beginner, and I wrote code that looks like this:

# Return nicely formatted location string.
def location
  if (city == "") and ((state != "") or (zip_code != ""))
    "#{state} #{zip_code}"
  elsif (city != "") and ((state != "") or (zip_code != ""))
    "#{city}, #{state} #{zip_code}"
  elsif (city != "") and ((state == "") and (zip_code == ""))
    "#{city}"
  else
    "Not Provided"
  end
end

I'm trying to return a sensible string based on which location fields are filled in.  I use this method in one model, and a variation in a different model.  Is there a place where I can stick a refactored version for both models to share?  Also, is writing the logic explicitly the only option in a case like this?

Thank you for your help!
John

Re: Refactoring a method used in more than one model.

Here is an example that may answer your question.
I created a module to contain a method I want to share, then I include it in each class I want to share it with:

module SomethingToShare
  def return_this(string)
    return "RETURNING: #{string}"
  end
end

class Test1
  include SomethingToShare
end
class Test2
  include SomethingToShare
end

t1 = Test1.new
puts t1.return_this("testing test one")
t2 = Test2.new
puts t2.return_this("testing test two")


Notice I have two classes and one method in a module that is included in both. The output looks like this:
pullmonkey:~/Tests$ ruby test.rb 
RETURNING: testing test one
RETURNING: testing test two

So in a rails app, you can create a shareme.rb file in your RAILS_ROOT/lib directory.  Then include that module in your models.

Last edited by pullmonkey (2008-01-14 23:02:43)

Re: Refactoring a method used in more than one model.

Thank you!

Re: Refactoring a method used in more than one model.

Hi balishaggy,

The code snippet below is a refactoring of your code, which is a little tough to understand. It should do exactly the same thing with fewer lines and in a more "plain english" sort of way. Notice that I changed the name of the method to reflect what it does. It's a little longer, but many times more understandable; when you look at this code 6 months from now, you will still be able to understand what it does.

def formatted_location_string
  state_and_zip = [state, zip].join(" ")
  if city.blank?
    state_and_zip.blank? ? "Not Provided" : state_and_zip
  else
    [city, state_and_zip].join(", ")
  end
end

If you have any questions about any of that, feel free to ask. You may also want to look at the composed_of method; using that, Location would become a class, allowing you to do something like:

your_model_object.location.to_formatted_s

It's a little more "object oriented," but its not a biggie.