Passing hashes instead of method parameters

49,127

Solution 1

Both approaches have their own advantages and disadvantages, when you use an options hash replacing standard arguments you lose clarity in the code defining the method but gain clarity whenever you use the method because of the pseudo-named paramaters created by using an options hash.

My general rule is if you either have a lot of arguments for a method (more than 3 or 4) or lots of optional arguments then use an options hash otherwise use standard arguments. However when using an options hash it is important to always include a comment with the method definition describing the possible arguments.

Solution 2

Ruby has implicit hash parameters, so you could also write

def my_method(options = {}) 

my_method(:width => 400, :height => 50, :show_border => false)

and with Ruby 1.9 and new hash syntax it can be

my_method( width: 400, height: 50, show_border: false )

When a function takes more than 3-4 parameters, it's much easier to see which is what, without counting the respective positions.

Solution 3

I'd say that if you are either:

  1. Having more than 6 method parameters
  2. Passing options that have some required, some optional and some with default values

You would most probably want to use a hash. It's much easier to see what the arguments mean without looking up in the documentation.

To those of you saying it's hard to figure what options a method takes, that just means that the code is poorly documented. With YARD, you can use the @option tag to specify options:

##
# Create a box.
#
# @param [Hash] options The options hash.
# @option options [Numeric] :width The width of the box.
# @option options [Numeric] :height The height of the box.
# @option options [Boolean] :show_border (false) Whether to show a
#   border or not.
def create_box(options={})
  options[:show_border] ||= false
end

But in that specific example there's such few and simple parameters, so I think I'd go with this:

##
# Create a box.
#
# @param [Numeric] width The width of the box.
# @param [Numeric] height The height of the box.
# @param [Boolean] show_border Whether to show a border or not.
def create_box(width, height, show_border=false)
end

Solution 4

It is not common practice in Ruby to use a hash rather than formal parameters.

I think this is being confused with the common pattern of passing a hash as a parameter when the parameter can take a number of values e.g. setting attributes of a Window in a GUI toolkit.

If you have a number of arguments to your method or function then explicitly declare them and pass them. You get the benefit that the interpreter will check that you have passed all the arguments.

Don't abuse the language feature, know when to use it and when not to use it.

Solution 5

I think this method of parameter passing is much clearer when there are more than a couple of parameters or when there are a number of optional parameters. It essentially makes method calls manifestly self-documenting.

Share:
49,127

Related videos on Youtube

rmaruszewski
Author by

rmaruszewski

Updated on July 05, 2022

Comments

  • rmaruszewski
    rmaruszewski almost 2 years

    I see that in Ruby (and dynamically typed languages, in general) a very common practice is to pass a hash, instead of declaring concrete method parameters. For example, instead of declaring a method with parameters and calling it like this:

    def my_method(width, height, show_border)
    my_method(400, 50, false)
    

    you can do it this way:

    def my_method(options)
    my_method({"width" => 400, "height" => 50, "show_border" => false})
    

    I'd like to know your opinion about it. Is it a good or a bad practice, should we do it or not? In what situation using this practice is valid, and it what situation can it be dangerous?

    • sarahhodne
      sarahhodne over 14 years
      A little note: {width => 400, height => 50, show_border => false} is not valid ruby syntax. I think you mean {:width => 400, :height => 50, :show_border => false} or {width: 400, height: 50, show_border: false} (the latter is only valid in ruby 1.9.1)
  • MBO
    MBO over 14 years
    If you need variable number of arguments just use def method(*args), hashes don't solve that particular problem
  • Chris McCauley
    Chris McCauley over 14 years
    Thanks for pointing out the potential confusion. I was really thinking of the case the original poster raised where the order and number of parameters is variable.
  • FilBot3
    FilBot3 over 10 years
    Now, if I were to have an options hash, could I just put the whole hash in, or do I have to pass the keys => values individually?
  • jtzero
    jtzero about 9 years
    To add to that point, that anti-pattern is also known as Magic Container
  • Dragan Nikolic
    Dragan Nikolic over 8 years
    It is true that both have advantages and disadvantages, however I disagree with general rule. I believe options are in general bad practice and should be used only if that is the only way to solve a problem.
  • Dragan Nikolic
    Dragan Nikolic over 8 years
    While your statement is true I don't think is the best way to keep a library compatible. You can achieve the same by simply adding new parameters as optional. They do not break compatibility and still have benefit of clarity and self-documentation.
  • Mirv - Matt
    Mirv - Matt over 7 years
    @DraganNikolic I'm on board with the sany metz stuff too - the benefits of not having to arrange a list of manual parameters is huge & there's other ways of self-documenting code laid out in the book
  • Aldo 'xoen' Giambelluca
    Aldo 'xoen' Giambelluca about 7 years
    Another thing many (included me!) often forget is that the order of parameters is a dependency too! This mean that just use optional params means you can't change the order of params for whatever reason (but usually you don't want many args arguments anyway!)
  • tanius
    tanius about 4 years
    As of Ruby 2.7 now, implicit hash parameters are deprecated and will be removed in Ruby 3.0 – means, we need again the {…} explicitly to pass a hash (see). The Ruby 1.9 syntax shown here is still possible for calling a method with keyword parameters.