Home > Net >  Ruby procs and calling methods without instantiation
Ruby procs and calling methods without instantiation

Time:10-01

I'm trying to get this RSpec test pasted below to pass, but I'm lost on how I should call multiply_by. I believe that I have to store multiply_by as a proc so that it can be called on the expect line, but how do I get access to the multiply_by method if I don't instantiate the NumProcessor class? Maybe I'm not supposed to make NumProcessor a class, but I need to use . operator so I can do NumProcessor.multiply_by, so I'm unclear on that. There is something I'm missing about how procs work in ruby, I believe. Any guidance would be much appreciated.

# First attempt 
class NumProcessor
  def multiply_by(num)
    multiply_by = Proc.new { |num| num * 2} 
  end
end

# Trying to write code to make this test pass without changing it
describe 'NumProcessor#multiply_by' do
  it 'should double' do
    double = NumProcessor.multiply_by(2)
    expect(double.call(4)).to eq(8)
  end
end

Updated Tests

class NumProcessor
  def self.multiply_by(factor)
    Proc.new { |num| num * factor} 
  end
end

# Trying to write code to make this test pass without changing it
describe 'NumProcessor#multiply_by' do
  it 'should double' do
    double = NumProcessor.multiply_by(2)
    expect(double.call(2)).to eq(4)
  end
  it 'should triple' do
    triple = NumProcessor.multiply_by(3)
    expect(triple.call(2)).to eq(6)
  end
end

Got them working!

CodePudding user response:

I need to use . operator so I can do NumProcessor.multiply_by

In order to call the method on the class itself, you have to define a so-called class method, e.g. by prefixing it with self.: (see Methods – Scope)

class NumProcessor
  def self.multiply_by(number)
    # ...
  end
end

You can now call it via:

NumProcessor.multiply_by(4)
#=> #<Proc:...>

If you are not going to create instances of NumProcessor, you can make it a module:

module NumProcessor
  def self.multiply_by(number)
    # ...
  end
end

Last not least there's module_function – it turns a public instance method (one without self.) into both, a class method and a private instance method:

module NumProcessor
  def multiply_by(number)
    # ...
  end
  module_function :multiply_by
end

This is how the built-in Math module is implemented. The method can be called as a class method:

NumProcessor.multiply_by(4)
#=> #<Proc:...>

But you can also include the module into another class in order to call its methods without explicit receiver:

class Foo
  include NumProcessor

  def bar
    multiply_by(4)
  end
end

while not exposing it to the outside:

foo = Foo.new
foo.bar
#=> #<Proc:...>

foo.multiply_by(4)
# NoMethodError: private method `multiply_by' called

Regarding your implementation:

  • the multiply_by = assignment is superfluous, because the multiply_by variable isn't used afterwards
  • the number argument isn't used
  • despite its name, multiply_by(number) always multiplies by 2 (instead of multiplying by number)

To fix it, I'd implement it as: (see the docs for Proc which uses the very same implementation as an example)

def multiply_by(factor)
  Proc.new { |n| n * factor }
end

A corresponding test could look like this:

describe 'NumProcessor' do
  describe '.multiply_by' do
    let(:multplier) { NumProcessor.multiply_by(factor) }

    context 'with a factor of 2' do
      let(:factor) { 2 }

      it 'should double' do
        expect(multiplier.call(4)).to eq(8)
      end
    end

    context 'with a factor of 3' do
      let(:factor) { 3 }

      it 'should triple' do
        expect(multiplier.call(4)).to eq(12)
      end
    end
  end
end

CodePudding user response:

Change multiply_by to a class method by adding self. in the start of the method name.

class NumProcessor
  def self.multiply_by(number)
    multiply_by = Proc.new { |num| num * 2} 
  end
end

This will allow a call like NumProcessor.multiply_by(4)

  • Related