I'm currently learning Ruby and doing so by reading the popular book "The well-grounded Rubyist". I do understand code blocks pretty decently, or so I thought, until I hit this code example from the book on page 191:
open_user_file do |filename|
fh = File.open(filename)
yield fh
fh.close
rescue
puts "Couldn't open your file"
end
Now the thing I do no quite get here is, whom do I yield to when yielding within a code block? The way I understood it is that if you call a method that can yield and you provided a code block, the method will yield to your code block (maybe even with arguments), your code block executes and then gives control back to the method. But here in this code example we do not yield within the method, but in the code block. Could someone explain to me how this works and how a construct like this may look like? Any clarifications are appreciated!
(P.S. please don't tell me "You shouldn't do this". I am not asking because I want to do this in production code, I merely want to understand the inner workings of Ruby in-depth.)
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.)
Related
Learning ruby here, how can I use if an error was raised to control an if/else statement. In ruby pseudocode it would be something like.
if block.call raise?
#if block doesn't exist and an error is raised do this
else
#if block does exist do this
end
You could use an inline rescue like this:
if block.call rescue false
else
end
Though you shouldn't do this. The reason being that this will rescue any error in the block. Say you you mispelled some word in the block definition. Your code will act like this is intended, and you might have a hard time tracking it down.
In this case, to check if the block was given you can simply say if block. If you're using yield instead of the &block parameter, you can use the special method if block_given?.
Of course you can achieve conditional logic using rescues, but that's not what they're intended for, and it's not making things much simpler in most cases.
Ruby Koans has the following exercise in about_blocks.rb:
def method_with_block_arguments
yield("Jim")
end
def test_blocks_can_take_arguments
method_with_block_arguments do |argument|
assert_equal __, argument
end
end
I know the answer is assert_equal "Jim", argument, but I'm struggling to understand what is happening. Specifically:
Is argument or assert_equal... the block?
What is yield doing given that method_with_block_arguments returns "Jim" without yield?
I think some of the above commenters are correct in saying that you currently don't have a very deep understanding of Ruby, but don't let that discourage you. It just takes time to learn. When I was first learning Ruby, the concept of blocks and their syntax did take some time to wrap my head around. Once you get it the syntax is very simple, but you until you reach that point...
Anywho, this is my attempt to help you out. :)
argument is a block variable. All the stuff between do and end is the block. assert_equal is just a regular method call, nothing to do with blocks.
What yield does is the key to understanding how blocks work. What yield does it that it "yields" control to the calling function. You may think of it as a callback. When you say "yield" in the middle of a function, you are essentially saying "in the middle of this function, I want to allow someone else to plug in their code and make decisions about what should happen." If you use yield with no arguments, no data from your method gets passed back to the caller.
In essence, yield is a way of "yielding" control to somebody else, in this case the caller of your function.
When you call yield with one or more arguments, you are passing data from the your function back up to the caller. So when you say yield("Jim") you are handing the String "Jim" back to whoever calls method_with_block_arguments.
Lastly, you have to understand that in Ruby, methods always return the result of whatever was the last expression in a particular method. That's why you usually don't need an explicit return statement.
For instance, this method will return 42.
def foo
42
end
That's because 42 is a valid expression in Ruby. It's just an identity, but it's valid Ruby, so Ruby just says "okay, you said 42 and that's the last thing in this method declaration. So when people call 'foo' they get 42 back".
I hope this helps. I think at this point you should assume that you're still pretty early on in terms of your Ruby learning, but you're on the right track investigating blocks. Once you get them you'll understand one of the most powerful parts of Ruby.
Is argument or assert_equal... the block?
No, neither argument nor assert_equal is a block, argument is the variable and anything between do and end is the block. assert_equal is a normal method call.
What is yield doing given that method_with_block_arguments returns "Jim" without yield?
Yield is what makes it special. It calls the block (ie. everything between do and end) and executes it. "Jim" is the argument to the block.
Here is a gist that I copied from Paul while I was learning ruby. That should help in learning about closures in ruby.
i manage to do "something"(like deleting files,etc) when exit or exit! is called from Object.
by changing the method exit and exit! inside Object class. (at_exit is too unreliable)
but then the "something" never execute if there's error such as NameError, etc.
is there way so i can make "something" that still execute if there's error.
(any error possible if necessary).
something like at_exit but works with all errors
thanks in advance for assistance. and forgive me if there's already question ask for this.
i do search a lot before asking here. and found nothing.
edit: i don't know where the original author of the code place the method. since the original author load it from dll files in the exe program the author used for launcher. (i can only edit After the original code take place...). so i think i need another approach for this... but i manage to make a workaround for my problem... by putting begin rescue in other places that send data to the original object. i filter the data send and throw my own error before it reach the main program... so i guess this works too.
Borrowing from an answer on a different thread, and definitely along the lines of what Marek commented, this is how you should handle errors in Ruby:
begin
# something which might raise an exception
rescue SomeExceptionClass => some_variable
# code that deals with some exception
rescue SomeOtherException => some_other_variable
# code that deals with some other exception
else
# code that runs only if *no* exception was raised
ensure
# ensure that this code always runs, no matter what
end
Original credit: Begin, Rescue and Ensure in Ruby?
I am working with a custom testing framework and we are trying to expand some of the assert functionality to include a custom error message if the assert fails. The current assert is called like this:
assert_compare(first_term, :neq, second_term) do
puts 'foobar'
end
and we want something with the functionality of:
assert_compare(first_term, :neq, second_term, error_message) do
puts 'foobar'
end
so that if the block fails the error message will describe the failure. I think this is ugly, however, as the framework we are moving away from did this and i have to go through a lot of statements that look like:
assert.compare(variable_foo['ARRAY1'][2], variable_bar['ARRAY2'][2], 'This assert failed because someone did something unintelligent when writing the test. Probably me, since in am the one writing this really really long error statement on the same line so that you have to spend a quarter of your day scrolling to the side just to read it')
This type of method call makes it difficult to read, even when using a variable for the error message. I feel like a better way should be possible.
assert_compare(first_term, :neq, second_term) do
puts 'foobar'
end on_fail: 'This is a nice error message'
This, to me, is the best way to do it but i don't know how or if it is even possible to accomplish this in ruby.
The goal here is to make it as aesthetic as possible. Any suggestions?
You could make on_fail a method of whatever assert_compare returns and write
assert_compare(first_term, :neq, second_term) do
puts 'foobar'
end.on_fail: 'This is a nice error message'
In short, no. Methods in ruby take a block as the final parameter only. As Chuck mentioned you could attempt to make the on_fail method a method of whatever assert_compare returns and that is a good solution. The solution I've come up with is not what you are looking for, but it works:
def test block, param
block.call
puts param
end
test proc { puts "hello"}, "hi"
will result in
"hello"
"hi"
What I've done here is create a Proc (which is essentially a block) and then passed it as a regular parameter.
I have recently tried sharpening my rails skills with this tool:
http://github.com/edgecase/ruby_koans
but I am having trouble passing some tests. Also I am not sure if I'm doing some things correctly since the objective is just to pass the test, there are a lot of ways in passing it and I may be doing something that isn't up to standards.
Is there a way to confirm if I'm doing things right?
a specific example:
in about_nil,
def test_nil_is_an_object
assert_equal __, nil.is_a?(Object), "Unlike NULL in other languages"
end
so is it telling me to check if that second clause is equal to an object(so i can say nil is an object) or just put assert_equal true, nil.is_a?(Object) because the statement is true?
and the next test:
def test_you_dont_get_null_pointer_errors_when_calling_methods_on_nil
# What happens when you call a method that doesn't exist. The
# following begin/rescue/end code block captures the exception and
# make some assertions about it.
begin
nil.some_method_nil_doesnt_know_about
rescue Exception => ex
# What exception has been caught?
assert_equal __, ex.class
# What message was attached to the exception?
# (HINT: replace __ with part of the error message.)
assert_match(/__/, ex.message)
end
end
Im guessing I should put a "No method error" string in the assert_match, but what about the assert_equal?
assert_equal true, nil.is_a?(Object) is indeed the correct solution. The question is "Are nils in Ruby objects or not?", and in Ruby's case, they are. Thus, in order to pass the assertion, you should assert the truth of that test.
In the second example, when you call an undefined method on nil, you get NoMethodError: undefined method 'foo' for nil:NilClass. Thus, the exception class is NoMethodError, and the message is undefined method 'foo' for nil:NilClass. Test the failing behavior in a console, and see what you get from it, and then apply that knowledge to the test.
Are you running
ruby path_to_enlightenment.rb
at the command prompt after you correct each test? It will give you lots of help.
Also "remember that silence is sometimes the best answer" -- if you are stumped don't put in anything and the tool will help you.
Well, in holding with the typical TDD motto of Red-Green-Refactor, you should run the test (probably with rake in a separate console) and see the failure happen. From there, they have provided you a few pieces of information about what was expected.
As for style, the koans aren't really teaching that. You should just find and read some code written in ruby to get a feel for the typical conventions and idioms of the ruby community.
Simplicity is the key with Ruby Koans - when I started it I thought it must be harder than what it is, but it's not! Just ask IRB the question Koans is asking you, and after a few you get a feel for it. I've written a blog piece about it to help others, too:
Ruby Koans Answers
I remember when I did this that I tried to out think the test and tried to put in
<Answer> and <"Answer">
The thing to remember is that the actual class doesn't have to be in a string or something.
So the answer is NOT
ex.class, ex.class
As suggested above, put the code into irb and execute it.
(1..5).class == Range
is a big hint