What Does the Fail Keyword Do in Ruby

What does the fail keyword do in Ruby?

In Ruby, fail is synonymous with raise. The fail keyword is a method of the Kernel module which is included by the class Object. The fail method raises a runtime error just like the raise keyword.

The fail method has three overloads:

  • fail: raises a RuntimeError without an error message.

  • fail(string): raises a RuntimeError with the string argument as an error message:

    fail "Failed to open file"
  • fail(exception [, string [, array]]): raises an exception of class exception (first argument) with an optional error message (second argument) and callback information (third argument).

    Example: Assume you define a function which should fail if given a bad argument. It is better to raise an ArgumentError and not a RuntimeError:

    fail ArgumentError, "Illegal String"

    Another Example: You can pass the whole backtrace to the fail method so you can access the trace inside the rescue block:

    fail ArgumentError, "Illegal String", caller

    caller is a Kernel method which returns the backtrace as an array of strings in the form file:line: in 'method'.

With no arguments, raises the exception in $! or raises a RuntimeError
if $! is nil. With a single String argument, raises a RuntimeError
with the string as a message. Otherwise, the first parameter should be
the name of an Exception class (or an object that returns an Exception
object when sent an exception message). The optional second parameter
sets the message associated with the exception, and the third
parameter is an array of callback information. Exceptions are caught
by the rescue clause of begin...end blocks.

Source: Ruby Documentation on the Kernel Module.

Fail vs. raise in Ruby : Should we really believe the style guide?


use 'raise' for exceptions to be caught, and 'fail' for serious errors which are not meant to be handled

This is not what the official style guide or the link you provided say on the matter.

What is meant here is use raise only in rescue blocks. Aka use fail when you want to say something is failing and use raise when rethrowing an exception.

As for the "does it matter" part - it is not one of the most hardcore strictly followed rules, but you could make the same argument for any convention. You should follow in that order:

  1. Your project style guide
  2. Your company style guide
  3. The community style guide

Ideally, the three should be the same.


Update: As of this PR (December 2015), the convention is to always use raise.

Need 'return' statement following 'fail' call in a Ruby method?

No, you don't need the return.

def do_something
puts "start"
fail NotAuthenticatedError
puts "this doesn't print"
end

That code will never get to the last line.

Check out:

what-does-the-fail-keyword-do-in-ruby

Why are tests failing in Ruby on Rails?

You're mixing up two different kinds of tests.

The first is functional controller tests in the form of ActionController::TestCase which you probably have in your book. These are tests that create a mocked request object and pass it to an instance of the controller. The use of these is greatly discouraged outside of legacy apps since they tend to let tons of bugs through. In functional tests you would write post :create, params: { product: { ... }} because you are actually calling the create method on the controller instance.

The second is integration tests in the form of ActionDispatch::IntegrationTest where you send actual HTTP requests to your Rails application. This is the modern approach and it's just as fast as functional tests in modern versions of Rails which was not the case when the book was written.

In an integration test you pass a path or url:

post '/products', params: { product: ... }

A second issue is the mandatory separation the of keyword parameters that was introduced in Rails 5.

In previous versions of Rails you could call:

get '/foo', bar: 1, baz: 2

And Rails would treat any unknown keys (not params, headers, format, etc) as parameters which was the source of many bugs. In Rails 5+ you explicitly need to pass parameters in the params option.

get '/foo', params: { bar: 1, baz: 2 }

I would suggest you complement the book with some more modern sources or get a newer edition as quite a lot has changed since Rails 4 and following a book that old in Rails 6 is going to be painful.

Why would Ruby fail equality on 2 floats that appear the same?

Floating-point numbers cannot precisely represent all decimal numbers within their range. For example, 0.9 is not exactly 0.9, it's a number really close to 0.9 that winds up being printed as it in most cases. As you do floating-point calculations, these errors can accumulate and you wind up with something very close to the right number but not exactly equal to it. For example, 0.3 * 3 == 0.9 will return false. This is the case in every computer language you will ever use — it's just how binary floating-point math works. See, for example, this question about Haskell.

To test for floating point equality, you generally want to test whether the number is within some tiny range of the target. So, for example:

def float_equal(a, b)
if a + 0.00001 > b and a - 0.00001 < b
true
else
false
end
end

You can also use the BigDecimal class in Ruby to represent arbitrary decimal numbers.

If this is a test case, you can use assert_in_delta:

def test_some_object_total_is_calculated_correctly
assert_in_delta 22.23, some_object.total, 0.01
end

Why is yield not passing the result to block (Rails)?

In your controller you need to have a variable for the result.

def create
Command.run(params) do |result|
if result
render json: { message: 'Successfully processed request' }, status: :success
else
render json: { message: 'Encountered an error' }, status: :bad_request
end
return
end
render json: { message: 'Encountered an error' }, status: :bad_request
end

(EDIT)

Also, you are calling the class method which call the instance method. You have to pass the block from the calling code to the instance method you are calling.

def self.run(params, &block)
new.run(params, &block)
end

Why does this Ruby code using char.upcase! fail but char = char.upcase works?

This is due to the definition of upcase! and downcase!. Here's upcase!

Upcases the contents of str, returning nil if no changes were made. Note: case replacement is effective only in ASCII region.

Notice that it returns nil if no changes were made. In this case, if the character you are iterating over is already in the correct case, it'll return nil and effectively remove the character from the string as part of your map function.

In your specific example, you'll notice every other character is being removed because it's already in lower case and downcase! is returning nil.

loop next if fail

You can continue beyond the first failed connection attempt by handling the exception raised by Net::Telnet::initialize. You'll find you aren't getting the error you think you're getting:

begin
tn = Net::Telnet::new("Host" => "#{hostname}",
"Timeout" => false,
"Prompt" => /[$%#>] \z/n)
rescue => e
puts "Unable to connect to #{hostname}: #{e.message}"
next
end

Produces

Unable to connect to host1: getaddrinfo: nodename nor servname provided, or not known

That is, you're not actually providing a host name. The argument isn't called "Host", it's called "host", and case matters. Ruby options are also typically passed as symbols, not strings. Try this:

  tn = Net::Telnet::new(:host    => "#{hostname}",
:timeout => false,
:prompt => /[$%#>] \z/n)

Now you'll get the error you were expecting:

Unable to connect to host1: Connection refused - connect(2)

Rspec: How do I make my passing tests fail?

create_client raises the ArgumentError, because it expects three keyword arguments and you are passing only one: (maybe you should have tested your helper, too)

def create_client(access_token:, access_key:, environment:)
# intentionally left empty
end

create_client(access_key: nil)
# in `create_client': missing keywords: access_token, environment (ArgumentError)

You could use default values in your helper to overcome this:

def create_client(access_token: :foo, access_key: :bar, environment: :baz)
Contentstack::Client.new(access_token: access_token, access_key: access_key, environment: environment)
end

create_client(access_key: nil)
# in `validate_configuration!': You must specify an access_key (ArgumentError)

Finally, you could be more specific regarding the error message:

expect { ... }.to raise_error(ArgumentError, 'You must specify an access_key')


Related Topics



Leave a reply



Submit