How to DRY this?

P

Peter Szinek

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
 
B

benjohn

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

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
 
K

Ken Bloom

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.

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

Jean Helou

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

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
 
D

David Holroyd

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
 
J

Jean Helou

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

great !! I like yours better, now I wish I hadn't tried :)

jean
 
B

benjohn

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

M. Edward (Ed) Borasky

Peter said:
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

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

Simon Kröger

Peter said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top