Reed G. Law
Reed G. Law

Reputation: 3945

How to make Ruby methods show what types they expect

What is the best practice for expressing intent with Ruby method names?

For example, with the following:

class Formula
  attr_accessor :steps

  def find_step(key)
    @steps.find {|s| s.name == key }
  end

end

and:

class Step
  attr_accessor :name
end

The Formula#find_step method expects a Symbol but it could just as easily take a Step type and check that the names match. I could rename the method find_step_by_name but it's still not clear what name is. Is it a string or a symbol? Or is it matching the name on a Step object?

Edit: marked as a possible duplicate of Does Ruby support type-hinting? I'm not asking if it's possible, I'm asking a question about naming.

Upvotes: 1

Views: 127

Answers (3)

ReggieB
ReggieB

Reputation: 8247

I think the best answer to the question is good choice of the argument name. If you intend a symbol to be passed into your find_by method, why not do this:

def find_by(symbol)
  @steps.find {|s| s.name == symbol }
end

However, I think this would be clearest option:

def find_by_name(symbol)
  @steps.find {|s| s.name == symbol }
end

I don't agree that using a keyword solves this problem. All it does is change the way the string or symbol is passed into the method.

def find_by(name:)
  puts name
end

find_by name: 'this'  # name has to be passed in via hash
find_by name: :that

def find_by(name)
  puts name
end

find_by 'this' # name is passed in directly
find_by :that

It doesn't get over the problem that 'name' doesn't imply what type of object is being passed in.

I think keywords are really useful if you are passing in multiple arguments into the method, as they makes the code much more readable at the point it is used.

def mass(width:, height:, density:)
  width * height * density
end

mass width: 3, height: 11, density: 0.5

That means you don't have to go to the mass method definition each time it's used to work out what's being passed in. It also removes the necessity to get the argument order correct. This will work just as well:

mass width: 3, density: 0.5, height: 11

But for a method that has a single argument, I think you're just adding complexity by using keywords.

Upvotes: 1

Huang Minghe
Huang Minghe

Reputation: 441

after Ruby2.1 I think the following solution is nice.

def find_by(name:)
  # and you can check if type of name is what you expect as you need.
  # But that is not duck-typingy.
end

Upvotes: 1

spickermann
spickermann

Reputation: 106972

I would argue that find_step_by_name makes it clear that is expects a name and not a step that might have a name. If you are unsure if to support strings or symbols then allow both:

  def find_step_by_name(name)
    @steps.find { |s| s.name == name.to_s }
  end

Upvotes: 1

Related Questions