Ruby treats blocks of code as first-class values that can be passed to methods. A block is an anonymous chunk of code written either with curly braces or with the do and end keywords. By convention, curly braces are used for single-line blocks while do...end is preferred for multi-line blocks, partly because curly braces bind more tightly to the surrounding expression than do...end does.
When you want to store a block in a variable or pass it around like any other object, you wrap it in a Proc. A Proc can be created with Proc.new or with the arrow syntax, and it is invoked with .call or similar shorthand. A Lambda is a special kind of Proc that behaves more like a method: it strictly checks the number of arguments and returns control to the calling method rather than exiting it. As a result, a Lambda raises ArgumentError when given the wrong arity, while a Proc silently assigns nil to missing arguments. Returning from a Lambda returns to the caller, whereas returning from a Proc exits the enclosing method, which can cause surprising flow control.
Methods can accept blocks implicitly using the yield keyword, which transfers execution to whatever block was passed in. To avoid a LocalJumpError when no block is provided, methods often guard yield with block_given?. The ampersand operator provides a bridge between Procs and blocks: writing &block in a parameter list captures an incoming block as a Proc object, while passing &proc at a call site converts a Proc back into a block. This makes it possible to defer, store, or transform the executable chunks of code that flow through a Ruby program.