# How to DRY this?

**URL:** <https://www.ruby-forum.com/t/how-to-dry-this/70980>\
**Category:** Ruby\
**Created:** [October 4, 2006, 3:06pm UTC](https://www.ruby-forum.com/t/how-to-dry-this/70980 "2006-10-04T15:06:28Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [October 4, 2006, 3:06pm UTC](https://www.ruby-forum.com/t/how-to-dry-this/70980/1 "2006-10-04T15:06:28Z")

</div>

Hello,

I have two very similar code snippets in two different methods, and I am  
absolutely sure there is some nice way to DRY them in Ruby… I am still  
a noob when comes to Ruby idioms so I’d appreciate some help 😉

```
 ...
 while element.class != Hpricot::Doc do
     path.push element.name
     element = element.parent
 end
 ...

```

and  
…  
while element.class != Hpricot::Doc do  
path.push element  
element = element.parent  
end  
…

i.e. in the first snippet I am pushing element’s names, and in the  
latter the elements themselves.

Thanks,  
Peter  
[http://www.rubyrailways.com](http://www.rubyrailways.com)

---

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [October 4, 2006, 3:16pm UTC](https://www.ruby-forum.com/t/how-to-dry-this/70980/2 "2006-10-04T15:16:29Z")

</div>

> ```
> end
> ...
> 
> ```
> 
> and  
> …  
> while element.class != Hpricot::Doc do  
> path.push element  
> element = element.parent  
> end  
> …

Pull out the loop logic in to another method…

def traverse\_up(element)  
while element.class != Hpricot::Doc do  
yield(element)  
element = element.parent  
end  
end

The two cases now become:  
traverse\_up(element) {|e| path.push e,name}  
and  
traverse\_up(element) {|e| path.push e}

Additional comments:

It may (or may not) make sense for the traverse\_up method to be a member  
of element’s class.

You might like to provide an additional, optional argument, that can be  
used to control loop termination…

def traverse\_up(element, terminate\_at = Hpricot::Doc)  
while !(terminate\_at === element) do  
yield(element)  
element = element.parent  
end  
end

(I’ve also changed the way that element’s class is checked here - but  
classes of Hpricot::Doc will also match and terminate the loop).

Cheers,  
Benj

---

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [October 4, 2006, 3:18pm UTC](https://www.ruby-forum.com/t/how-to-dry-this/70980/3 "2006-10-04T15:18:30Z")

</div>

On 10/4/06, Peter S. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> ```
> end
> 
> ```
> 
> i.e. in the first snippet I am pushing element’s names, and in the  
> latter the elements themselves.
> 
> Thanks,  
> Peter  
> [http://www.rubyrailways.com](http://www.rubyrailways.com)

interesting question …

def element\_pusher(root\_element, attribute=nil)  
while element.class!=Hpricot::Doc do  
pushie = attribute.nil? ? element : element.send(attribute.to\_sym)  
path.push pushie  
element = element.parent  
end  
path  
end

good ?

I don’t have time to mockup something to test it but  
path.push(attribute.nil? ? element : element.send(attribute.to\_sym))  
might even work …

jean

jean

---

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [October 4, 2006, 3:20pm UTC](https://www.ruby-forum.com/t/how-to-dry-this/70980/4 "2006-10-04T15:20:30Z")

</div>

On Wed, Oct 04, 2006 at 10:05:41PM +0900, Peter S. wrote:

> Hello,
> 
> I have two very similar code snippets in two different methods, and I am  
> absolutely sure there is some nice way to DRY them in Ruby… I am still  
> a noob when comes to Ruby idioms so I’d appreciate some help 😉

how about (untested)…

def element\_and\_each\_ancestor(element)  
while element.class != Hpricot::Doc do  
yield element  
element = element.parent  
end  
end

> ```
> ...
> while element.class != Hpricot::Doc do
> path.push element.name
> element = element.parent
> end
> ...
> 
> ```

element\_and\_each\_ancestor(element) do |el|  
path.push el.name  
end

> and  
> …  
> while element.class != Hpricot::Doc do  
> path.push element  
> element = element.parent  
> end  
> …

element\_and\_each\_ancestor(element) do |el|  
path.push element  
end

> i.e. in the first snippet I am pushing element’s names, and in the  
> latter the elements themselves.

ta,  
dave

---

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [October 4, 2006, 3:22pm UTC](https://www.ruby-forum.com/t/how-to-dry-this/70980/5 "2006-10-04T15:22:30Z")

</div>

On 10/4/06, removed\_email\_address@domain.invalid [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> > ```
> > end
> > 
> > ```
> 
> Pull out the loop logic in to another method…  
> and
> 
> Cheers,  
> Benj

great !! I like yours better, now I wish I hadn’t tried 🙂

jean

---

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [October 4, 2006, 3:24pm UTC](https://www.ruby-forum.com/t/how-to-dry-this/70980/6 "2006-10-04T15:24:30Z")

</div>

On Wed, 04 Oct 2006 22:05:41 +0900, Peter S. wrote:

> ```
> end
> 
> ```
> 
> i.e. in the first snippet I am pushing element’s names, and in the  
> latter the elements themselves.

Option 1: don’t. It’s a very small snippet of code.  
Option 2:

def dopush  
while element.class!= Hpricot::Doc do  
path.push(yield(element))  
element = element.parent  
end  
end

The first snippet becomes

dopush {|e| e.name}

The second snippet becomes

dopush {|e| e}

---

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [October 4, 2006, 3:30pm UTC](https://www.ruby-forum.com/t/how-to-dry-this/70980/7 "2006-10-04T15:30:32Z")

</div>

> jean wrote  
> great !! I like yours better, now I wish I hadn’t tried 🙂

Well, thanks, but don’t let that put you off (or I’ll wish I hadn’t).

It’s quite interestesting to look at the different approaches. The four  
suggestions have three different approaches, and two of the approaches  
are _very_ similar (utilisation of yield).

---

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [October 4, 2006, 9:09pm UTC](https://www.ruby-forum.com/t/how-to-dry-this/70980/8 "2006-10-04T21:09:08Z")

</div>

Peter S. wrote:

> Hello,
> 
> I have two very similar code snippets in two different methods, and I am  
> absolutely sure there is some nice way to DRY them in Ruby… I am still  
> a noob when comes to Ruby idioms so I’d appreciate some help 😉

If you had this:

def ancestors element  
…  
while element.class != Hpricot::Doc do  
path.push element  
element = element.parent  
end  
path  
end

> ```
> ...
> while element.class != Hpricot::Doc do
> path.push element.name
> element = element.parent
> end
> ...
> 
> ```

would become: ancestors(element).map{|a|a.name}

> and  
> …  
> while element.class != Hpricot::Doc do  
> path.push element  
> element = element.parent  
> end  
> …

would become: ancestors(element)

> i.e. in the first snippet I am pushing element’s names, and in the  
> latter the elements themselves.
> 
> Thanks,  
> Peter

cheers

Simon

---

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [October 4, 2006, 3:36pm UTC](https://www.ruby-forum.com/t/how-to-dry-this/70980/9 "2006-10-04T15:36:33Z")

</div>

Peter S. wrote:

> ```
> end
> 
> ```
> 
> i.e. in the first snippet I am pushing element’s names, and in the  
> latter the elements themselves.
> 
> Thanks,  
> Peter  
> [http://www.rubyrailways.com](http://www.rubyrailways.com)

A couple of obvious (to me, anyhow) questions:

1. You are “pushing” elements and element names. Are you “pulling” or  
“popping” them somewhere else?

2. If you just pushed the element name, could you retrieve the element  
from its name?

3. Is there some reason you need two loops up the chain? Could you do

while element.class != Hpricot::Doc do  
path.push [element.name =\> element]  
element = element.parent  
end

or something similar?
