Forum: Ruby Helper for method pipelines?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Tom M. (Guest)
on 2006-04-20 09:28
(Received via mailing list)
Here is a minor coding problem that I suspect is common enough to have
an idiomatic solution.  I just don't know what that solution is.  If
you know of a more Rubyish way, please let me know.

Here's the problem.

Say you have a starting value and want to run it through a method
pipeline to yield a final result.  Part way through the pipeline, you
end up with an intermediate result that you cannot just chain into the
pipeline -- it doesn't have the necessary method.  So you are forced
to save that result into a variable and then pass it into some other
object's method in order to continue the pipeline and yield the final
result:

     intermediate_result =
       value.
       method1(args).
       method2(args)

     final_result =
       some_other_object.method3(intermediate_result)

What I do is add a 'with_self' method to Object that lets me incorporate
foreign method calls into my pipeline:

     final_result =
       value.
       method1(args).
       method2(args).
       with_self { |x| some_other_object.method3(x) }

The definition is straightforward:

     class Object
       def with_self
         block_given? ? yield(self) : self
       end
       def in_passing
         yield(self)
         self
       end
     end

I also define an 'in_passing' method for those times when you want to
preserve the input value yet process it for the side effects:

     1.with_self { |x| x + 1 }
     # returns 2

     1.in_passing { |x| p(x + 1) }
     # prints 2
     # returns 1

The thing is, I can't believe that something like this isn't already
built into Ruby.  I'm I overlooking it?

Thanks for your help.

Cheers,
Tom
Farrel L. (Guest)
on 2006-04-20 11:09
(Received via mailing list)
Any particular reason you just can't write it like

some_other_object.method3(value.method1(args).method2(args))

Farrel
Tom M. (Guest)
on 2006-04-21 00:20
(Received via mailing list)
Farrel L. wrote:
> Any particular reason you just can't write it like
>
> some_other_object.method3(value.method1(args).method2(args))

Yes, because it obscures the flow of the data, especially in more
complex examples.

In the general case, where you may have several intermediate values, and
where the methods and arguments may not be so conveniently compact, such
a refactoring seems likely to be hard to understand.  What was a
top-to-bottom pipeline has become a zigzag flow of data.

Consider this example (sans comments):

   def crumbs_from_path_parts(parts)
     parts.map do |crumb|
       crumb.
         gsub(/\/index/,'').
         sub(/.*--/,'').
         gsub(/ANY|-/,' ').
         sub(/^articles\//,'').
         strip.
         split(/\s+/).
         map { |s| @part_namer.canonicalize(s) }.
         join(" ").
         with_self { |s| @breadcrumb_namer.canonicalize(s) }
     end
   end

You can easily visualize the data flow, top to bottom, as it passes
through each transformative step.

Now let's say I want to trace the value around the last step.  I would
do it like this, without disturbing the flow of the pipeline:

   def crumbs_from_path_parts(parts)
     parts.map do |crumb|
       crumb.
         gsub(/\/index/,'').
         sub(/.*--/,'').
         gsub(/ANY|-/,' ').
         sub(/^articles\//,'').
         strip.
         split(/\s+/).
         map { |s| @part_namer.canonicalize(s) }.
         join(" ").
         in_passing { |s| @logger.debug "before naming: #{s}" }.
         with_self  { |s| @breadcrumb_namer.canonicalize(s)   }.
         in_passing { |s| @logger.debug "after naming: #{s}"  }
     end
   end

For cases like this, I think the pipeline is the simplest
representation.  Rewriting to remove in_passing and with_self would
increase the code's complexity.

What I want to know is whether Ruby has anything like in_passing and
with_self built in.  Given how common pipelining is, I would expect so,
but I can't find them.

Cheers,
Tom
This topic is locked and can not be replied to.