Pass block passed to method to another method in Ruby

22,311

Solution 1

You can reference the block explicitly

def discard(&block)
  self - self.keep(&block)
end

or implicitly

def discard
  self - self.keep(&Proc.new {})
end

In your case, I would suggest the first approach.

Solution 2

In the second example, &Proc.new {} doesn't pass a block, it creates a new empty one. One should omit {} and write it as self.keep(&Proc.new) or just keep(&proc) as self. is redundant and proc is the recommended synonym for Proc.new:

# passes on the block or the absence of a block
def discard(&block)
  self - keep(&block)
end

# passes on the block and fails if no block given
def discard
  self - keep(&proc)
end

Both Proc.new and proc without a block use the block of the current method.

&proc will fail if discard doesn't get a block. So the first example is the best if you want to pass the block or the absence of a block (&nil passes no block at all). The second example (as I changed it) is the best if missing block is an error.

In both cases, each time 'discard' is called, a new 'Proc' object is created, and it's not free.

Share:
22,311
Kappie001
Author by

Kappie001

Updated on October 14, 2020

Comments

  • Kappie001
    Kappie001 over 3 years

    I'm trying to write a clone of the ruby keep_if and delete_if array methods. Here is my code.

    module Strain
      def keep
        self.inject([]) do |extracts, element|
          yield(element) ? extracts << element : extracts 
        end
      end
    
      def discard
        self.inject([]) do |extracts, element|
          !yield(element) ? extracts << element : extracts
        end
      end
    end
    
    class Array
      include Strain
    end
    

    This works. But I want to do something like:

    def discard
      self - self.keep &block
    end
    

    Desired behaviour:

    [1, 2, 3].discard { |number| number < 2 }
    # => [2, 3]
    

    So I need to pass the block that is passed to the discard method, to be passed on to the keep method. How do I achieve this?

  • Kappie001
    Kappie001 over 10 years
    Thanks! I ended up solving it without passing the block, but I learned something nonetheless.
  • New Alexandria
    New Alexandria over 10 years
    I'm surprised that's all this answer needed. It sounded like a more complicated solution was desired. I'll try to formulate and ask the original way I read the question