Optionally calling a block in ruby - ruby

I have two ways I might need to call some code using a block.
Option 1:
foo()
Option 2:
block_function do
foo()
end
How do I switch between the two of these at runtime? I really don't want to do the following, because foo() is actually a whole lot of code:
if condition then
foo()
else
block_function do
foo()
end
end

def condition_or_block_function
if condition
yield
else
block_function { yield }
end
end
condition_or_block_function do
foo() # which is really a lot of code :)
end
Or as others suggested, make the foo() bunch of code an actual method and write what you wrote in the OP.
More generic version as #tadman suggests:
def condition_or_block condition, block_method, *args
if condition
yield
else
send(block_method, *args) { yield }
end
end
condition_or_block(some_condition, some_block_yielding_method) do
foo() # which is really a lot of code :)
end
#Christian Oudard added a comment specifying the specific problem, optionally decorating a code block with div do...end with Erector. This suggests another approach:
class BlockWrapper
def initialize(method=nil)
#method = method
end
def decorate
#method ? send(method) { yield } : yield
end
end
wrapper = BlockWrapper.new( condition ? nil : :div )
wrapper.decorate do
#code block
end

Related

Proc.new binding change and loops logic abstraction

I've two loops in two different methods which look very similar. I wanted to abstract most of their logic in a Proc.new
This works
def matches_base?
proc_exec = Proc.new do |subclass, breakpoint|
# next and return are meant to act inside the loop and quit it if needed
response = process_match(subclass)
next if response == :continue
return true if response == false
return response
end
subclasses(BASE_NAMESPACE).each do |subclass|
proc_exec.call(subclass)
end
false
end
The obvious issue here is the proc_exec is defined inside the method itself, but I want to use it in another method
def matches_breakpoints?
breakpoints.fetch.each do |breakpoint|
# I want to include the proc_exec here too
end
false
end
So I just tried to extract it at the class level like so
This does not work
def proc_exec
Proc.new do |subclass, breakpoint|
response = process_match(subclass)
next if response == :continue
return true if response == false
return response
end
end
def matches_base?
subclasses(BASE_NAMESPACE).each do |subclass|
proc_exec.call(subclass)
end
false
end
Then I could have called it like proc_exec.call from within both instance methods. Currently it throws
LocalJumpError:
unexpected return
I tried many tricks such as instance_eval or instance_exec without success. I'm out of solution right now.
Easily executable, simplified example of what I want below.
class MyLoops
def proc_exec
Proc.new do |num|
next if num == 1
# we want this `return` to act in the method context
# as it would do if I defined it inside a method directly
return if num == 4
puts "Current number : #{num}"
end
end
def method_a
[0,1,2].each do |num|
proc_exec.call(num)
end
false
end
def method_b
[3,4,5].each do |num|
proc_exec.call(num)
end
end
# this `false` below should never be reached ; that's the trick
false
end
loops = MyLoops.new
loops.method_a
loops.method_b
You can't have your cake and eat it too. If you want return from the proc to abort the method, it must be in the method's lexical scope* (which is another way to say "it must be defined within the same method").
An alternative is to have proc/lambda return a "stop" value, which caller will use to abort its execution.
(Your experiments with instance_eval/instance_exec were misdirected, sadly. Those methods only change current self. This problem has nothing to do with current self, but rather current lexical scope, in which return is executed.)
* The error you're getting, it is caused by return trying to return from a method that is no longer running (proc_exec).

Understanding the control flow using `each` and `yield`

I came across this code:
class RandomSequence
def initialize(limit,num)
#limit,#num = limit,num
end
def each
#num.times { yield (rand * #limit).floor }
end
end
i = -1
RandomSequence.new(10,4).each do |num|
i = num if i < num
end
Is it the case that the each method is called only once and will compute four different values, and then for each of those values we execute code block between do and end? Is my understanding of control flow correct?
Your understanding is close. The random number will be generated, then a block yielded, then another generated, etc 4 times. You can verify this easily by adding puts statements into you blocks to see when they are executed.
class RandomSequence
def initialize(limit,num)
#limit,#num = limit,num
end
def each
puts "in each"
#num.times { yield (rand.tap {|x| puts "Generated #{x}" } * #limit).floor }
end
end
i = -1
RandomSequence.new(10,4).each do |num|
puts "in block"
i = num if i < num
end
Outputs
in each
Generated 0.6724385316643955
in block
Generated 0.8906983274750662
in block
Generated 0.49038868732214036
in block
Generated 0.38100454011243456
in block
The each method on the RandomSequence class is called once. In it #num.times which creates an Enumerator. The Enumerator is iterated over and the block with the yield statement is called (with the argument to it ignored).
The yield statement calls the block that's passed to the each method passing the value of (rand * #limit).floor. In your code the block is not bound to a variable, i.e you could get a reference to the block by doing:
def each(&block)
#... do stuff with block, e.g. block.call("some args")
end
which can be useful at times.
A bit off topic, but one thing I found scary with Ruby starting out is that a return statement returns the flow of execution from where it was defined.
def create_proc
puts "Creating proc"
Proc.new do
puts "In proc!"
return "some value" # notice the explicit return
end
end
def do_stuff
my_proc = create_proc
my_proc.call # This will cause a runtime error
end
If the explicit return is removed everything works there is no error... Lesson being that in ruby you should probably avoid using explicit returns.

How to force a LocalJumpError on return in Ruby?

My function bar gets a block, and I'd like to run this block without allowing it to return. How do I do that by modifying only bar below, and keeping everything else, including foo, intact?
def bar()
yield
end
def foo()
bar do
p "it"
return # This works. But I'd like to get LocalJumpError.
end
end
foo
Well, you get a LocalJumpError when you try and pass a block with a return to a Proc (as opposed to a lambda).
You can get away with not changing foo except for how it's called, if you do something like
def bar()
Proc.new
end
def foo()
bar do
p "it"
return
end
end
foo[]
That gives a LocalJumpError.
Anyway, this article might help.
Edit: A return in a proc will return from the outer method, not from the anonymous method in the block. You might be able to set a flag that you can check in bar to see if it returned prematurely:
bar_finished = false
def bar(&block)
proc = Proc.new &block
l.call
bar_finished = true
end
Then, if a return is in the block passed to bar, bar_finished will still be false. Not sure if adding a non-local variable like this is an option, but if so, you could track returns from the block and throw whatever exception you want if it happens.
If there's some "cleaning up" code you want done after yield, then you may want to use begin and then ensure.
Based on suggestions in other answers, I have managed to solve the problem:
def bar()
has_returned = true
begin
x = yield
has_returned = false
x
rescue
has_returned = false
raise
ensure
raise LocalJumpError if has_returned
end
end
def foo()
bar do
p "it"
return # This makes bar raise a LocalJumpError.
end
end
foo

What's the difference between these two Ruby snippets?

Snippet 1:
module A
def cm(m,ret)
class_eval do
define_method(m.to_sym) do
return ret
end
end
end
end
and snippet 2:
module B
def cm(m,ret)
class_eval do
"def #{m} #{ret} end"
end
end
end
The methods defined in these modules are to be used to create methods on a class, that returns certain values. Here's an example:
class Whatever
extend A
cm("two",2)
end
and this will create a method named 2, that will return 2. The thing is, the code in the second snippet does not work. Any ideas why? I thought class_eval could take a string.
class_eval takes a string as an argument, but you've passed the string to the function in a block.
Try this instead:
module B
def cm(m,ret)
class_eval("def #{m}() #{ret} end")
end
end

How to create a method like ".find_by_something_and_something_else" using Ruby?

Using Ruby I know you can get pretty creative with how you name your methods. For instance in rails you have .find_by_this_and_that.
How can I do this?
Example:
def get_persons_with_5_things
res = []
persons.each do |person|
if person.number_of_things == %MAGICALLY GET THE NUMBER 5 FROM FUNCTION NAME%
res << person
end
end
return res
end
I'm not even sure how you call this kind of things so any pointers would be appreciated.
I'm a little confused by your example. If you define the method with the hardcoded 5 in the method name, then you don't need to magically figure it out inside the body of the method. If you want to do something dynamic with method missing, it would be something like this:
def method_missing(name, *args)
if name.to_s =~ /get_persons_with_(\d+)_things/
number_of_things = $1.to_i
res = []
persons.each do |person|
if person.number_of_things == number_of_things
res << person
end
end
return res
else
return super(name, *args)
end
end
[EDIT (Jörg W Mittag)]: This is a more Rubyish way of implementing that same method:
def method_missing(name, *args)
return super unless name.to_s =~ /get_persons_with_(\d+)_things/
number_of_things = $1.to_i
return persons.select {|person| person.number_of_things == number_of_things }
end
super without any arguments just passes the original arguments along, no need to pass them explicitly
an early return guarded by a trailing if or unless expression greatly clears up control flow
all the each iterator does, is select items according to a predicate; however, there already is an iterator for selecting items: select
Ruby has different meta programming techniches to do this kind of stuff.
First we need our variable method
class DB
def get_persons_with_x_things(x)
res = []
persons.each do |person|
if person.number_of_things == x
res << person
end
end
return res
end
end
define_method
If there is a finite number of x's. We could use define_method to create all this methods. define_method creates a method. The first argument is the name of the method, the seccond argument or the given block is the stuff, which get's executed when the method is called.
This way, you don't realy create such method's, but It will look for the user if he calls it, as if it existed. But if the user relies on Object#methods and such, he will never see your inifinite number of fake methods.
class DB
99.times do |i|
define_method("get_persons_with_#{i}_things") do
get_persons_with_x_things(i)
end
end
end
method_missing
If there is an infinite numbor of x's method_missing would be better suited for this Task. If someone tries to call a method which does not exist, method_missing is executed instead. The first argument for method_missing is the method name as symbol, the following arguments are the original arguments.
class DB
def method_missing(name, *args)
case name.to_s
when /^get_persons_with_(\d+)_things$/
get_persons_with_x_things($1.to_i)
else
super(name, *args)
end
end
end
method_missing and send
To not use static regexe would be even cooler. But this could have some security implications. The method send I use here, calls a method by it's name.
class DB
def method_missing(name, *args)
name.to_s=~ /\d+/
# always be carefull with $ variables, they are global for this thread, so save everything as fast as you can
new_name= "#{$`}x#{$'}"
number= $1.to_i
if method_defined?(new_name)
send(new_name, number)
else
super(name, *args)
end
end
end
you can do a lot of things like this with method missing:
Ruby Docs
StackOveflow method_missing
Have a look at Ruby's callbacks specially method_missing.

Resources