How to DRY up these methods?

I’ve clearly go a lot of repetition in the following methods:

def name=(value)
write_attribute(:name, if [0,“0”,""," “].include? value then nil
else value end)
end
def address=(value)
write_attribute(:address, if [0,“0”,”"," “].include? value then nil
else value end)
end
def city=(value)
write_attribute(:city, if [0,“0”,”"," "].include? value then nil
else value end)
end

Can someone tell me the convention to DRY this code? I’m wondering if I
use a lambda but am not quite sure that’s the right path or how to do
that.

Thanks,
Bill

Hi –

On Sat, 17 Oct 2009, Bill D. wrote:

end
def city=(value)
write_attribute(:city, if [0,“0”,“”," "].include? value then nil
else value end)
end

Can someone tell me the convention to DRY this code? I’m wondering if I
use a lambda but am not quite sure that’s the right path or how to do
that.

One idea is simply to parameterize the process:

def generic_writer(attr, value)
@blanks ||= [0, “0”, “”, " "]
write_attribute(attr, @blanks.include?(value) ? nil : value)
end

def name=(value)
generic_writer(:name, value)
end

etc.

If you want, you can automate the creation of the methods instead:

%w{ name address city }.each do |attr|
define_method(“#{attr}=”) do |value|
@blanks ||= [0, “0”, “”, " "]
write_attribute(attr, @blanks.include?(value) ? nil : value)
end
end

David


The Ruby training with D. Black, G. Brown, J.McAnally
Compleat Jan 22-23, 2010, Tampa, FL
Rubyist http://www.thecompleatrubyist.com

David A. Black/Ruby Power and Light, LLC (http://www.rubypal.com)

Thanks for the help. I used the latter to come up with:

@fields_to_normalize = %w{ address city country_id email_general fax
name phone phone_local state_id vacation_end_on vacation_notice
vacation_start_on zip_code }

@fields_to_normalize.each do |attr|
define_method("#{attr}=") do |value|
@blanks ||= [0, “0”, “”, " "]
write_attribute(attr, @blanks.include?(value) ? nil : value)
end
end

Is it possible to do something more Rails-ish so it reads:

normalize_fields :address, :city, :country_id

This additional re-factoring would allow re-use across models.

Bill

Hi –

On Sat, 17 Oct 2009, Bill D. wrote:

 write_attribute(attr, @blanks.include?(value) ? nil : value)

end
end

Is it possible to do something more Rails-ish so it reads:

normalize_fields :address, :city, :country_id

Sure – just write the method :slight_smile: I would consider putting the blanks
in a constant.

class Whatever

 BLANKS = [0, "0", "", " "]

 def self.normalize_fields(*syms)
   syms.each do |attr|
     define_method("#{attr}=") do |value|
       write_attribute(attr, BLANKS.include?(value) ? nil : value)
     end
   end
 end

 normalize_fields :address, :city, :country_id

end

David


The Ruby training with D. Black, G. Brown, J.McAnally
Compleat Jan 22-23, 2010, Tampa, FL
Rubyist http://www.thecompleatrubyist.com

David A. Black/Ruby Power and Light, LLC (http://www.rubypal.com)

Thanks to your help David I now have:

lib/normalizer.rb

module Normalizer

BLANKS = [0, “0”, “”, " "]

def self.included(base)
base.class_eval do
def self.normalize(*syms)
syms.each do |attr|
define_method("#{attr}=") do |value|
write_attribute(attr, BLANKS.include?(value) ? nil : value)
end
end
end
end
end

end

(I struggled with the above until I read that class_eval is needed to
make the inclusion happen. I wonder if a change to class_eval to yield
the included module would feel more natural.)

and

models/model.rb

include Normalizer
normalize :name, :address, :city, :country_id

Looks great and tests pass!

Thanks,
Bill

Hi –

On Sun, 18 Oct 2009, Bill D. wrote:

 def self.normalize(*syms)
   syms.each do |attr|
     define_method("#{attr}=") do |value|
       write_attribute(attr, BLANKS.include?(value) ? nil : value)
     end
   end
 end

end
end

end

(I struggled with the above until I read that class_eval is needed to
make the inclusion happen. I wonder if a change to class_eval to yield
the included module would feel more natural.)

You should be able to do:

def self.included(base)
def base.normalize(*syms)

end
end

I might be inclined to do:

module Normalizer
def normalize(*syms)

end
end

class Whatever
extend Normalizer

end

i.e., extend the class object directly rather than grabbing it
indirectly and operating on it. It seems a little more streamlined.

David


The Ruby training with D. Black, G. Brown, J.McAnally
Compleat Jan 22-23, 2010, Tampa, FL
Rubyist http://www.thecompleatrubyist.com

David A. Black/Ruby Power and Light, LLC (http://www.rubypal.com)

The second option works great as suggested. I tip my cap to you.

Bill