# Recursive send

**URL:** <https://www.ruby-forum.com/t/recursive-send/82610>\
**Category:** Ruby\
**Created:** [January 19, 2007, 4:31pm UTC](https://www.ruby-forum.com/t/recursive-send/82610 "2007-01-19T16:31:40Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![cuspendlove](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/f9ae1b/32.png) [@cuspendlove](https://www.ruby-forum.com/u/cuspendlove)\
**Post date:** [January 19, 2007, 4:31pm UTC](https://www.ruby-forum.com/t/recursive-send/82610/1 "2007-01-19T16:31:40Z")

</div>

I posted this to the Dallas Ruby Brigade, but thought it might be  
interesting for the larger Ruby audience. Your comments, questions,  
and complete debunking of why something like this is even necessary are  
welcomed.

## Curtis S.

Ruby’s Object#send is very useful, but what if we wanted to call  
several levels deep on an object? For instance:

# Normal call chain

post.comments.first.commented\_at

# Dynamically with send? Have to call three times.

post.send(:comments).send(:first).send(:commented\_at)

What if the number of calls to send is variable depending on what we’re  
trying to show? In one case we might need post.posted\_at for the date,  
and in another case we might need post.comments.first.commented\_at for  
the date.

How could we dynamically craft the definition of the methods to send if  
we don’t know how many calls to Object#send we’ll have? We need a way  
to define an arbitrary number of method calls.

Behold, a recursive send: Object#rsend

class Object  
def rsend(\*args, &block)  
obj = self  
args.each do |a|  
b = (a.is\_a?(Array) && a.last.is\_a?(Proc) ? a.pop : block)  
obj = obj. **send** (\*a, &b)  
end  
obj  
end  
alias\_method : **rsend** , :rsend  
end

Each argument passed to Object#rsend is an array with the symbols and  
arguments that will be passed on to Object#send:

post.rsend([:comments],[:first],[:commented\_at])

If there are no arguments to be passed on to send, the array brackets  
can be omitted:

post.rsend(:comments, :first, :commented\_at)

Of course, in practice you’ll probably be defining your method call  
chain in one part of your code, putting it in a variable, and sending  
it to rsend with a splat\*:

the\_date = [:comments, :first, :commented\_at]

#…somewhere else in your code you’ve passed the\_date along:  
post.rsend(\*the\_date)

With arguments:

a = [0,1,2,3,4,5,6,7,8,9]

a.rsend([:slice, 2, 8]) #=\> [2, 3, 4, 5, 6, 7, 8, 9]

a.rsend([:slice, 2, 8], [:slice, 1, 3]) #=\> [3, 4, 5]

Object#send accepts a block. What about blocks? Pass in a proc:

a.rsend([:map, (proc { |x| x\*2 })])  
#=\> [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

a.rsend([:map, (proc { |x| x\*2 })],  
[:select, (proc { |x| x % 4 == 0})])  
#=\> [0, 4, 8, 12, 16]

And, in an effort to make Object#rsend behave like Object#send for the  
simple case, you can send a regular block:

a.rsend(:map) { |x| x\*2 }  
#=\> [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Caveat: For the case needing parameters, Object#rsend does require an  
array, so:

a.rsend(:slice, 2, 8) # wrong, does not work like Object#send

a.rsend([:slice, 2, 8]) # right

A quirk that I’ve left in for fun, but it might (and maybe should)  
change: If providing a single block, that block will be called on  
every call unless you’ve already passed in a proc:

a.rsend(:map, :map) { |x| x\*2 }  
#=\> [0, 4, 8, 12, 16, 20, 24, 28, 32, 36]

a.rsend(:map, [:map, (proc { |x| x+5 })], :map) { |x| x\*2 }  
#=\> [10, 14, 18, 22, 26, 30, 34, 38, 42, 46]  
#outer block was called on first and third :map

Can anyone come up with a good use for this call-the-block-each-time  
behavior?

Has anyone done this already? I searched for such a thing and came up  
empty. Maybe this method should be called something else? I named it  
based on each call recursing down the chain of methods with a new  
object being returned for the next method to be sent to.

Suggestions and comments are welcome.

* * *

The original blog post:  
[http://www.csummers.org/index.php/2007/01/18/ruby-recursive-send/](http://www.csummers.org/index.php/2007/01/18/ruby-recursive-send/)

---

<div class="post-metadata">

**Author:** ![cuspendlove](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/f9ae1b/32.png) [@cuspendlove](https://www.ruby-forum.com/u/cuspendlove)\
**Post date:** [January 22, 2007, 2:27am UTC](https://www.ruby-forum.com/t/recursive-send/82610/2 "2007-01-22T02:27:37Z")

</div>

Curtis S. wrote:

> # Dynamically with send? Have to call three times.
> 
> post.send(:comments).send(:first).send(:commented\_at)
> 
> What if the number of calls to send is variable depending on what we’re  
> trying to show? In one case we might need post.posted\_at for the date,  
> and in another case we might need post.comments.first.commented\_at for  
> the date.

A word of warning: I once thought of using that same functionality in  
order  
to post complex information from an html form, e.g.  
  
  
raw html  
textile

But I then realized that was a major security hole. It allows an  
attacker  
to post stuff like:  
\<input name=“blog[connection.drop\_database.something]”  
By the time the recursive send fails on “something=”, the database has  
already been wiped. Well, this example doesn’t really work  
(drop\_database  
requires an argument), but you get the idea.

Daniel

---

<div class="post-metadata">

**Author:** ![cuspendlove](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/f9ae1b/32.png) [@cuspendlove](https://www.ruby-forum.com/u/cuspendlove)\
**Post date:** [January 22, 2007, 4:26pm UTC](https://www.ruby-forum.com/t/recursive-send/82610/3 "2007-01-22T16:26:30Z")

</div>

> But I then realized that was a major security hole. It allows an attacker  
> to post stuff like:  
> \<input name=“blog[connection.drop\_database.something]”  
> By the time the recursive send fails on “something=”, the database has  
> already been wiped. Well, this example doesn’t really work (drop\_database  
> requires an argument), but you get the idea.

Well, if you are going to send an unescaped, form submitted value to  
rsend, then, yes, that would be a security hole. But that’s kind of  
like saying you’re going to allow an unescaped, client submitted value  
to eval–which would be silly.

My usage of this is more along the lines of:

RoR controller w/ several actions that will render the same view. The  
date that I want to show in that view might be one of several choices  
of variable method depth depending on the action being rendered. So,  
in each action I set the appropriate method call chain to pass to  
rsend, and then use that variable in the view.

Here’s a contrived example:

#controller  
def action1  
@posts.find(:all, :include =\> :comments)  
@use\_this\_date = [:posted\_at]  
render :template =\> ‘posts/list’  
end

def action2  
@posts.find(:all, :include =\> :comments)  
@use\_this\_date = [:comments, :first, :commented\_at]  
render :template =\> ‘posts/list’  
end

#view  
\<% @posts.each do |post| %\>  
\<%= h post.title %\>,  
\<%= h post.rsend(\*@use\_this\_date) %\>  
\<% end %\>

---

<div class="post-metadata">

**Author:** ![cuspendlove](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/f9ae1b/32.png) [@cuspendlove](https://www.ruby-forum.com/u/cuspendlove)\
**Post date:** [January 23, 2007, 9:06am UTC](https://www.ruby-forum.com/t/recursive-send/82610/4 "2007-01-23T09:06:15Z")

</div>

Curtis S. wrote:

> to eval–which would be silly.  
> Heheh. True to a certain extent, but while sending an unescaped string  
> to  
> eval is obviously crazy, send seems safer. After all, RoR relies on  
> stuff  
> like \<input name=“obj[field]” absolutely all over the place. So it may  
> not  
> be immediately apparent that \<input name=“obj[field.subfield]” is far  
> more  
> dangerous.

But on the original topic, my own implementation of rsend was more like  
this:  
class Object  
def rsend(msg, \*others)  
result = send(msg)  
result = result.rsend(\*others) unless others.empty?  
result  
end  
end  
This allows a class to override the rsend method in order to provide  
specific  
behavior, e.g. in case send(msg) returns nil.

Daniel
