How to DRY this?

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

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

 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

On 10/4/06, Peter S. [email protected] 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

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

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

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

On 10/4/06, [email protected] [email protected] 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 :slight_smile:

jean

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}

jean wrote
great !! I like yours better, now I wish I hadn’t tried :slight_smile:

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

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

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

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

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?