Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby check if block is nil

Tags:

yield

ruby

I call a method with a block;

method do
  "Hello"
end

and the method is defined as;

def method
  yield
end

and when defining method; i want to check if given block is empty (nil) or not, because the variable in the method may end up like this;

method do
  ""
end

So in definition, i want to check if the yield block is nil or not. Like;

def method
  if yield ? yield : "Empty block? Seriously?"
end

I know the above does not work. Bu it is what i want to achieve.

Also keep in mind that block_given? will always be "true" since the block is given even if it is nil or empty string.

UPDATE: As most of the comments/answers state that the question is unclear; here is the problem simplified by @ndn:

I want to check if the result of executing a block is "empty"(nil or "") without invoking it first.

like image 837
tozlu Avatar asked Aug 02 '15 13:08

tozlu


People also ask

How do you check nil in Ruby?

In Ruby, you can check if an object is nil, just by calling the nil? on the object... even if the object is nil.

Is nil or empty Ruby?

Well, nil is a special Ruby object used to represent an “empty” or “default” value. It's also a “falsy” value, meaning that it behaves like false when used in a conditional statement.

What does nil mean Ruby?

In Ruby, nil is a special value that denotes the absence of any value. Nil is an object of NilClass. nil is Ruby's way of referring to nothing or void.

What are procs Ruby?

A Proc object is an encapsulation of a block of code, which can be stored in a local variable, passed to a method or another Proc, and can be called. Proc is an essential concept in Ruby and a core of its functional programming features.


2 Answers

It is unclear what you are asking, because a block itself can not be empty. Therefore, you might mean a few different things:

  1. A missing block. You can check if a block is given

    block_given?
    
  2. Block with empty body (aka {} or do end). This is not impossible, but requires some advanced voodoo ruby metaprogramming magic. Generally, if this is what you are looking for, either you are writing something very interesting or your approach is completely wrong.
  3. You want to check if the result of executing a block is "empty" without invoking it first. This is impossible. For example, consider the following block:

    { [nil, "", true].sample }
    

    Obviously, there is no way to know in advance.

  4. You are ok with calling the block. Then you can assign the result to a variable and make checks on it:

    def some_method
      evaluation_result = yield if block_given?
      if evaluation_result.nil? or evaluation_result == ""
        # do something if the block was not given or the result is nil/empty
        puts "Empty block? Seriously?"
      else
        # do something if the block was given and the result is non nil/empty
        puts evaluation_result
      end
    end
    

    Now when you invoke some_method:

    some_method { "something" } # => "something"
    some_method { 3 + 5 } # => 8
    some_method { nil } # => "Empty block? Seriously?"
    some_method { "" } # => "Empty block? Seriously?"
    some_method { } # => "Empty block? Seriously?"
    some_method # => "Empty block? Seriously?"
    

EDIT: A workaround for case #3 might be to create two procs, one with what you want to do if the block is "empty" and one - if it is not, then pass them around to the endpoint where you will finally invoke the block. This might or might not be applicable depending on your exact situation.

EDIT2: Another workaround can be to redefine the Proc#call method for your proc instances. However, this doesn't work for yield:

def secure(&block)
  insecure_call = block.method(:call)
  block.define_singleton_method(:call) do
    insecure_call_result = insecure_call.call
    if insecure_call_result.nil? or insecure_call_result == ""
      "<b>Bummer! Empty block...</b>"
    else
      insecure_call_result
    end
  end
end

x = proc { }
y = proc { "" }
z = proc { nil }
a = proc { 3 + 5 }
b = proc { "something" }
u = proc { [nil, "", true].sample }
[x, y, z, a, b, u].each { |block| secure &block }

# some method that uses the block
def user(&block)
  "What I got is #{block.call}!"
end


user &x # => "What I got is <b>Bummer! Empty block...</b>!"
user &y # => "What I got is <b>Bummer! Empty block...</b>!"
user &z # => "What I got is <b>Bummer! Empty block...</b>!"
user &a # => "What I got is 8!"
user &b # => "What I got is something!"
user &u # => Different each time

EDIT3: Another alternative, which is sort of cheating, is to wrap the given proc in another proc. This way, it will work for yield too.

def wrap(&block)
  proc do
    internal_proc_call_result = block.call
    if internal_proc_call_result.nil? or internal_proc_call_result == ""
      "<b>Bummer! Empty block...</b>"
    else
      internal_proc_call_result
    end
  end
end

Now using the result of wrap and will get you behavior similar to secure.

like image 68
ndnenkov Avatar answered Sep 30 '22 19:09

ndnenkov


If I understand correctly, you want to statically determine what the runtime value of a block is. This is one of the many known impossible problems resulting from the undecidability of the Halting Problem.

In other words: it can't be done.

Not "it can't be done in Ruby", not "it is hard", it simply can't be done, period. And it can be (and has been) mathematically proven that it can't be done. Ever.

like image 41
Jörg W Mittag Avatar answered Sep 30 '22 20:09

Jörg W Mittag