[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

whats this lambda code doing?

hemant

10/19/2006 12:48:00 AM

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?


--
There was only one Road; that it was like a great river: its springs
were at every doorstep, and every path was its tributary.

21 Answers

Ezra Zygmuntowicz

10/19/2006 12:54:00 AM

0


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

> 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?


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 Zygmuntowicz
-- Lead Rails Evangelist
-- ez@engineyard.com
-- Engine Yard, Serious Rails Hosting
-- (866) 518-YARD (9273)



David Vallner

10/19/2006 12:56:00 AM

0

hemant wrote:
> 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?
>
>

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 Vallner

Ken Bloom

10/19/2006 12:57:00 AM

0

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

> 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?

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

--
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu...
I've added a signing subkey to my GPG key. Please update your keyring.

David Vallner

10/19/2006 1:02:00 AM

0

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

Ezra Zygmuntowicz

10/19/2006 1:12:00 AM

0


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>



-- Ezra Zygmuntowicz
-- Lead Rails Evangelist
-- ez@engineyard.com
-- Engine Yard, Serious Rails Hosting
-- (866) 518-YARD (9273)



Ken Bloom

10/19/2006 1:14:00 AM

0

On Thu, 19 Oct 2006 10:02:11 +0900, 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

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 Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu...
I've added a signing subkey to my GPG key. Please update your keyring.

Ezra Zygmuntowicz

10/19/2006 1:33:00 AM

0


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

> On Thu, 19 Oct 2006 10:02:11 +0900, 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
>
> 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 Bloom. 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 Zygmuntowicz
-- Lead Rails Evangelist
-- ez@engineyard.com
-- Engine Yard, Serious Rails Hosting
-- (866) 518-YARD (9273)



Morton Goldberg

10/19/2006 1:57:00 AM

0

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

Ezra Zygmuntowicz

10/19/2006 2:39:00 AM

0


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)



Rick DeNatale

10/19/2006 2:40:00 AM

0

On 10/18/06, Ezra Zygmuntowicz <ezmobius@gmail.com> 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.denh...