Ezra Zygmuntowicz
10/19/2006 2:39:00 AM
On Oct 18, 2006, at 6:56 PM, Morton Goldberg wrote:
> On Oct 18, 2006, at 9:12 PM, Ezra Zygmuntowicz wrote:
>
>>
>> On Oct 18, 2006, at 6:02 PM, David Vallner wrote:
>>
>>> Ezra Zygmuntowicz wrote:
>>>> klasses.inject(block) do |blk, klass|
>>>> lambda { klass.with_scope(scope_hash, &blk) }
>>>> end.call
>>>>
>>>> That outer block that ends on end returns the lambda made inside
>>>> of that
>>>> block. So end.call ends up being lambda { klass.with_scope
>>>> (scope_hash,
>>>> &blk) }.call
>>>>
>>>
>>> Hmm. Unless I'm very mistaken, it would also only call the lambda
>>> for
>>> the last object in klasses, generating several garbage (and
>>> relatively
>>> expensive) lambdas. Boggle. Am I missing something?
>>>
>>> David Vallner
>>
>> It looks like it would only call the last lambda but it does call
>> all of them. Here is a simplification of whats happening.
>>
>> irb(main):065:0> def scoper(klasses=[:foo, :bar, :baz], &block)
>> irb(main):066:1> klasses.inject(block) do |blk, klass|
>> irb(main):067:2* lambda { puts klass; blk.call}
>> irb(main):068:2> end.call
>> irb(main):069:1> end
>> => nil
>> irb(main):070:0* scoper { puts '&block called' }
>> baz
>> bar
>> foo
>> &block called
>> => nil
>> irb(main):071:0>
>
> I think I'm still confused by this. I infer from your post that the
> lambdas get nested by the inject (at the position of 'blk') with
> the first one innermost, which is why they get called in reverse
> order (and 'block' last of all). Is that right?
>
> Regards, Morton
>
Yes thats essentially it. Maybe this helps clear it up a bit. Lets
forget about the with_scope AR stuff for a minute and just mock this
out:
class Scoper
def self.with_block(hsh={}, &block)
p hsh.merge( {:klass => name})
block.call
end
end
class Foo < Scoper
end
class Bar < Scoper
end
class Baz < Scoper
end
def set_scopers(klasses=[Foo, Bar, Baz], &block)
hash = {:test => 'test'}
klasses.inject(block) do |blk, klass|
lambda { klass.with_block hash, &blk }
end.call
end
puts set_scopers { puts 'done'}
# outputs
{:test=>"test", :klass=>"Baz"}
{:test=>"test", :klass=>"Bar"}
{:test=>"test", :klass=>"Foo"}
done
Now if you comment out the block.call line in the Scoper.with_block
method:
class Scoper
def self.with_block(hsh={}, &block)
p hsh.merge( {:klass => name})
#block.call
end
end
<snip>
puts set_scopers { puts 'done' }
#ouput
{:test=>"test", :klass=>"Baz"}
nil
and run the same code again it only calls the last lambda that gets
made. It is a twisty little mess of block and wrappers that is hard
to follow. But essentially inject is chaning those lambda's together
and the final .call calls the chain which calls the final &blk in the
end.
nil
Cheers-
-- Ezra Zygmuntowicz
-- Lead Rails Evangelist
-- ez@engineyard.com
-- Engine Yard, Serious Rails Hosting
-- (866) 518-YARD (9273)