How can I declare a method with keyword arguments just like rails do. some examples may be
Person.find(:all, :conditions => "...").
How can I use symbols to create methods similar to the above?
I am very new to ruby. Thanks in advance!
Ruby doesn't actually have keyword arguments. Rails is exploiting a feature of Ruby which lets you omit the braces around a hash. For example, with find, what we're really calling is:
Person.find(:all, { :conditions => "...", :offset => 10, :limit => 10 } )
But if the hash is the last argument of the method, you can leave out the braces and it will still be treated as a hash:
Person.find(:all, :conditions => "...", :offset => 10, :limit => 10)
You can use this in your own methods:
def explode(options={})
defaults = { :message => "Kabloooie!", :timer => 10, :count => 1 }
options = defaults.merge(options)
options[:count].times do
sleep options[:timer]
puts options[:message]
end
end
And then call it:
explode :message => "Meh.", :count => 3
Or call it without an argument, resulting in all default values being used:
explode
Since Ruby 2.0, ruby does have keyword arguments.
def my_method(arg1, name: 'defaultName', number: 0)
puts arg1, name, number
end
I agree with accepted answer given by Samir Talwar and christopherwright. The only potential downside is that you get no warnings if you use an incorrect keyword symbol as an argument or when looking up an option, it just ends up ignored. If that's something you're concerned about, the gem hash_keyword_args addresses it. The idiom would be
def explode(opts={})
opts = opts.keyword_args(:message => "Kabloooie!", :timer => 10, :count => 1)
opts.count.times do
sleep opts.timer
puts opts.message
end
end
Notice the use of accessor methods so you'll get a NoMethodError if you mistype a keyword. And the calling behavior is:
explode(:message => "Okay") # works
explode(:msg => "Oops") # raises ArgumentError
The gem also provides a few other features you might or might not care about, such as being able to indicate that a keyword is required. I've been using it happily for a while now.
(Disclaimer: I'm the author of the gem.)
You just need to define a method where one of the parameters is a hash. It's actually pretty simple.
def method(arg1, params)
name = params[:name]
number = params[:number]
And then call it like:
method(arg1, :name => 'Eric', :number => 2)
Two notes:
In Ruby, you don't need to surround the parameters hash in {} when you call the method in most cases, unless you have something complicated going on like passing multiple hashes. In that case, make sure you surround those parameters with {}
Ruby is dynamically typed, so you don't need to say that params is a hash when you define the method.
Ruby 2.0 introduced real keyword arguments, and Ruby 2.1 added required keyword arguments.
There's a nice article up at https://chriszetter.com/blog/2012/11/02/keyword-arguments-in-ruby-2-dot-0/ on this, I've borrowed the examples from there:
Ruby 2.0+:
def exclaim(text, exclamation: '!', number: 7)
text + exclamation * number
end
exclaim('hello', number: 4) #=> 'hello!!!!'
# equivalent:
exclaim('hello', {:number => 4}) #=> 'hello!!!!'
Ruby 2.1+:
def exclaim(text, exclamation: '!', number:)
text + exclamation * number
end
exclaim('Yo', number: 5) # => 'Yo!!!!!'
exclaim('Yo') # raises: ArgumentError: missing keyword: number
Since Ruby is typed dynamically, just do :
def my_method(arg1, arg2)
#things
end
example:
my_method(:test, {:somehash => "yay"})
or
my_method :test, :somehash => "yay"
or
my_method(:test, :somehash => "yay")
Related
How I can pass the keyword arguments to a call to another method in my code without specifying each of them.
Consider the following method with the new first-class support for kwargs:
def foo(obj, bar: 'a', baz: 'b')
another_method(bar, baz)
end
Consider the old Ruby options hash syntax:
def foo(obj, options = {})
another_method(options)
end
I want to pass the keyword arguments without specifying each of them explicitly as with the case of the old options hash syntax. Is that even possible?
You can use the double-splat operator:
def a(q: '1', w: '2')
p q
p w
end
def b(**options)
a(options)
end
b
# => 1
# => 2
b(q: '3', w: '4')
# => 3
# => 4
Keyword arguments work similarly to your "old Ruby options hash syntax". A hash's keys will substitute as keywords and their values as arguments. This should work fine:
def foo(obj, bar: 'a', baz: 'b')
another_method(bar, baz)
end
arg_hash = { bar: 'alt bar', baz: 'alt baz' }
foo(Object.new, arg_hash)
Some things to be aware of
If your hash keys are strings, such as { 'bar' => 'alt bar', 'baz' => 'alt baz' }, this won't work. You'll get an ArgumentError: wrong number of arguments (2 for 1) error. This is easy enough to fix in Rails by calling .symbolize_keys on the hash. If you're not in Rails, you'll have to convert the keys manually or reimplement that method.
Also, in Rails, a HashWithIndifferentAccess (such as the params hash in a controller), although it has both strings and symbols for keys, can't be used in this way. If you pass params, it will look at the string keys instead and you'll get the same error as above. I'm not sure why this is the case, but my guess it's because the params hash starts with strings and adds symbols so the symbols are secondary and not the default. Total guess on that though.
I am trying to set the first argument to a method as being optional, followed by any number of args. For example:
def dothis(value=0, *args)
The issue I am running into is that it doesn't seem like this is actually possible? When I call dothis("hey", "how are you", "good") I was hoping it would set value to default to 0, but instead it is just making value="hey". Is there any way to accomplish this behavior?
This is not possible directly in Ruby
There are plenty of options though, depending on what you are doing with your extended params, and what the method is intended to do.
Obvious choices are
1) Take named params using hash syntax
def dothis params
value = params[:value] || 0
list_of_stuff = params[:list] || []
Ruby has nice calling convention around this, you don't need to provide the hash {} brackets
dothis :list => ["hey", "how are you", "good"]
2) Move value to the end, and take an array for the first param
def dothis list_of_stuff, value=0
Called like this:
dothis ["hey", "how are you", "good"], 17
3) Use a code block to provide the list
dothis value = 0
list_of_stuff = yield
Called like this
dothis { ["hey", "how are you", "good"] }
4) Ruby 2.0 introduced named hash parameters, which handle a lot of option 1, above for you:
def dothis value: 0, list: []
# Local variables value and list already defined
# and defaulted if necessary
Called same way as (1):
dothis :list => ["hey", "how are you", "good"]
This post is a little bit old, but I want to contribute if someone is looking for the best solution for that. Since ruby 2.0, you can do that easily with named arguments defined with a hash. The syntax is easy and more readable.
def do_this(value:0, args:[])
puts "The default value is still #{value}"
puts "-----------Other arguments are ---------------------"
for i in args
puts i
end
end
do_this(args:[ "hey", "how are you", "good"])
You can also do the same thing with the greedy keyword **args as a hash, like this:
#**args is a greedy keyword
def do_that(value: 0, **args)
puts "The default value is still #{value}"
puts '-----------Other arguments are ---------------------'
args.each_value do |arg|
puts arg
end
end
do_that(arg1: "hey", arg2: "how are you", arg3: "good")
You will need to use named parameters to accomplish this:
def dothis(args)
args = {:value => 0}.merge args
end
dothis(:value => 1, :name => :foo, :age => 23)
# => {:value=>1, :name=>:foo, :age=>23}
dothis(:name => :foo, :age => 23)
# => {:value=>0, :name=>:foo, :age=>23}
by using value=0 you are actually assigning 0 to value. just to retain the value, you can either use the above mentioned solutions or just simply use value everytime you call this method def dothis(value, digit=[*args]).
The default arguments are used when the arguments are not provided.
I came across the similar issue and I got over it by using:
def check(value=0, digit= [*args])
puts "#{value}" + "#{digit}"
end
and simply call check like this:
dothis(value, [1,2,3,4])
your value would be default and other values belong to other argument.
Given the example:
def method_of_doom
my_string = "I sense impending doom."
my_string.ah_ha_i_called_a_nonexistent_method
rescue NoMethodError => e:
puts "PROBLEM: " + e.to_s
rescue Exception:
puts "Uhh...there's a problem with that there method."
end
On the line where it says:
rescue NoMethodError => e:
What is the '=>' doing?
How is it different than this usage:
module FighterValues
BAMBOO_HEAD = { 'life' => 120, 'hit' => 9 }
DEATH = { 'life' => 90, 'hit' => 13 }
KOALA = { 'life' => 100, 'hit' => 10 }
CHUCK_NORRIS = { 'life' => 60000, 'hit' => 99999999 }
def chuck_fact
puts "Chuck Norris' tears can cure cancer..."
puts "Too bad he never cries."
end
end
module ConstantValues
DEATH = -5 # Pandas can live PAST DEATH.
EASY_HANDICAP = 10
MEDIUM_HANDICAP = 25
HARD_HANDICAP = 50
end
puts FighterValues::DEATH
→ {'life'=>90,'hit'=>13}
puts ConstantValues::DEATH
→ -5
The Hash Rocket is a Syntactic Token
The hash rocket is actually a syntactic token. You can find the token in the grammar defined by ext/ripper/ripper.y:
%token tASSOC /* => */
In other words, Ripper uses the hash rocket to associate things.
How tASSOC is Used
In general, this token is used in hash literals to associate a key with a value. For example:
{ :e => 'foo' }
associates the string literal foo with the symbol :e. This common usage is why people tend to think of the hash rocket as solely a hash-related construct.
On the other hand, the following associates a variable with an exception:
rescue => e
In this case, rather than associating a key with a value, Ripper is associating the variable e with the implied StandardError exception, and uses the variable to store the value of Exception#message.
Further Reading
If you understand tokenizers, lexers, and parsers, ripper.y and the various contents of ext/ripper/lib/ripper are instructive. However, on page 19 of Ruby Under a Microscope, Pat Shaughnessy warns:
Ruby doesn’t use the Lex tokenization tool, which C programmers commonly use in conjunction with a parser generator like Yacc or Bison. Instead, the Ruby core wrote the Ruby tokenization code by hand.
Just something to keep in mind when you're trying to grok Ruby's grammar at the source code level.
There are a bunch of good links on the Ruby Info page.
It depends on context.
In the context of a rescue it means:
"Assign the exception object to the variable e."
This is how it can be used as e.to_s later.
In a Hash literal it means:
A pair, represented by key=>value.
Here is a Hash literal is created from two pairs: {:name => "Fred", :age => 20}
(Ruby 1.9/2.0+ also allows {name: "Fred", age: 20} syntax, where name and age refer to Symbols.)
In a String, it is what it is:
"=>Whee!".
In this case puts FighterValues::DEATH, is equivalent to puts FighterValues::DEATH.to_s. That is, the output displayed comes from a string. Consider this: puts "{a => b}".
I'm very new to ruby and I'm trying to write a web application using the rails framework. Through reading I've seen methods being called like this:
some_method "first argument", :other_arg => "value1", :other_arg2 => "value2"
Where you can pass an unlimited number of arguments.
How do you create a method in ruby that can be used in this way?
Thanks for the help.
That works because Ruby assumes the values are a Hash if you call the method that way.
Here is how you would define one:
def my_method( value, hash = {})
# value is requred
# hash can really contain any number of key/value pairs
end
And you could call it like this:
my_method('nice', {:first => true, :second => false})
Or
my_method('nice', :first => true, :second => false )
This is actually just a method that has a hash as an argument, below is a code example.
def funcUsingHash(input)
input.each { |k,v|
puts "%s=%s" % [k, v]
}
end
funcUsingHash :a => 1, :b => 2, :c => 3
Find out more about hashes here http://www-users.math.umd.edu/~dcarrera/ruby/0.3/chp_03/hashes.html
Maybe that *args can help you?
def meh(a, *args)
puts a
args.each {|x| y x}
end
Result of this method is
irb(main):005:0> meh(1,2,3,4)
1
--- 2
--- 3
--- 4
=> [2, 3, 4]
But i prefer this method in my scripts.
You can make the last argument be an optional hash to achieve that:
def some_method(x, options = {})
# access options[:other_arg], etc.
end
However, in Ruby 2.0.0, it is generally better to use a new feature called keyword arguments:
def some_method(x, other_arg: "value1", other_arg2: "value2")
# access other_arg, etc.
end
The advantages of using the new syntax instead of using a hash are:
It is less typing to access the optional arguments (e.g. other_arg instead of options[:other_arg]).
It is easy to specify a default value for the optional arguments.
Ruby will automatically detect if an invalid argument name was used by the caller and throw an exception.
One disadvantage of the new syntax is that you cannot (as far as I know) easily send all of the keyword arguments to some other method, because you don't have a hash object that represents them.
Thankfully, the syntax for calling these two types of methods is the same, so you can change from one to the other without breaking good code.
The following returns whatever integer I feed it as a parameter.
def space(spacer_count)
spacer_count.times do
image_tag("24gray/spacer.png", :class => "spacer")
end
end
How do I make it return the desired number of images? E.g., I want space(6) to return six copies of spacer.png, not the number "6".
Thanks!
If you want to return a single string containing all the <img> tags, then you can do the following:
def space(spacer_count)
image_tag("24gray/spacer.png", :class => "spacer") * spacer_count
end
If you use Ruby 1.8.7 or greater
def space(spacer_count)
spacer_count.times.map do
image_tag("24gray/spacer.png", :class => "spacer")
end
end
With Ruby 1.8.6 you should use a workaround (it also works with 1.8.7 and greater).
First workaround.
def space(spacer_count)
Array.new(spacer_count).map do
image_tag("24gray/spacer.png", :class => "spacer")
end
end
Second workaround (using ranges).
def space(spacer_count)
(1..spacer_count).map do
image_tag("24gray/spacer.png", :class => "spacer")
end
end
There are some good answers here already, so I'm not going to give another one. However, it might help to understand what's going wrong here. Every block in Ruby returns the evaluation of it's last statement.
The times method returns the number of times the blocks was run.
The each method returns the array that the block was run on.
Seeing a pattern here? Most of these iterator methods return what was passed to it.
Blocks are passed to the calling function almost like an argument. Essentially this is what's happening in your code.
block = Proc.new { image_tag("24gray/spacer.png", :class => "spacer")}
def space(spacer_count)
spacer_count.times(&block)
end
Knowing now that iterators return what was passed to it, you can see why space(6) returns 6