Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No named parameters in Ruby?

This is so simple that I can't believe it caught me.

def meth(id, options = "options", scope = "scope")   puts options end  meth(1, scope = "meh")  -> "meh" 

I tend to use hashes for argument options just because it was how the herd did it– and it is quite clean. I thought it was the standard. Today, after about 3 hours of bug hunting, I traced down an error to this gem I happen to be using that assumes named parameters will be honored. They are not.

So, my question is this: Are named parameter officially not honored in Ruby (1.9.3), or is this a side effect of something I'm missing? If they are not, why not?

like image 614
JohnMetta Avatar asked Mar 08 '12 03:03

JohnMetta


People also ask

What are parameters in Ruby?

What is a parameter? Parameters in ruby are variables that are defined in method definition and which represent the ability of a method to accept arguments. So, if we will not have the appropriate parameters, then we will not be able to pass arguments to a method that will contain the data we need.

How do you use keyword arguments in Ruby?

If you want to accept keyword arguments, in principle you should always use def foo(k: default) or def foo(k:) or def foo(**kwargs) . Note that Ruby 3.0 doesn't behave differently when calling a method which doesn't accept keyword arguments with keyword arguments.

What is a Ruby method?

A method in Ruby is a set of expressions that returns a value. Within a method, you can organize your code into subroutines which can be easily invoked from other areas of their program. A method name must start a letter or a character with the eight-bit set.


2 Answers

What's actually happening:

# Assign a value of "meh" to scope, which is OUTSIDE meth and equivalent to #   scope = "meth" #   meth(1, scope) meth(1, scope = "meh")  # Ruby takes the return value of assignment to scope, which is "meh" # If you were to run `puts scope` at this point you would get "meh" meth(1, "meh")  # id = 1, options = "meh", scope = "scope" puts options  # => "meh" 

There is no support* for named parameters (see below for 2.0 update). What you're seeing is just the result of assigning "meh" to scope being passed as the options value in meth. The value of that assignment, of course, is "meh".

There are several ways of doing it:

def meth(id, opts = {})   # Method 1   options = opts[:options] || "options"   scope   = opts[:scope]   || "scope"    # Method 2   opts = { :options => "options", :scope => "scope" }.merge(opts)    # Method 3, for setting instance variables   opts.each do |key, value|     instance_variable_set "@#{key}", value     # or, if you have setter methods     send "#{key}=", value   end   @options ||= "options"   @scope   ||= "scope" end  # Then you can call it with either of these: meth 1, :scope => "meh" meth 1, scope: "meh" 

And so on. They're all workarounds, though, for the lack of named parameters.


Edit (February 15, 2013):

* Well, at least until the upcoming Ruby 2.0, which supports keyword arguments! As of this writing it's on release candidate 2, the last before the official release. Although you'll need to know the methods above to work with 1.8.7, 1.9.3, etc., those able to work with newer versions now have the following option:

def meth(id, options: "options", scope: "scope")   puts options end  meth 1, scope: "meh" # => "options" 
like image 128
brymck Avatar answered Oct 14 '22 15:10

brymck


I think 2 things are happening here:

  1. You are defining a parameter on the method named 'scope' with a default value of "scope"
  2. When you call the method, you are assigning the value "meh" to a new local variable named 'scope', which is unrelated to the parameter name on the method you are calling.
like image 30
Jon M Avatar answered Oct 14 '22 15:10

Jon M