Is there any built in way to require that a block be passed to a Ruby method? I realize I can just raise an exception if block_given? is false, but is there some nicer way to do it?
Simply by using yield.
If you include yield in a method, and a block is not given, it throws an error.
Put this in a file and run it:
def needs_block
yield
end
needs_block
It will throw an error like this:
LocalJumpError: no block given
from (irb):14:in `needs_block'
from (irb):16
raise 'need block' unless block_given?
If your method required a block, Ruby will prompt it. The raise keyword doesn't require a block, it only prompts a message for handling an Exception.
It could be a method like the above example
def needs_block
yield
end
needs_block
Or you could require a Proc
def needs_block(&Proc)
proc.call
end
Anyway, adding raise block_given? would be nice.
Here says:
"The raise method is from the Kernel module. By default, raise creates
an exception of the RuntimeError class. To raise an exception of a
specific class, you can pass in the class name as an argument to
raise".
Related
Given a class:
class Foo
def initialize(input1)
#input1 = input1
end
end
is there some way that would throw a more helpful error against a = Foo.new()? How can I build a method that throws an ArgumentError in a more helpful way?
I'd like to build this into the class. The Programming Ruby site lists several error-trapping mechanisms, but all of these seem to depend on wrapping a = Foo.new() in a catch block or the like. I would like to have my error trapping within the class itself however.
Since you're new to Ruby it's understandable this error might seem odd, yet it's also an error that's very specific to passing the wrong arguments in. Remapping it to something else isn't necessarily helpful, it ends up hiding problems in your code. I'd suggest leaving it as-is and expecting errors like that to occur if you're not calling it correctly.
The alternative is, at least in newer versions of Ruby, to declare keyword arguments with no defaults:
def initialize(input1:)
end
That's a required keyword argument, and the error is more specific:
ArgumentError: missing keyword: input1
The downside is you have to call it like this:
Foo.new(input1: 'test')
That might be beneficial in terms of clarity. It's up to you.
you can use a default value and raise whatever error you need within the initialize method for example
Class A
def initialize(a = nil)
raise("give me an A") if a.nil?
#a = a
end
end
You can do this pretty simply by raising that error when the argument is not defined. You can add a message to the ArgumentError exception by specifying it as an argument on the exception:
class Foo
def initialize(input1=nil)
raise ArgumentError, "expected a value for Foo.new('value')" unless input1
#input1 = input1
end
end
After reading Programming Ruby a bit more, I think using alias_method as a hook might serve:
alias_method :initialize_orig, :initialize
def initialize(*args)
begin
result = initialize_orig(*args)
return result
rescue Exception
$stderr.print "Need to use argument 'input1'\n"
raise
end
end
Here's what I'm trying to do:
class Foo
def foo
raise "lol noob"
end
# ... many other methods here ...
rescue StandardError => e
puts "error caught: #{e}"
end
Foo.new.foo
RuntimeError: lol noob
from (pry):45:in `foo'
As you can see, this does not work.
What I'm trying to avoid is to put a rescue block into every single method, given that they're many. Is it possible? If not, what's the best practice?
TL;DR
In Ruby, you generally have to wrap the caller with rescue, rather than the receiver.
Explanation
It's likely that you're finding this behavior surprising because you're not thinking of classes as executable code. A rescue clause in a class definition will capture exceptions raised while the class is being interpreted. Consider the following example:
class Foo
raise 'bar'
rescue
'Rescued!'
end
#=> "Rescued!"
Here, the rescue clause works because the exception is raised while the class code is executing. However, some Foo#bar method wouldn't normally get rescued by this clause (except possibly while being defined) because Foo is no longer the caller, it's the receiver.
A rescue clause on the class will catch exceptions raised when the class itself is being defined. However, to rescue within a method at run-time, you need to rescue within the caller (in this case, the #bar method) as follows:
class Foo
def bar
raise 'method exception'
rescue
'Rescued method exception.'
end
end
Foo.new.bar
#=> "Rescued method exception."
It would make no sense to have a common rescue block for a class, each method does different things right? How would you handle the variety of errors all in the same way? Error handling is just as much part of the logic as the "main" part of the method(s).
There's no silver bullet here, you rescue where you need to, and do what is needed when it is needed.
The Exceptional Ruby book may be of interest if you want to learn some common best practices.
If you're absolutely sure that the only error handling you need to do is log out errors, you can abstract away some of the repetitive error logging by defining a helper method that takes in a block and do your error handling there.
For example,
class Foo
def foo
handle_exception do
raise "lol noob"
end
end
def bar
handle_exception do
raise "rofl"
end
end
def handle_exception
yield
rescue => e
puts "error caught: #{e}"
end
end
Foo.new.foo # error caught: lol noob
Foo.new.bar # error caught: rofl
This has the benefit that later on you decide that want some alternate behavior, ie adding in a backtrace, you only have to touch one line of code:
def handle_exception
yield
rescue => e
puts "error caught: #{e}\n#{e.backtrace.join("\n")}"
end
I have written a program that utilizes an external ruby gem. As I am doing a lot of different actions with this, I want to be able to rescue and handle exceptions across the board, instead of implementing it each time I call a method.
What is the best way to do this?
Should I write my own method that simply calls the external gem and also rescues exceptions? Or is there another way to do something like "Whenever an exception of this type comes up anywhere in the program, handle it this way"?
I know that if I wrote the external gem code I could add error handling like that, but that is not feasible.
The basic answer to this is probably to wrap the class you're working with; Ruby allows for a lot of flexibility for doing this since it has method_missing and a pretty dynamic class environment. Here's an example (which may or may not be fatally flawed, but demonstrates the principle:
# A Foo class that throws a nasty exception somewhere.
class Foo
class SpecialException < Exception; end
def bar
raise SpecialException.new("Barf!")
end
end
# This will rescue all exceptions and optionally call a callback instead
# of raising.
class RescueAllTheThings
def initialize(instance, callback=nil)
#instance = instance
#callback = callback
end
def method_missing(method, *args, &block)
if #instance.respond_to? method
begin
#instance.send(method, *args, &block)
rescue Exception => e
#callback.call(e) if #callback
end
else
super
end
end
end
# A normal non-wrapped Foo. Exceptions will propagate.
raw_foo = Foo.new
# We'll wrap it here with a rescue so that we don't exit when it raises.
begin
raw_foo.bar
rescue Foo::SpecialException
puts "Uncaught exception here! I would've exited without this local rescue!"
end
# Wrap the raw_foo instance with RescueAllTheThings, which will pass through
# all method calls, but will rescue all exceptions and optionally call the
# callback instead. Using lambda{} is a fancy way to create a temporary class
# with a #call method that runs the block of code passed. This code is executed
# in the context *here*, so local variables etc. are usable from wherever the
# lambda is placed.
safe_foo = RescueAllTheThings.new(raw_foo, lambda { |e| puts "Caught an exception: #{e.class}: #{e.message}" })
# No need to rescue anything, it's all handled!
safe_foo.bar
puts "Look ma, I didn't exit!"
Whether it makes sense to use a very generic version of a wrapper class, such as the RescueAllTheThings class above, or something more specific to the thing you're trying to wrap will depend a lot on the context and the specific issues you're looking to solve.
I have seen Ruby code that raises exceptions using a class:
raise GoatException, "Maximum of 3 goats per bumper car."
Other code uses an instance:
raise GoatException.new "No leotard found suitable for goat."
Both of these are rescued the same way. Is there any reason to use an instance vs a class?
It makes no difference; the exception class will be instiantiated in either case.
If you provide a string, either as the argument to new or as the second argument to raise, it be passed to initialize and will become the exception instance's .message.
For example:
class GoatException < StandardError
def initialize(message)
puts "initializing with message: #{message}"
super
end
end
begin
raise GoatException.new "Goats do not enjoy origami." #--|
# | Equivilents
raise GoatException, "Goats do not enjoy origami." #--|
rescue Exception => e
puts "Goat exception! The class is '#{e.class}'. Message is '#{e.message}'"
end
If you comment the first raise above, you'll see that:
In both cases, initialize is called.
In both cases, the exception class is GoatException, not class as it would be if we were rescuing the exception class itself.
After I have created a serious bunch of classes (with initialize methods), I am loading these into IRb to test each of them. I do so by creating simple instances and calling their methods to learn their behavior. However sometimes I don't remember exactly what order I was supposed to give the arguments when I call the .new method on the class. It requires me to look back at the code. However, I think it should be easy enough to return a usage message, instead of seeing:
ArgumentError: wrong number of arguments (0 for 9)
So I prefer to return a string with the human readable arguments, by example using "puts" or just a return of a string. Now I have seen the rescue keyword inside begin-end code, but I wonder how I could catch the ArgumentError when the initialize method is called.
Thank you for your answers, feedback and comments!
It is possible to hook into object creation by overriding the Class#new method e.g.
class Class
# alias the original 'new' method before overriding it
alias_method :old_new, :new
def new(*args)
return old_new(*args)
rescue ArgumentError => ae
if respond_to?(:usage)
raise ArgumentError.new(usage)
else
raise ae
end
end
end
This overriden method calls the normal new method but catches ArgumentError and if the class of the object being created provides a usage method then it will raise an ArgumentError with the usage message otherwise it will reraise the original ArgumentError.
Here is an example of it in action. Define a Person class:
class Person
def initialize(name, age)
end
def self.usage
"Person.new should be called with 2 arguments: name and age"
end
end
and then try and instantiate it without the required arguments:
irb(main):019:0> p = Person.new
ArgumentError: Person.new should be called with 2 arguments: name and age
from (irb):8:in `new'
from (irb):22
Note: this isn't perfect. The main problem being that it is possible that the ArgumentError we catch has been caused by something other than an incorrect number of arguments being passed to initialize which would lead to a misleading message. However it should do what you want in most cases.