# How to DRY up these methods?

**URL:** <https://www.ruby-forum.com/t/how-to-dry-up-these-methods/177448>\
**Category:** Rails\
**Created:** [October 17, 2009, 2:38pm UTC](https://www.ruby-forum.com/t/how-to-dry-up-these-methods/177448 "2009-10-17T14:38:28Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![devaulw](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/8e7dd6/32.png) [@devaulw](https://www.ruby-forum.com/u/devaulw)\
**Post date:** [October 17, 2009, 2:38pm UTC](https://www.ruby-forum.com/t/how-to-dry-up-these-methods/177448/1 "2009-10-17T14:38:28Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![devaulw](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/8e7dd6/32.png) [@devaulw](https://www.ruby-forum.com/u/devaulw)\
**Post date:** [October 17, 2009, 2:59pm UTC](https://www.ruby-forum.com/t/how-to-dry-up-these-methods/177448/2 "2009-10-17T14:59:15Z")

</div>

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](http://www.thecompleatrubyist.com)

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

---

<div class="post-metadata">

**Author:** ![devaulw](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/8e7dd6/32.png) [@devaulw](https://www.ruby-forum.com/u/devaulw)\
**Post date:** [October 17, 2009, 5:46pm UTC](https://www.ruby-forum.com/t/how-to-dry-up-these-methods/177448/3 "2009-10-17T17:46:49Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![devaulw](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/8e7dd6/32.png) [@devaulw](https://www.ruby-forum.com/u/devaulw)\
**Post date:** [October 17, 2009, 6:17pm UTC](https://www.ruby-forum.com/t/how-to-dry-up-these-methods/177448/4 "2009-10-17T18:17:06Z")

</div>

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 🙂 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](http://www.thecompleatrubyist.com)

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

---

<div class="post-metadata">

**Author:** ![devaulw](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/8e7dd6/32.png) [@devaulw](https://www.ruby-forum.com/u/devaulw)\
**Post date:** [October 18, 2009, 1:33pm UTC](https://www.ruby-forum.com/t/how-to-dry-up-these-methods/177448/5 "2009-10-18T13:33:30Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![devaulw](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/8e7dd6/32.png) [@devaulw](https://www.ruby-forum.com/u/devaulw)\
**Post date:** [October 18, 2009, 8:05pm UTC](https://www.ruby-forum.com/t/how-to-dry-up-these-methods/177448/6 "2009-10-18T20:05:47Z")

</div>

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](http://www.thecompleatrubyist.com)

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

---

<div class="post-metadata">

**Author:** ![devaulw](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/8e7dd6/32.png) [@devaulw](https://www.ruby-forum.com/u/devaulw)\
**Post date:** [October 19, 2009, 3:31am UTC](https://www.ruby-forum.com/t/how-to-dry-up-these-methods/177448/7 "2009-10-19T03:31:13Z")

</div>

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

Bill
