Whats this lambda code doing?

I came across following code in typo’s application.rb and I can’t
understand the last part,

def with_blog_scoped_classes(klasses=[Content, Article, Comment,
Page, Trackback], &block)
default_id = this_blog.id
scope_hash = { :find => { :conditions => “blog_id = #{default_id}”},
:create => { :blog_id => default_id } }
klasses.inject(block) do |blk, klass|
lambda { klass.with_scope(scope_hash, &blk) }
end.call
end

Method will be called with a block and will probably add some scope to
the respective methods of ActiveRecord classes. But whats the last
part?
I mean, end.call?

On Oct 18, 2006, at 5:48 PM, hemant wrote:

klasses.inject(block) do |blk, klass|
lambda { klass.with_scope(scope_hash, &blk) }
end.call
end

Method will be called with a block and will probably add some scope to
the respective methods of ActiveRecord classes. But whats the last
part?
I mean, end.call?

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

– Ezra Z.
– Lead Rails Evangelist
[email protected]
– Engine Y., Serious Rails Hosting
– (866) 518-YARD (9273)

hemant wrote:

 lambda { klass.with_scope(scope_hash, &blk) }

end.call
end

Method will be called with a block and will probably add some scope to
the respective methods of ActiveRecord classes. But whats the last
part?
I mean, end.call?

The call to klasses.inject returns a block (a Proc). The .call evaluates
the Proc.

The code is a bit too clever for its own good. Personally I could go
around and slap everyone that chains a method after a code block /
closure block, or tags a statement modifier conditional / loop after it,
most of the time it’s just golfing to hide an unsightly level of control
flow nesting.

David V.

Ezra Z. 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 V.

On Thu, 19 Oct 2006 10:02:11 +0900, David V. wrote:

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

This some wierd kind of lambda chaining. You’re confusing “block” with
“blk”, so you’re thinking that each version of this code is using the
original block, but this is not so. Each lambda is chained to call the
previous lambda. I haven’t got a clue what with_scope does, but this
would
all be a lot easier to understand if it was possible to eliminate the
lambda, and deal with each class separately.

On Oct 18, 2006, at 6:02 PM, David V. wrote:

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

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>

– Ezra Z.
– Lead Rails Evangelist
[email protected]
– Engine Y., Serious Rails Hosting
– (866) 518-YARD (9273)

On Thu, 19 Oct 2006 09:48:07 +0900, hemant wrote:

  lambda { klass.with_scope(scope_hash, &blk) }
end.call

end

Method will be called with a block and will probably add some scope to
the respective methods of ActiveRecord classes. But whats the last
part?
I mean, end.call?

The whole do…end thing (and its binding to the inject call) has higher
precedence than the method call that follows it. It’s equivalent to

x=klasses.inject(block) do

end
x.call

On Oct 18, 2006, at 9:12 PM, Ezra Z. wrote:

block. So end.call ends up being lambda { klass.with_scope

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

On Oct 18, 2006, at 6:15 PM, Ken B. wrote:

(scope_hash,
This some wierd kind of lambda chaining. You’re confusing “block” with
“blk”, so you’re thinking that each version of this code is using the
original block, but this is not so. Each lambda is chained to call the
previous lambda. I haven’t got a clue what with_scope does, but
this would
all be a lot easier to understand if it was possible to eliminate the
lambda, and deal with each class separately.


Ken B… PhD candidate. Linguistic Cognition Laboratory.

with_scope is an ActiveRecord thing for scoping your where clauses.
It works like this:

Post.with_scope(:find => {:conditions => [“customer_id = ?”,
@customer.id]}) do
@post = Post.find(:all)
end

And you can nest these with_scope blocks inside each other so the
innermost #find method gets the scoped :conditions merged with its
own conditions. So the original posters code is taking a list of
Model classes and scoping the find and create calls on them to a
specific blog.id .

But I have to agree that code is too clever for its own good.
Twisting it up like that may make a golfer happy, the next guy to
come across it will be confused when it could have been stated clearer.

Cheers.

– Ezra Z.
– Lead Rails Evangelist
[email protected]
– Engine Y., Serious Rails Hosting
– (866) 518-YARD (9273)

On Oct 18, 2006, at 6:56 PM, Morton G. wrote:

That outer block that ends on end returns the lambda made inside
expensive) lambdas. Boggle. Am I missing something?
irb(main):069:1> end
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

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 Z.
– Lead Rails Evangelist
[email protected]
– Engine Y., Serious Rails Hosting
– (866) 518-YARD (9273)

On 10/18/06, Ezra Z. [email protected] wrote:

But I have to agree that code is too clever for its own good.
Twisting it up like that may make a golfer happy, the next guy to
come across it will be confused when it could have been stated clearer.

You know, I can understand, or maybe sympathize, with the appeal of
real golf, although I’ve yet to break 110, but I can’t for the life of
me understand why anyone things code golfing is good for anyone.

But then again “Succintness is Power!”


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On 10/19/06, Morton G. [email protected] wrote:

That outer block that ends on end returns the lambda made inside

=> nil
first one innermost, which is why they get called in reverse order
(and ‘block’ last of all). Is that right?

Regards, Morton

Hmm…i think i get the idea, but i do not understand how its .call is
working on all the lambdas?

On 10/19/06, hemant [email protected] wrote:

expensive) lambdas. Boggle. Am I missing something?
irb(main):069:1> end
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

Ok…i get it now…Thanks for the nice explanation Ezra.

On Oct 18, 2006, at 7:38 PM, Ezra Z. wrote:

hash = {:test => ‘test’}
{:test=>“test”, :klass=>“Foo”}
#block.call

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

What it is doing with the inject is essentially building up a block

with multiple procs inside it and then calling that. So the block
that gets called looks something like this by the time it gets call’ed:

lambda {
lambda { Foo.with_block hash, &blk }
lambda { Bar.with_block hash, &blk }
lambda { Baz.with_block hash, &blk }
}.call

Make sense?

– Ezra Z.
– Lead Rails Evangelist
[email protected]
– Engine Y., Serious Rails Hosting
– (866) 518-YARD (9273)

On Oct 18, 2006, at 10:48 PM, Ezra Z. wrote:

What it is doing with the inject is essentially building up a
block with multiple procs inside it and then calling that. So the
block that gets called looks something like this by the time it
gets call’ed:

lambda {
lambda { Foo.with_block hash, &blk }
lambda { Bar.with_block hash, &blk }
lambda { Baz.with_block hash, &blk }
}.call

Maybe I’m being even more dense than usual, but I can’t accept that.
It wouldn’t produce the observed print-out order. I think it must be
more like:

lambda { Baz.with_block(hash,
   lambda { Bar.with_block(hash,
      lambda { Foo.with_block(hash, &blk) }) }) }.call

Regards, Morton

It’s really not that bad. Learn to love the lambda, I say =)

unwind = lambda {|x| puts x}
(1..9).inject(unwind) do |stack, i|
  lambda {|x| puts x; stack[i]}
end[10]

On Oct 18, 2006, at 9:08 PM, Morton G. wrote:

lambda { Baz.with_block hash, &blk }
Regards, Morton

Ahh yes you are totally correct. See? It is tricky code ;)

– Ezra Z.
– Lead Rails Evangelist
[email protected]
– Engine Y., Serious Rails Hosting
– (866) 518-YARD (9273)