-
Notifications
You must be signed in to change notification settings - Fork 1
Ruby Expressions
In Ruby, syntax elements can be grouped as follows:
- Literals
- Variable/constant references
- Assignments
- Method calls, including array references
- Operators
- Lambda expressions
- Other "statement-like" expressions
- Miscellaneous (e.g. comments)
This page describes the "statement-like" expressions and miscellaneous. I call them "statement-like" because in other programming languages you often can't nest these types of expressions inside other expressions, however in Ruby, all the syntax elements are basically created equal. All syntax elements have a return value (except for control transfer expressions), and subject to certain rules you have to follow in combining them together, you can basically use any syntax element anywhere in Ruby code that requires a Ruby expression.
On this page, the <stmts> symbol and <*_stmts> symbols represent a Ruby block of statements, and other symbols represent Ruby expressions unless otherwise specified.
alias new_method old_method
alias :new_method :old_method
Returns: nil
alias creates an alias new_method of method old_method in the class base context. Any valid method names are permitted. Both methods may be expressed as a raw method name or as any form of symbol literal.
alias $new_gvar $old_gvar
Returns: nil
alias creates an alias $new_gvar of global variable $old_gvar. Any valid global variable names are permitted, except you may not create aliases of numbered backreferences e.g. $1.
undef method_name, :method_name2, ...
Returns: nil
undef marks 1 or more methods as undefined in the class base context. Any valid method names are permitted. Methods may be expressed as raw method names or as any form of symbol literals.
if <cond> then <stmts> [ elsif <cond> then <stmts> ... ] [ else <stmts> ] end
if <cond>; <stmts> [ elsif <cond>; <stmts> ... ] [ else <stmts> ] end
if <cond>
<stmts>
[ elsif <cond>
<stmts> ... ]
[ else <stmts> ]
end
unless <cond> then <stmts> [ else <stmts> ] end
<stmt> if <cond>
<stmt> unless <cond>
Returns: The value of the statements block that was executed by the expression, or if none was, nil
Standard if-then-else statement. The then keyword is optional if it is preceded by a semicolon or a newline. unless is the reverse of if - it succeeds if the condition is falsy.
If/unless statement modifiers cause their statement to be executed only if their test succeeds.
The condition expression in if/unless expressions, and in some other expressions, is processed with some special rules.
- If the condition is composed of any of the following structures, it will apply these rules to each subcomponent of them:
- Logical operations i.e.
and,or,&&and|| - Grouped expressions or begin/end expressions i.e.
(...)orbegin ... end - If the condition is a multiple left-hand-side assignment e.g.
a, b = true, false, this is a compile time error - there is no technical reason this must be the case but apparently Ruby creators find the idea of a multiple assignment in a conditional expression to be so particularly unintuitive and confusing that they've disallowed it - If the condition is an assignment of a literal, Ruby may warn you that perhaps you meant
==, not= - If the condition is a regular expression literal, this is treated as syntax shorthand for a match to
$_i.e./hello/is treated as/hello/ =~ $_except that no named capture assignment is performed - If the condition is some other type of literal, it may issue a warning
- If the condition is a range literal, it is treated as a flip-flop
while <cond> do <stmts> end
while <cond>; <stmts> end
while <cond>
<stmts>
end
until <cond> do <stmts> end
<stmt> while <cond>
<stmt> until <cond>
begin <stmts> end while <cond>
begin <stmts> end until <cond>
Returns: nil if the loop terminated normally, however the loop can be explicitly terminated with a return value via a break expression
Standard while loop. The do keyword is optional if it is preceded by a semicolon or a newline. until is the reverse of while - it succeeds if the condition's value is falsy.
While/until modifiers cause their statement to be repeatedly executed as long as their test succeeds.
A while/until modifier on a begin ... end expression is a slightly different special case - Ruby will execute the begin ... end statement once before it tests the condition and proceeds with the loop. In all other forms, Ruby will test the condition first.
The condition is processed according to the condition expression special rules described above.
case [ <expr> ] when <whenargs> then <stmts> [ when <whenargs> then <stmts> ] [ else <stmts> ] end
case [ <expr> ]; when <whenargs>; <stmts> [ when <whenargs>; <stmts> ] [ else <stmts> ] end
case [ <expr> ]
when <whenargs>
<stmts>
[ when <whenargs>
<stmts> ]
[ else <stmts> ]
end
Returns: The value of the statements block that was executed by the expression, or if none was, nil
A reasonably standard switch/case expression. Searches for the first successfully matching when clause, and executes the statements associated with it. If no when clause matches, executes the else clause.
The arguments to the when clause are a comma-separated list of regular and splat arguments, which are processed similar to method call arguments. After splatting, the case statement will test each argument in the list and if any one of them matches, then the when clause has matched. Arguments are tested as follows:
- If no
<expr>was provided, test succeeds if argument is truthy - Otherwise, test succeeds if matches
<expr>value via a the case equality operator i.e.<arg> === <expr>
The first when clause may be optionally preceded by a semicolon, and the then keyword is optional if it is preceded by a semicolon or newline.
for <lhs> in <expr> do <stmts> end
for <lhs> in <expr>; <stmts> end
for <lhs> in <expr>
<stmts>
end
Returns: The value returned by the each method call
Reasonably standard for each ... in ... statement. The do keyword is optional if it is preceded by a semicolon or a newline.
Basically, the for expression iterates over <expr> by calling the each method on it and passing <stmts> as the block. The <lhs> is almost, but not quite, processed as though it is the formal arguments in said block - <lhs> can actually be any valid left hand side target including multiple-assignment targets.
Specifically, the for statement is a syntax shorthand for certain forms of each method calls, with one difference - the for statement does not push a new dynamic variable context, so local variables created inside a for statement can be accessed outside of it. The block passed into the each method has a single hidden argument, which is not named and not accessible from Ruby code - we'll call it intarg in the explanations below.
<lhs> can be any simple assignment target (including variables, constants, array references and attributes), and it can also be a multiple-left-hand-side assignment target (i.e. a comma-separated list of lhs targets, rest targets, and nested multiple targets).
If <lhs> is a simple assignment target, the for expression effectively translates to:
<expr>.each { |intarg|
<lhs> = intarg
<stmts>
}
If <lhs> is a multiple target (including if it's a single rest arg), the for expression will implement an autosplat rule similar to (not quite the same as) that involved in block calls. The for expression effectively translates to:
<expr>.each { |*intarg|
if intarg.length == 1 and ingarg[0].kind_of? Array
intarg = intarg[0]
end
<lhs> = intarg
<stmts>
}
Normal class form:
class <classname> [ < <superclass> ]; <stmts> end
class <classname> [ < <superclass> ]
<stmts>
end
Singleton class form:
class << <obj>; <stmts> end
class << <obj>
<stmts>
end
Returns: The value of <stmts>
The class expression is the normal way to define a Ruby class. It creates a class (or accesses an existing class) and sets up the execution context so that code in <stmts> operates on that class, in particular def, class, module statements and certain API methods.
The normal class form:
-
<classname>may be any constant reference -
<superclass>may be any expression resolving to a Class object. If absent, the Object class is the default. - Looks for the specified
<classname>constant - If not found, creates a new class with the given
<superclass>and sets it to the<classname>constant - If found, and it's not a class, raise an exception
- If found, and its superclass (excluding singletons/mixins) doesn't match the given
<superclass>, raise an exception - Makes
selfthe class, pushes the class onto the class reference stack, and executes<stmts>
The singleton class form:
-
<obj>may be any expression - Accesses the singleton class of
obj - Makes
selfthe class, pushes the class onto the class reference stack, and executes<stmts>
The singleton class form may be used anywhere in Ruby code. The normal class form may not be used inside a def expression, unless it is further nested inside a class singleton form expression.
module <modulename> <stmts> end
Returns: The value of <stmts>
The module expression is the normal way to define a Ruby module. It creates a module (or accesses an existing module) and sets up the execution context so that code in <stmts> operates on that module, in particular def, class, module statements and certain API methods.
-
<modulename>may be any constant reference - Looks for the specified
<modulename>constant - If not found, creates a new module and sets it to the
<classname>constant - If found, and it's not a module, raise an exception
- Makes
selfthe module, pushes the module onto the class reference stack, and executes<stmts>reference stack, and executes<stmts>
module may not be used inside a def expression, unless it is further nested inside a class singleton form expression.
Normal form:
def <methodname> <formalargs> <stmts> end
Singleton method form:
def <obj>.<methodname> <formalargs> <stmts> end
def <obj>::<methodname> <formalargs> <stmts> end
Returns: nil
Normal form defines (or replaces) a method on the class base context class, and sets visibility as per the current visibility mode of the class base context.
Singleton method form defines a public method on the singleton class of <obj>. <obj> may not be a literal.
Formal arguments must either be surrounded by ( and ) or followed by a ; or newline.
Block clause form:
<block_stmts>
[ rescue [ <exc_list> ] [ => <exvar> ] then <rescue_stmts> ]
[ rescue ... ]
[ else <else_stmts> ]
Modifier form:
<stmt> rescue <stmt>
Returns: the value of the executed rescue or else block, if executed, otherwise the value of the initial <stmts>
Reasonably standard exception catching clause. The then keyword is optional if it is preceded by a semicolon or a newline.
Rules for block clause form:
- These rescue/else clauses are only permitted at the end of the statement body of
class,module,defandbegin/endexpressions, but they must come before anyensureclause.<block_stmts>here is the whole statement body within those expressions - If an exception is thrown in
<block_stmts>, each of therescueclauses is tested for a match, and the first match found is executed - The
rescueclauses function exactly likewhenclauses would function in acasestatement matching to the raised exception: -
<exc_list>is a comma-separated list of regular and splat arguments, which are processed similar to method call arguments.<exc_list>is optional and if not provided, defaults to theStandardErrorclass - If any one of the list matches the raised exception via case equality i.e.
<arg> === <exception>, the rescue clause matches - Typically, arguments to
rescueare classes, and===will (by default) match an exception object which is an instance of a subclass of that class -
<exvar>is any valid reference permitted on the left-hand-side of a non-multiple assignment. If a rescue clause matches, the exception that is caught will be assigned to this reference. - If no exception is raised in
<block_stmts>, theelseclause is executed
The modifier form works the same way as the block clause form, except that no <exc_list> or <exvar> can be provided (so it can only catch instances of StandardError), only one rescue clause can be provided, and no else clause can be provided. Multiple rescue modifiers can be chained though, as with other modifiers. The following are equivalent:
<stmt1> rescue <stmt2> rescue <stmt3>
begin
begin
<stmt1>
rescue
<stmt2>
end
rescue
<stmt3>
end
<block_stmts>
ensure
<ensure_stmts>
Returns: the value of <block_stmts>
Reasonably standard "finally" clause. <ensure_stmts> will be executed after <block_stmts>, even if <block_stmts> is terminated early due to a control transfer:
- If
<block_stmts>completes normally,<ensure_stmts>is executed afterwards - If
<block_stmts>does not complete normally due to some form of control transfer (e.g. it raises an exception, uses thethrowmethod, or uses a control transfer expression),<ensure_stmts>will be executed and then execution will continue as it would have if theensureclause was not there - Note that in this case, the
ensureblock functions similar to a catch-allrescueclause (except it catches all control transfers, not just exceptions), and then at the end of<ensure_stmts>, it re-raises the caught exception/control transfer. Therefore, if<ensure_stmts>itself causes a second control transfer and does not complete normally, then the original control transfer will not be re-raised.
The ensure clause is only permitted at the end of the statement body of class, module, def and begin/end expressions. The <block_stmts> here is the whole statement body within those expressions, including rescue clauses.
BEGIN { <stmts> }
Returns: N/A
The <stmts> inside a BEGIN{} block will be executed before any other code in the source file. If there are multiple BEGIN{} blocks, they will be executed in the order they occur. If there is a BEGIN{} block inside another BEGIN{} block, the inner BEGIN{} block will be executed before any other code in the outer BEGIN{} block.
BEGIN{} is more of a declarative statement than an expression really, because it's fully processed at compile time - the only such example in all of Ruby.
BEGIN{} blocks may not be nested within any other code structures except for other BEGIN{} blocks.
What is the point, you may ask, when you can just put the code at the top of the source file instead? I'm not really sure. Maybe it's intended to be used for some scenario where a Ruby sourcefile is spliced together from multiple sources.
END { <stmts> }
Returns: nil
The <stmts> inside a END{} block will be executed just prior to the running Ruby VM terminating. It does this by, when the END{} block is executed at runtime, creating a Proc out of the <stmts> and flagging the Proc to be called when the Ruby VM terminates. END{} blocks are only processed the first time they are executed at runtime - subsequent executions of the same END{} block do nothing.
In the case of multiple END{} blocks, they will be executed in reverse of the order that they were processed.
begin <stmts> end
Returns: the value of <stmts>
begin/end is perhaps more of a grammatical statement grouping syntax, like ( <smts> ), than an expression. However, while ( <stmts> ) has no meaning other than to group together some statements and put them in a place in Ruby code they might not normally be allowed, begin/end has a bit of meaning:
- Its statement body supports the
rescueandensureclauses - It has special meaning for the
untilandwhilemodifiers
super [ <args> ] [ <block> ]
Returns: the value of the superclass method call
super is used to invoke the superclass's version of the currently executing method. It has two basic forms:
- implicit arguments, where
<args>is not provided and the superclass's method is called with the arguments used to invoke the currently executing method (or in some cases the current value of those formal argument variables) - explicit arguments, where
<args>is provided and the superclass's method is called with those arguments. Note that if you want to explicitly specify no arguments, you can usesuper()
In all cases, if neither <block> nor a block call argument is provided, super will pass the block that was passed to the currently executing method (if any). If you wish to explicitly specify no block, give it a &nil block argument.
First the "method context" of the super call must be discovered, i.e. the method whose superclass implementation needs to be called. The way Ruby does this is a bit confusing, inconsistent and buggy, but the intention is probably as follows:
- A
supercall's method context can be defined by: - Its local variable context, if that context is a
defexpression (i.e. basic method definition) - Any of the contexts in its dynamic variable context stack, potentially, if the associated blocks were made into methods via the
define_methodAPI - Method context includes the name of the running method, and the class/module in which the running method's definition was found
- Method aliasing does not impact this - if a method alias is called, and the method contains a
supercall,superwill invoke the superclass's version of the original method name, not the alias name - A variable context defines the method context only if it was created (i.e. when the code was invoked that created the context which the
supercall can now see) as a method call - i.e. dynamic variable contexts created by blocks invoked by
yieldorcalldon't have associated method contexts - they only have method contexts if they are invoked by a method call when they've beendefine_method'd - The method context of a
supercall is the innermost method context on the dynamic variable context stack or, if none, the local variable context's method context - If no method context is found,
superwill raise an error. - Note that a Proc closure can contain a super call, and the closure will remember its method context
Next, super will locate the superclass implementation of the method context:
- Identify a class/module to start searching from
- If the method is implemented on a class, start with its superclass
- If the method is implemented on a module, locate the reference to the module in
self's class's ancestors, and start with the superclass of that reference- For some reason, if the module reference is not found, it will start with Object.
- If the start class is not part of
self's inheritance tree, an error will be raised - it seems this can only happen due to Ruby bugs or due toselfbeing deliberately altered, e.g. by invokingsuperin a Proc which is invoked viainstance_eval. -
superwill locate the method via standard method search logic, beginning with the start class, and invoke it on theselfobject: - It will search the start class and each of its ancestor classes in turn for the method
-
supercan access methods of any visibility - If the method is not found or it's marked undefined,
self.method_missingwill be invoked (unlessmethod_missingis the method context we're trying tosuper)
Note that the odd Object clause above means you can invoke super with a self incompatible with the method context (e.g. by changing self through instance_eval), as long as the method context is a module method and the new self is an instance of Object.
If explicit arguments are provided, they take the same form as method call arguments and are processed the same way. They may be provided in parentheses (method call style) or without parentheses (command call style).
If arguments are implicit, then the superclass method will be called with arguments very similar to the method context's call arguments, except that in general the current value of formal argument variables at the time of the super invocation is what gets provided to the superclass method. Specifically, the superclass's method will be called with call arguments as follows:
- The current value of each mandatory formal argument is provided as a call argument
- The current value of each optional formal argument is provided as a call argument
- The current value of the rest formal argument is provided as a splat call argument
- The current value of each post formal argument is provided as a call argument
- The exception to the above rules is for any multiple-assignment arguments in mandatory or post - for each multiple-assignment argument, the original simple-form call argument that the method context was called with (i.e. prior to the argument begin assigned to the multiple-assignment) is provided as a call argument
- Note that implicit
supercalls in method contexts which were created viadefine_methodare not yet supported in Ruby 1.9.3
yield [ <args> ]
Returns: the value returned by invoking the block
yield invokes the block that was passed in to the method, as stored in its local variable context, with the arguments provided. Arguments may be provided in parentheses (method call style) or without parentheses (command call style).
yield cannot be used to pass a block as a call argument. Nor can it be used to invoke the block passed to the currently executing block (because yield invokes the block stored in the local variable context, i.e. the block passed to the method). If you wish to invoke a block passed to the current block, capture the passed block in a block formal argument and use the call method. If you wish to pass a block as a call argument to a block, do so using the call method.
return [ <args> ]
Returns: N/A
Similar to a typical return statement, return is used to immediately exit from the currently running method or lambda block.
Arguments to return must be in command call style, i.e. provided without enclosing parentheses. They take the same form as method call arguments, except that you may not pass a block or block argument to return. The arguments are used to determine the return value of the method call/lambda block being exited, as follows:
- If a single, non-splatted argument is provided, its value is the return value
- If a set of hash associations is provided (and nothing else), the return value is a hash composed of them
- Otherwise, the return value is an array containing the list of arguments (after splatting etc.)
- If no args are provided, it returns
nil
The implementation of return is again somewhat buggy, but the intention appears to be an echo of the logic for super, as follows:
- A
returnstatement has the potential to return from anydefunit or lambda block it is lexically enclosed by, limited only by an enclosingdef. It can only return from the specific invocation of saiddef/blocks which created the unit of code containing thereturn- not just any invocation. Specifically, it can return from: - A block invocation whose corresponding dynamic variable context is accessible by the
return - A
defmethod invocation whose corresponding local variable context is accessible by thereturn - A block or def invocation whose variable context is accessible by the context which invoked a
classormoduleexpression whose local variable context is accessible by thereturn. This is applied transitively in the case of multiple nestedclass/moduleexpressions- In other words, a
returnexpression can cross aclass/moduleexpression to find what it's returning from -class/moduleis treated more like a dynamic context than a local context
- In other words, a
-
returnwill return from the innermost of the eligible invocations that is still currently running. This can involve returning from multiple intermediate running methods/blocks as well e.g. a Proc may contain a return statement that returns from a method 4 steps up the program stack - If nothing is found which can be returned from (e.g. return is called from a Proc closure created inside a method which is no longer running), an error is raised
Probably the most important bug is that you can't return from a lambda block which was invoked with the yield statement - this is fixed from Ruby 2.1.
break [ <args> ]
Returns: N/A
The break expression can be used as follows:
- If in the context of a lambda block, terminate that block, the same as
return - If in the context of a non-lambda block, terminate the method call which this block is lexically a part of
- If in the context of a while/until/for loop, terminate the loop
Arguments to break determine the return value of the expression that break is terminating, and have the same rules as the arguments to return.
The target of a break expression will be the innermost while/until/for loop or block lexically enclosing the break expression, limited only by an enclosing def. It is selected at compile time, and if not found causes a compile time error. The target is then processed as follows:
- If a loop, the loop is terminated
- If a lambda block, the block is terminated
- If a non-lambda block, look at the invocation of the lexical parent context of the block i.e. the parent dynamic or local variable context of the block
- If the parent context is not currently running, raise an error
- If the parent context is currently running but it's not currently within the method call to which the block was passed, raise an error
- Otherwise (i.e. the parent context is currently in the method call to which the block was passed), terminate the method call. Note that this form will therefore terminate the block, its lexical parent method call, and in the case of Proc closures potentially multiple intermediate method/block invocations as well.
There are some limitations on break:
- It may not pass a singleton
classexpression - if a singletonclassexpression is nested between thebreakexpression and its target, the target is not found and a compile time error is raised - It may not pass an "eval" boundary - you may not use an
evalmethod to compile and run code containing abreakexpression in order to break out of a target outside that code
next [ <args> ]
Returns: N/A
next has the same rules as break except for how it processes its target:
- If a
while/untilloop, the body of the loop is skipped and execution resumes with evaluating the loop's condition. The arguments passed tonexthave no effect. - If a
forloop, the body of the loop is skipped an execution resumes with theforloop's next iteration. The arguments passed tonextare the value of theforbody which is returned to its iterator. - If a block (lambda or not), the block is terminated. The arguments are the value of the block.
keyword_redo
Returns: N/A
redo has the same rules as break except that it accepts no arguments and for how it processes its target:
- If a
while/until/forloop, execution restarts at the start of the body of the loop. - If a block (lambda or not), execution restarts at the start of the block
retry
Returns: N/A
retry may be used in a rescue clause, in order to restart execution of the statement block which was covered by that rescue clause.
retry may not be further nested inside:
- Blocks/lambdas
-
def,classormethodexpressions - "Eval" boundaries (i.e. you may not invoke
eval 'retry'or suchlike to compile code containing a retry which attempts to target a statement block outside of the compiled code
Comments in Ruby take two forms:
- When a
#is encountered, the rest of the line is a comment - When the beginning of a line is
=begin(followed by whitespace or end of line), all further text is a comment until a line is reached which begins in=end(followed by whitespace or end of line)
Magic comments are Ruby # comments which include special syntax to declare the encoding of the source file or change other parameters. They take the following form of comments which contain somewhere within a set of name-value pairs as follows:
-*- [ <name> : <value> ] [ ; ] [ <name> : <value> ] ... -*-
<value> may be:
- Any string of characters excluding whitespace,
"and; - Any string of characters within double-quotes - all backslash-escaped characters are interpreted as themselves
is case insensitive.
Recognised names include:
-
encoding,coding: declares<value>to be the encoding of the source file. This magic comment may only appear in a comment on the first line of the source file (excluding any#!shebang line if present), and the comment must be preceded by nothing but whitespace -
warn-indent,warn_indent:<value>is case-insensitive and eithertrueorfalse, to enable or disable issuing warnings about code indenting mismatches
Encoding magic comments are also permitted in a looser form, possibly for backward compatibility - they will be interpreted even if the comment doesn't contain full magic comment syntax, but only contains somewhere (insensitively) coding : <value> or coding = <value>.
The following special variables exist:
-
__ENCODING__is the character encoding of the current ruby source file -
__FILE__is the name of the current ruby source file -
__LINE__is the line number of the current line of code in the source file -
selfis value of self
__END__
TODO
A Ruby Language Reference
Copyright © by Michael Hore, 2016.
Introduction to This Document
Ruby Elements
- Classes and Modules
- Methods
- Blocks, Procs and Lambdas
- Execution Context and Closures
- Variables, Constants and Namespaces
- Types and Literals
- Ruby Expressions
- Operators
Syntax Grammar
Exceptions and Throw
Ruby Sourcefiles and Libraries
Multi Threading
Execution and Lifecycle