How can I "close" an enumerator in Ruby? - ruby

I have an enumerator that loops on data from an external API. The API requests that I notify it when I'm done looping. This is pretty easy when the enumerator is allowed to go until it runs out of data:
def api_enum
return enum_for(:api_enum) unless block_given?
loop_on_api_calls { |thing| yield thing }
notify_api_that_i_am_done
end
But what about this case?
api_enum.each do |thing|
do_stuff(thing)
break
end
The break means I'm never going to call notify_api_that_i_am_done. How could I structure this to guarantee that notify_api_that_i_am_done gets called? Is there a good pattern to for this?

You can just call break with an argument:
api_enum.each do |thing|
do_stuff(thing)
break(notify_api_that_i_am_done)
end

In your api_enum method, you can use ensure to always execute cleanup code, regardless of any exception being thrown or the user breaking. This is a good idea to use for any cleanup code since only tht way you can ensure that the it us run even if something raises an exception in the user-supplied block.
This can be used like this:
def api_enum
return enum_for(:api_enum) unless block_given?
loop_on_api_calls { |thing| yield thing }
ensure
notify_api_that_i_am_done
end
With that pattern, you don't need to follow any special call conventions.

Related

How do next, break, redo, and return work from the block caller's perspective?

Because Ruby relies so heavily on the use of blocks for iteration (while and for loops do exist in Ruby, but they're very rarely used by Ruby developers in practice) Ruby allows some keywords often associated with loops in other languages, such as break, next, and redo, to be used inside of blocks. return in Ruby also returns from the method the block is inside, which is consistent with the behavior of a loop, instead of from the block itself as functional programming languages do. This answer explains those behaviors quite nicely.
Now what I'm wondering is how this behavior works from the block caller's perspective (by block caller, I mean the method the block is being called from, as opposed to the method the block is defined in). For some of these keywords it's pretty self-explanatory (e.g. next simply returns from the block, and redo calls the block again with the same arguments), but for others the exact behavior is unclear to me. Specifically...
When I call break from a block, does execution immediately get handed to the point after the block is defined? If I have some post-iteration cleanup steps to perform from within the iterator method, do they get completely skipped? E.g.:
def iterate
setup
1.upto(5) do |x|
yield x
end
cleanup # Does this get called? If not, how can I ensure that it does?
end
iterate do |x|
puts x # Prints 1
break # Break prevents further looping, somehow
end
# Execution continues here after break
What about with return? I would assume it has similar behavior to that of break in this context (whatever that behavior may be). Is that indeed the case?
What's even more confusing though is that Ruby blocks can be captured as method parameters and converted to Proc objects. These objects can then be called from anywhere, even outside the method that the block was originally defined in:
def get_proc(&block)
block
end
def get_iterator_proc
p = get_proc do |x| # Block is defined here...
puts x
break
end
# ...so `break` makes execution continue here...
do_other_stuff
return p
end
p = get_iterator_proc
p.call # ...even though the block isn't actually called until here?
And again, the same goes for return. What does it actually return from in this context? How do these keywords really work?
def iterate
setup
1.upto(5) do |x|
yield x
end
cleanup # Does this get called? If not, how can I ensure that it does?
end
iterate do |x|
puts x # Prints 1
break # Break prevents further looping, somehow
end
# Execution continues here after break
cleanup # Does this get called?
Yes. However, if there is an exception, then exception handling takes precedence.
cleanup # If not, how can I ensure that it does?
You can use ensure without a rescue, for when there is an exception.
break # Break prevents further looping, somehow
I'd suspect it uses throw as that is how Enumerable works.
What about with return? I would assume it has similar behavior to that of break in this context (whatever that behavior may be). Is that indeed the case?
It depends on the context. You might get a LocalJumpError: unexpected return or it might work as a break
def get_proc(&block)
block
end
def get_iterator_proc
p = get_proc do |x| # Block is defined here...
puts x
break
end
# ...so `break` makes execution continue here...
Effectively, but there is a subtle difference. It is more like your first example.
def get_proc(&block)
block
# ...so `break` makes execution continue here...
end
def get_iterator_proc
p = get_proc do |x| # Block is defined here...
puts x
break
end
# get_proc returned here (after assigning to p)
Remember, get_proc is just a function call with a special syntax to pass functions.
if you had
def get_proc(&block)
block
7
end
Then in get_iterator_proc p would be 7 (And we'd be sad that 7 didn't have a call method )

Ruby Code Kata: LocalJumpError: yield called out of block

I'm trying to work on a simple ruby kata. I don't really know the concept of yield so I don't really understand what this error means:
LocalJumpError: yield called out of block
Essentially I'm just trying to make this these test cases pass with this code:
def compute
return yield
"Do not compute"
end
Test.expect(compute { "Block" }, "Block")
Test.expect(compute, "Do not compute")
The first test case passes but the second doesn't. Shouldn't it pass though. The return yield should stop the function early with the block. If the method call does not have a block then it should go straight towards "Do not compute" Right?
You want to use block_given?
def compute
return yield if block_given?
"Do not compute"
end
yield always expects a block to yield to, so you have to prevent the yield from occurring yourself if you don't have a block. Thankfully, there's the inbuilt method, block_given? that can tell you whether there's been a block provided or not.
Try removing the return statement.
Once you call return, you exits the method, so the "Do not compute" statement is never reached.

How to return true if one statement in a block returns true in Ruby?

Is it possible to create a method in Ruby that takes a block and runs each statement in the block until one of them returns false?
done = prepare_it do |x|
method_1
method_2
method_3
end
puts "Something did not work" unless done
I wanted the function prepare_it to run each statement but if one of them fails, the function quits. If the function returns true, it means that all steps were successful.
This seems to be calling for an Exception, but I'm not sure how to trigger and process it. I wish I did not have to modify the method_x functions to throw an Exception upon failure, but rather make prepare_it throw the exception if one of the method_x fails (returns false)
Edit:
Thanks for the suggestions, I think I need to add more details to my question. This is my first stab at metaprogramming and creating a DSL. :)
I have a class to send commands to a router. There are many steps that always need to be executed in sequence (connect, login, pass, etc). I thought it would be nice to give the user the ability to change the order of the commands if they wish to do so. For example, some routers don't ask for a user and go directly to the password prompt. Others skip the login altogether. In some cases you need to elevate the privilege or set terminal options.
However, there should be a mechanism to fail the entire block if any of the commands failed. So, if for some reason the password failed (the method returns false/nil), it makes no sense to continue with the rest of the commands.
And I should flag it that something failed.
The && method works, but I don't think it would be a nice interface.
Maybe instead of getting a big block, maybe I should force users to give me smaller blocks, one at a time, which are put in a stack and a run command yields then one by one?
my_router.setup do {send_pass}
my_router.setup do {set_terminal}
my_router.setup do {enable_mode}
my_router.run_setup
I think it would be super cleaner if I could do
my_router.setup do |cmd|
cmd.send_pass
cmd.set_terminal
end
puts "Done" if my_router.ready?
So any magic trickery that happens behind the scene is welcome! :)
Solution
done = prepare_it do |x|
method_1 && method_2 && method_3
end
puts "Something did not work" unless done
Explanation
The && operator "short-circuits" so if method_1 returns false, 2 and 3 won't be called and done will be false.
Example
http://gist.github.com/1115117
There isn't a good way to hack things to work like that. I would do something like one of the following:
done = prepare_it do |x|
method_1 && method_2 && method_3
end
Now, in some specific cases, you can do magic trickery to make this work. For instance, if the methods are supposed to be called on the block argument x: you could do something like this:
class SuccessProxy
def initialize(obj)
#object = obj
end
def method_missing(meth, *args, &block)
#object.send(meth, *args, &block) || raise SuccessProxy::Exception
end
class Exception < ::Exception
end
end
def prepare_it
# however you usually generate x
yield SuccessProxy.new(x)
rescue SuccessProxy::Exception
false
end
prepare_it do |x|
x.method_1
x.method_2
x.method_3
end
Or alternatively, if the methods are to be called just like method_1 on the default object in context, you could use instance_eval instead of yield to put the proxy in scope.
Finally, if you really wanted to be fancy, you could actually parse the separate statements out using a Ruby parser.
But honestly, I'm not convinced you really want to be going to metaprogramming as a tool for this. Maybe if you provide better detail, I can be more helpful as to what the best solution is, but it's probably similar to the first code sample above.
Naturally compactness will be your highest priority, so I believe this is the best answer:
done = (1..3).all? { |n| send "method_#{n}" }
puts "Something did not work" unless done
If you use exceptions, it's not as pretty, but you don't have to rely on returning values:
done = prepare_it do |x|
begin
method_1
method_2
method_3
true
rescue
false
end
end
puts "Something did not work" unless done
def method_1
# No issues, doesn't matter what we return
return true
end
def method_2
# Uh-oh, problem
raise
end
At first I thought, "wouldn't it be nice if this was Lisp..." but that made me realize the exact problem. Doing what you want (executing each step in a block until one is false) would be nice, but requires the access to the AST.
Your idea to do
my_router.setup do {send_pass}
my_router.setup do {set_terminal}
my_router.setup do {enable_mode}
my_router.run_setup
is trying to do exactly what you would do in Lisp--build a list and hand the list off to something else to execute each thing until you get a false return value.
If you're just calling methods in your class without any arguments, then how about as a workaround defining something that takes symbols?:
class Router
def run_setup *cmds
# needs error handling
while c = cmds.shift
return false unless send(c)
end
end
end
my_router.run_setup :send_pass, :set_terminal, :enable_mode

How do you use Ruby blocks to conditionally execute something?

I recently purchased the book Seven Languages in Seven Weeks and have been reading through the chapter on Ruby. In the section which introduces blocks (page 40), a code sample is given which illustrates the use of blocks for the purpose of conditionally executing something:
in_case_of_emergency do
use_credit_card
panic
end
def in_case_of_emergency
yield if emergency?
end
This code doesn't make much sense to me, and the book doesn't provide much of an explanation. I was wondering if one of you Ruby gurus would mind helping me get my head around this one.
How can you have both a block and a function with the same name? How would you define "emergency?" I can't even create the block in IRB without it complaining:
NoMethodError: undefined method `in_case_of_emergency' for main:Object
from (irb):1
from :0
And how would you invoke this code to demonstrate how it works? Thanks!
First off: the two are in the wrong order. You need to define in_case_of_emergency first.
Second: You don't name blocks; therefore, it is incorrect that there are two things named in_case_of_emergency. One is a function definition, while the second is the function invocation of that same function.
So, step-by-step:
def emergency?
return rand(2) == 0
end
Let's say you have this function that returns true half the time, and false half the time, randomly. (Boy, that's a lot of emergencies!) Then:
def in_case_of_emergency
yield if emergency?
end
This defines a function named in_case_of_emergency. When called, it executes yield if emergency?, which is a statement yield modified by the conditional if. This is syntactic sugar for
if emergency?()
yield
end
Note that Ruby does not require brackets for function invocation, thus we can drop them; and if there is only one statement inside an if, you can write it on the same line as above (obviating the need for an end).
Next, we have a function invocation:
in_case_of_emergency do
use_credit_card
panic
end
This calls the function we just defined, in_case_of_emergency, passing it a block to execute. These are the two statements (use_credit_card, panic) that would be executed by yield - but only if the emergency? evaluates to true.
Does this make more sense now?
that's simple, that is the method:
def in_case_of_emergency
yield if emergency?
end
and that is the call to your method:
in_case_of_emergency do
use_credit_card
panic
end
wheres
do
use_credit_card
panic
end
is an argument. Ruby methods can implicitly take blocks as arguments. What happens is that
yield will execute the block you provided. In your case yield if emergency? means "execute provided block if conditions are met".
Adding to other answers, it may be easier to understand blocks if you drop the yield keyword and treat blocks in methods like the Procs they actually are.
# for testing purposes, let's always have `emergency?` return `true`
def emergency?
true
end
def in_case_of_emergency(&block)
block.call if emergency?
end
in_case_of_emergency do
puts "AHH! Emergency! Help!", "Seriously, I'm freaking out!"
end
See how much easier it is to realize that the block is actually an argument?

How to mock method with expectation on what is passed to the block

Lets say we have a class:
class ObjectWithCaching
def cached_attribute(key, cache_handler)
cache_handler.cache(key) { expensive_operation }
end
def expensive_operation
#...
end
end
And we already tested cache_handler so we know that it will execute the block only when key is not found in cache.
We want to test if cache_handler#cache is executed properly.
Question is: How to write remaining pending spec?
describe ObjectWithCaching, "#cached_attribute" do
let(:key) { double }
let(:cache_handler) { double }
specify do
cache_handler.should_receive(:cache).with(key)
subject.cached_attribute(key, cache_handler)
end
it "passes #expensive_operation to block of cache_handler#cache" do
pending
subject.cached_attribute(key, cache_handler)
end
end
Here's what I'd do. It feels kind of dirty to be mocking out another method on the same object (might expensive_operation belong on another class?), but given the constraints, I think this is the way to go. It'd sure be nice to be able to pass functions around directly and just check for function equality like you can in Clojure :)
it "passes #expensive_operation to block of cache_handler#cache" do
cache_handler.stub!(:cache) do |k, block|
subject.should_receive(:expensive_operation)
block.call
end
subject.cached_attribute(key, cache_handler)
end

Resources