Jeff Davis wrote:

> I know I can define a method like:
> 
> def foo(&p)
>    p.call
> end
> 
> or:
> 
> def foo
>    yield
> end
> 
> First, what's the difference between those two? I can pass a block to 
> either.

They pretty much do the same. The first will turn the block into an 
object you can assign to variables and so on, however.

> Also, how do I make the block optional? I would like to make a method 
> that performs it's task as usual, but you could also pass it a block 
> that it can use.

It already is optional when you do the above. In the &p case p can be 
nil and in the yield one you will get a LocalJumpError if the block is 
not there. (You can use block_given?() to check for the block in the 
latter case.)

> And also, can you pass more than one block to the same method? Is it 
> only the last argument?

You can only use the block passing syntax for one block. It's however 
possible to do something like this:

def x(y, &z) y.call(z.call()) end
x(lambda { ... }) { ... }

> And what's the difference between:
> proc { puts 'foo'}
> and:
> { puts 'foo' }
> ?
> 
> Is "proc" a keyword that turns the block into a value, or is the block 
> already a value and it's being passed to "proc" to turn it into a Proc 
> object?

Blocks are already values, but they are implicitly passed around by 
default. lambda (or proc or Proc.new) will convert such a block into an 
actual Object which you can store in variables or call methods on.