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

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

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

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 10/4/06, Peter Szinek <peter@rubyrailways.com> 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:

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

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 Wed, Oct 04, 2006 at 10:05:41PM +0900, Peter Szinek wrote:

--
http://david.holroyd.me.uk/

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}

···

On Wed, 04 Oct 2006 22:05:41 +0900, Peter Szinek 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:

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

--
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/
I've added a signing subkey to my GPG key. Please update your keyring.

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

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

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

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

jean

···

On 10/4/06, benjohn@fysh.org <benjohn@fysh.org> 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:
>
> ...
> 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

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