citizen428.blog()

Try to learn something about everything

RoR: 'Call': Cannot Yield From a Proc Type Filter.

As we all know, there are 2 main differences between procs and lambdas in Ruby.

1. Argument checking:

1
2
3
4
5
6
7
8
9
10
11
12

1
2
3
4
5
6
7
8
9
10
11
12
<span class='line'><span class="o">&gt;&gt;</span> <span class="nb">p</span> <span class="o">=</span> <span class="no">Proc</span><span class="o">.</span><span class="n">new</span> <span class="p">{</span> <span class="o">|</span><span class="nb">name</span><span class="o">|</span> <span class="s2">&quot;Hello </span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">&quot;</span> <span class="p">}</span>
</span><span class='line'><span class="o">=&gt;</span> <span class="c1">#&lt;Proc:0x000001008dc600@(irb):3&gt;</span>
</span><span class='line'><span class="o">&gt;&gt;</span> <span class="n">l</span> <span class="o">=</span> <span class="nb">lambda</span> <span class="p">{</span> <span class="o">|</span><span class="nb">name</span><span class="o">|</span> <span class="s2">&quot;Hello </span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">&quot;</span> <span class="p">}</span>
</span><span class='line'><span class="o">=&gt;</span> <span class="c1">#&lt;Proc:0x000001008d3c08@(irb):4 (lambda)&gt;</span>
</span><span class='line'><span class="o">&gt;&gt;</span> <span class="nb">p</span><span class="o">.</span><span class="n">call</span>
</span><span class='line'><span class="o">=&gt;</span> <span class="s2">&quot;Hello &quot;</span>
</span><span class='line'><span class="o">&gt;&gt;</span> <span class="n">l</span><span class="o">.</span><span class="n">call</span>
</span><span class='line'><span class="no">ArgumentError</span><span class="p">:</span> <span class="n">wrong</span> <span class="n">number</span> <span class="n">of</span> <span class="n">arguments</span> <span class="p">(</span><span class="mi">0</span> <span class="k">for</span> <span class="mi">1</span><span class="p">)</span>
</span><span class='line'>	<span class="n">from</span> <span class="p">(</span><span class="n">irb</span><span class="p">):</span><span class="mi">4</span><span class="ss">:in</span> <span class="sb">`block in irb_binding&#39;</span>
</span><span class='line'><span class="sb">	from (irb):6:in `</span><span class="n">call</span><span class="s1">&#39;</span>
</span><span class='line'><span class="s1">	from (irb):6</span>
</span><span class='line'><span class="s1">	from /Users/michi/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `&lt;main&gt;&#39;</span>
</span>

2. The possibility to return from within the closure:

1
2
3
4
5
6
7
8
9
10
11
12
>> p = Proc.new { return }
=> #<Proc:0x000001008c83a8@(irb):7>
>> l = lambda { return }
=> #<Proc:0x000001008c4050@(irb):8 (lambda)>
>> p.call
LocalJumpError: unexpected return
	from (irb):7:in `block in irb_binding'
	from (irb):9:in `call'
	from (irb):9
	from /Users/michi/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'
>> l.call
=> nil

I’ve known about this for a while, but as a Rubyists who until quite recently happily ignored Rails, I didn’t know that ActiveSupport changes the “LocalJumpError” from the second example into

1
2
3

1
2
3
<span class='line'><span class="sb">`call&#39;: Cannot yield from a Proc type filter.</span>
</span><span class='line'><span class="sb">   The Proc must take two arguments and execute</span>
</span><span class='line'><span class="sb">   #call on the second argument. (ArgumentError)</span>
</span>

when you try to return from callbacks like “before_save”. I spent quite some time debugging this before I finally found the solution in this blog post. Instead of

1
2
3
4

1
2
3
4
<span class='line'><span class="n">after_save</span> <span class="k">do</span> <span class="o">|</span><span class="n">foo</span><span class="o">|</span>
</span><span class='line'>  <span class="k">return</span> <span class="k">if</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'>  <span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'><span class="k">end</span>
</span>

you have to do

1
2
3
4

1
2
3
4
<span class='line'><span class="n">after_save</span><span class="p">(</span><span class="nb">lambda</span> <span class="p">{</span> <span class="o">|</span><span class="n">foo</span><span class="o">|</span>
</span><span class='line'>  <span class="k">return</span> <span class="k">if</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'>  <span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span><span class='line'><span class="p">})</span>
</span>

Probably obvious to more experienced Rails developers, but I really got thrown off by the “ArgumentError” that replaced the “LocalJumpError”.

Comments

Copyright © 2016 - Michael Kohl - Powered by Octopress