In Ruby, What structures can a `rescue` statement be nested in
You can only use rescue in two cases:
Within a
begin ... end
blockbegin
raise
rescue
nil
endAs a statement modifier
i = raise rescue nil
Function, module, and class bodies (thanks Jörg) are implicit begin...end
blocks, so you can rescue within any function without an explicit begin
/end
.
def foo
raise
rescue
nil
end
The block form takes an optional list of parameters, specifying which exceptions (and descendants) to rescue
:
begin
eval string
rescue SyntaxError, NameError => boom
print "String doesn't compile: " + boom
rescue StandardError => bang
print "Error running script: " + bang
end
If called inline as a statement modifier, or without argument within a begin
/end
block, rescue will catch StandardError
and its descendants.
Here's the 1.9 documentation on rescue
.
Why does this rescue syntax work?
rescue
s do not need to be tied to an explicit begin
when they're in a method, that's just the way the syntax is defined. For examples, see #19 here and this SO question, as well as the dupe above.
Can you yield inside a code block in Ruby?
The code you have there does in fact not work, because there is no block to yield
to.
You will get a LocalJumpError
, which gets swallowed by the catch-all rescue
, and thus it will look like there is a problem with the file, when in fact, there is actually a programming error. (Teachable moment: never ever do a blanket catch-all rescue
, always rescue only exactly those exceptions you want to handle.)
Accessing elements of nested hashes in ruby
The way I usually do this these days is:
h = Hash.new { |h,k| h[k] = {} }
This will give you a hash that creates a new hash as the entry for a missing key, but returns nil for the second level of key:
h['foo'] -> {}
h['foo']['bar'] -> nil
You can nest this to add multiple layers that can be addressed this way:
h = Hash.new { |h, k| h[k] = Hash.new { |hh, kk| hh[kk] = {} } }
h['bar'] -> {}
h['tar']['zar'] -> {}
h['scar']['far']['mar'] -> nil
You can also chain indefinitely by using the default_proc
method:
h = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
h['bar'] -> {}
h['tar']['star']['par'] -> {}
The above code creates a hash whose default proc creates a new Hash with the same default proc. So, a hash created as a default value when a lookup for an unseen key occurs will have the same default behavior.
EDIT: More details
Ruby hashes allow you to control how default values are created when a lookup occurs for a new key. When specified, this behavior is encapsulated as a Proc
object and is reachable via the default_proc
and default_proc=
methods. The default proc can also be specified by passing a block to Hash.new
.
Let's break this code down a little. This is not idiomatic ruby, but it's easier to break it out into multiple lines:
1. recursive_hash = Hash.new do |h, k|
2. h[k] = Hash.new(&h.default_proc)
3. end
Line 1 declares a variable recursive_hash
to be a new Hash
and begins a block to be recursive_hash
's default_proc
. The block is passed two objects: h
, which is the Hash
instance the key lookup is being performed on, and k
, the key being looked up.
Line 2 sets the default value in the hash to a new Hash
instance. The default behavior for this hash is supplied by passing a Proc
created from the default_proc
of the hash the lookup is occurring in; ie, the default proc the block itself is defining.
Here's an example from an IRB session:
irb(main):011:0> recursive_hash = Hash.new do |h,k|
irb(main):012:1* h[k] = Hash.new(&h.default_proc)
irb(main):013:1> end
=> {}
irb(main):014:0> recursive_hash[:foo]
=> {}
irb(main):015:0> recursive_hash
=> {:foo=>{}}
When the hash at recursive_hash[:foo]
was created, its default_proc
was supplied by recursive_hash
's default_proc
. This has two effects:
- The default behavior for
recursive_hash[:foo]
is the same asrecursive_hash
. - The default behavior for hashes created by
recursive_hash[:foo]
'sdefault_proc
will be the same asrecursive_hash
.
So, continuing in IRB, we get the following:
irb(main):016:0> recursive_hash[:foo][:bar]
=> {}
irb(main):017:0> recursive_hash
=> {:foo=>{:bar=>{}}}
irb(main):018:0> recursive_hash[:foo][:bar][:zap]
=> {}
irb(main):019:0> recursive_hash
=> {:foo=>{:bar=>{:zap=>{}}}}
Is it possible to force fail a rescue statement?
First off, def
is an implicit begin
, so you can save yourself that. However, from your limited description, I think this should work for you:
[email1, email2, email3].reject(&:empty?).first || "undefined"
It either returns the first set email address or "undefined". My version reads a bit more like how you would describe the problem (take the first set email address or return "undefined"), mu's version is a bit more compact and probably what I'd use.
ruby catch-throw and efficiency
In addition to being the "correct" way to get out of control structures, catch-throw
is also significantly faster(10 times as fast in my testing). Check out this gist for my code and results.
Ruby Style: How to check whether a nested hash element exists
The most obvious way to do this is to simply check each step of the way:
has_children = slate[:person] && slate[:person][:children]
Use of .nil? is really only required when you use false as a placeholder value, and in practice this is rare. Generally you can simply test it exists.
Update: If you're using Ruby 2.3 or later there's a built-in
dig
method that does what's described in this answer.
If not, you can also define your own Hash "dig" method which can simplify this substantially:
class Hash
def dig(*path)
path.inject(self) do |location, key|
location.respond_to?(:keys) ? location[key] : nil
end
end
end
This method will check each step of the way and avoid tripping up on calls to nil. For shallow structures the utility is somewhat limited, but for deeply nested structures I find it's invaluable:
has_children = slate.dig(:person, :children)
You might also make this more robust, for example, testing if the :children entry is actually populated:
children = slate.dig(:person, :children)
has_children = children && !children.empty?
Related Topics
Does Dependency Injection Exist in Rails
How to Call a Super Class Method
Make Rake Task from Gem Available Everywhere
Difference Between Map and Each
Error While Installing Ruby-1.8.7 on Fedora 15
Is 'Yield Self' the Same as Instance_Eval
How to Take Screenshots of Web Pages Using Ruby and a Unix Server
Include Params/Request Information in Rails Logger
Concatenating String with Number in Ruby
How to Install (Build) Ruby 1.9.3 on Osx Lion
Ruby Check If Nil Before Calling Method
Cannot Connect to Remote Db Using Ssh Tunnel and Activerecord
Adding Attributes to a Ruby Object Dynamically
How to Reference Global Variables and Class Variables
Generate All Possibles Combinations of an Array with a Length Within a Given Range