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 --

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.

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

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 --

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

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

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 --

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.)

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