ruby 2.0 named parameters from a hash - ruby

If I have a method in ruby that takes named arguments...
def smoosh(first: nil, second: nil)
first + second
end
Whats the easiest way to pass a hash to that method if the keys match:
params = { first: 'peanut', second: 'butter' }
smoosh(params)
The above produces an argument error.
Update:
It seems like this might be an issue with how Sinatra parameters work.
When I do:
get 'a_sinatra_route' do
hash = params.clone
hash.symbolize_keys!
smoosh(hash)
end
It works fine. It does not work when just passing the params in by themselves. (even though you can access the individual params with the symbol key params[:attr])

Seems to work just fine for me.
2.0.0p0 :007 > def smoosh(first: nil, second: nil)
2.0.0p0 :008?> first + second
2.0.0p0 :009?> end
=> nil
2.0.0p0 :010 > params = { first: 'peanut', second: 'butter' }
=> {:first=>"peanut", :second=>"butter"}
2.0.0p0 :012 > smoosh(params)
=> "peanutbutter"

If your function is using keyword arguments, you'll need to use ** (double splat). The ** (introduced in Ruby 2.0) acts like the original * operator, but can be used with Hashes (see also: another good resource):
def smoosh(first: nil, second: nil)
first + second
end
params = { first: 'double', second: 'splat' }
smoosh(**params)
=> "doublesplat"

It's throwing an ArgumentError because you're passing in one hash to a method that takes two arguments - even though the hash has two key/value pairs, it's still just one argument!
In this situation, you can try:
smoosh(params[:first], params[:second])
To pass in the values.

Related

How can I get named parameters into a Hash?

I want to have named parameters to a method so the API is clear to the caller, but the implementation of the method needs the named parameters in a hash. So I have this:
def my_method(required_param, named_param_1: nil, named_param_2: nil)
named_params = {
named_param_1: named_param_1,
named_param_2: named_param_2
}
# do something with the named params
end
This works, but I have to do this in quite a few places, and I would rather have some helper that dynamically gets the named parameters into a hash. I haven't been able to find a way to do this. Any thoughts on how to accomplish this?
def my_method(required_param, named_param_1: nil, named_param_2: nil)
named_params = method(__method__).parameters.each_with_object({}) do |p,h|
h[p[1]] = eval(p[1].to_s) if p[0] == :key
end
p named_params # {:named_param_1=>"hello", :named_param_2=>"world"}
# do something with the named params
end
my_method( 'foo', named_param_1: 'hello', named_param_2: 'world' )
Ruby 2.0 provides no built-in way to get a Hash from keyword arguments.
You must choose between:
Using keyword arguments (as you currently are) and creating a Hash from those values separately, OR...
Using a Hash argument with default values, such as the Ruby v1.9.3 strategies described here.
You could do write your method as below :
def my_method(required_param, named_param_1: nil, named_param_2: nil)
named_params = Hash[method(__method__).parameters.select do |first,last|
[last,eval("#{last}")] if first == :key
end]
end
my_method(12, named_param_1: 11,named_param_2: 12)
# => {:named_param_1=>11, :named_param_2=>12}

instance_eval's block argument(s)- documented? purpose?

Just realized that instance_eval yields self as an argument to the associated block (except for a bug in the 1.9.2 version: http://www.ruby-forum.com/topic/189422)
1.9.3p194 :003 > class C;end
1.9.3p194 :004 > C.new.instance_eval {|*a| a}
=> [#<C:0x00000001f99dd0>]
1.9.3p194 :005 >
Is this documented/spec'ed somewhere? Looking at ruby-doc:BasicObject, can't see any block params mentioned.
Is there a reason -apart from some purely historical one- for passing it explicitly when it self is always defined anyway?
The way I was hit by this is:
l = lambda { }
myobj.instance_eval(&l) # barks
This worked fine in 1.8.x (I guess because of block arity wasn't enforced).
Then upgraded to 1.9.2 - and it still worked! That's a strange coincidence as even though lambda block arguments are strictly enforced (so it would have complained for not declaring the argument for self), however due to the bug linked above - the self actually wasn't passed in this version..
Then upgraded to 1.9.3 where that bug got fixed, so it started to throwing the argument error - pretty surprising for a minor version change IMHO.
So one workaround is do declare parameter, or make lambda a block instead:
l = proc { }
myobj.instance_eval(&l) # fine
Just thought to describe the full story to help others avoid wasting time the way I did - until this is properly documented.
Reading Ruby's source code, what I can interpret is:
instance_eval is executing this:
return specific_eval(argc, argv, klass, self)
which in turn runs:
if (rb_block_given_p()) {
if (argc > 0) {
rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc);
}
return yield_under(klass, self, Qundef);
}
You can see they pass Qundef for the VALUES argument.
if (values == Qundef) {
return vm_yield_with_cref(th, 1, &self, cref);
}
In that particular line of code, they set manually argc (argument count) to 1 and the argument as "self". Later on the code that prepares the block sets the arguments to the block to these arguments, hence the first argument = "self" and the rest are nil.
The code that sets up the block arguments is doing :
arg0 = argv[0];
... bunch of code ...
else {
argv[0] = arg0;
}
for (i=argc; i<m; i++) {
argv[i] = Qnil;
}
Resulting in:
1.9.3p194 :006 > instance_eval do |x, y, z, a, b, c, d| x.class end
=> Object
1.9.3p194 :008 > instance_eval do |x, y, z, a, b, c, d| y.class end
=> NilClass
Why ? I have no idea but the code seems to be intentional. Would be nice to ask the question to the implementers and see what they have to say about it.
[Edit]
This probably is like that because the blocks you pass to instance_eval may or may not be crafted for it (code that depends on self being set to the class you want the block to modify), instead they may assume you are going to pass them the instance you want them to modify as an argument and in this way they would work with instance_eval as well.
irb(main):001:0> blk = Proc.new do |x| x.class end
#<Proc:0x007fd2018447b8#(irb):1>
irb(main):002:0> blk.call
NilClass
irb(main):003:0> instance_eval &blk
Object
Of course this is only a theory and without official documentation I can only guess.
I have just dicovered that unlike #instance_eval, which is primarily intended for string evaluation, #instance_exec primarily intended for block evaluation, does not have the described behavior:
o = Object.new
o.instance_exec { |*a| puts "a.size is #{a.size}" }
=> a.size is 0
This is probably an unintended inconsistency, so you might have discovered a bug. Post it on Ruby bugs.
I just asked the same question here: Ruby lambda's proc's and 'instance_eval'
And after reading the answer and working through some code, I think I understand why ruby has this strange (IMHO) inconsistency.
It basically allows Symbol#to_proc to work.
For example ["foo", "bar"].each(&:puts) is short for [...].each { |x| puts x }
NOT
[...].each { self.puts }
So ruby also passes self as the first param to the proc, so basically the proc can either use self or its first param.
Since instance eval does not by definition explicitly pass params this is almost always invisible behavior.
The exception is when the proc is a lambda. This DOES NOT WORK:
2.4.1 :015 > foo = -> { puts 'hi' }
=> #<Proc:0x007fcb578ece78#(irb):15 (lambda)>
2.4.1 :016 > [1, 2, 3].each(&foo)
ArgumentError: wrong number of arguments (given 1, expected 0)
from (irb):15:in `block in irb_binding'
from (irb):16:in `each'
from (irb):16
So I think the only time this becomes a problem is when instance_eval is being used with some unknown value, where you don't know if the proc is a lambda or not. In this case you have to do it like this:
proc_var.lambda? ? instance_exec(&proc_var) : instance_eval(&proc_var)
Weird (to me) that ruby just does not do this under the hood for you.
but I guess you could make it so:
alias original_instance_eval instance_eval
def instance_eval(*args, &block)
block&.lambda? ? instance_exec(&block) : original_instance_eval(*args, &block)
end

Ruby check if nil before calling method

I have a string in Ruby on which I'm calling the strip method to remove the leading and trailing whitespace. e.g.
s = "12345 "
s.strip
However if the string is empty nil I get the following error.
NoMethodError: undefined method `strip' for nil:NilClass
I'm using Ruby 1.9 so whats the easiest way to check if the value is nil before calling the strip method?
Update:
I tried this on an element in an array but got the same problem:
data[2][1][6].nil? ? data[2][1][6] : data[2][1][6].split(":")[1].strip
Ruby 2.3.0 added a safe navigation operator (&.) that checks for nil before calling a method.
s&.strip
If s is nil, this expressions returns nil instead of raising NoMethodError.
You can use method try from ActiveSupport (Rails library)
gem install activesupport
require 'active_support/core_ext/object/try'
s.try(:strip)
or you can use my gem tryit which gives extra facilities:
gem install tryit
s.try { strip }
If you don't mind the extra object being created, either of these work:
"#{s}".strip
s.to_s.strip
Without extra object:
s && s.strip
s.strip if s
I guess the easiest method would be the following:
s.strip if s
I'd opt for a solution where s can never be nil to start with.
You can use the || operator to pass a default value if some_method returns a falsy value:
s = some_method || '' # default to an empty string on falsy return value
s.strip
Or if s is already assigned you can use ||= which does the same thing:
s ||= '' # set s to an empty string if s is falsy
s.strip
Providing default scenario's for the absence of a parameters or variables is a good way to keep your code clean, because you don't have to mix logic with variable checking.
Method which works for me (I know, I should never pollute pristine Object space, but it's so convenient that I will take a risk):
class Object
def unless_nil(default = nil, &block)
nil? ? default : block[self]
end
end
p "123".unless_nil(&:length) #=> 3
p nil.unless_nil("-", &:length) #=> "-"
In your particular case it could be:
data[2][1][6].unless_nil { |x| x.split(":")[1].unless_nil(&:strip) }
ActiveSupport comes with a method for that : try. For example, an_object.try :strip will return nil if an_object is nil, but will proceed otherwise. The syntax is the same as send. Cf active_support_core_extensions.html#try.
If you want to avoid the error that appears in the question:
s.to_s.strip
To complete the options shown here, there is the "Existence Check Shorthand", recommended in the Ruby Style Guide:
Use &&= to preprocess variables that may or may not exist. Using &&= will change the value only if it exists [means, is not nil], removing the need to check its existence with if.
So in your case you would do:
s = "12345 "
s &&= s.strip
Simply put:
s = s.nil? ? s : s.strip
Tl;dr Check if s is nil, then return s, otherwise, strip it.

Ruby testing for definition of a variable

I know there are other questions similar such as:
Ruby: how to check if variable exists within a hash definition
Checking if a variable is defined?
But the answers aren't fully satisfactory.
I have:
ruby-1.9.2-p290 :001 > a=Hash.new
=> {}
ruby-1.9.2-p290 :002 > a['one']="hello"
=> "hello"
ruby-1.9.2-p290 :006 > defined?(a['one']['some']).nil?
=> false
ruby-1.9.2-p290 :007 > a['one']['some'].nil?
=> true
It seems like:
if a['one']['some'].nil?
a['one']['some']=Array.new
end
would be sufficient. Is this correct? Would this be correct for any data type? Is defined? needed in this case?
thx
You seem to be confusing two concepts. One is if a variable is defined, and another is if a Hash key is defined. Since a hash is, at some point, a variable, then it must be defined.
defined?(a)
# => nil
a = { }
# => {}
defined?(a)
# => "local-variable"
a.key?('one')
# => false
a['one'] = 'hello'
# => 'hello'
a.key?('one')
# => true
Something can be a key and nil at the same time, this is valid. There is no concept of defined or undefined for a Hash. It is all about if the key exists or not.
The only reason to test with .nil? is to distinguish between the two possible non-true values: nil and false. If you will never be using false in that context, then calling .nil? is unnecessarily verbose. In other words, if (x.nil?) is equivalent to if (x) provided x will never be literally false.
What you probably want to employ is the ||= pattern that will assign something if the existing value is nil or false:
# Assign an array to this Hash key if nothing is stored there
a['one']['hello'] ||= [ ]
Update: Edited according to remarks by Bruce.
I had to dig a number of pages deep into Google, but I eventually found this useful bit from the Ruby 1.9 spec:
"In all cases the test [defined?] is conducted without evaluating the operand."
So what's happening is that it looks at:
a['one']['some']
and says "that is sending the "operator []" message to the 'a' object - that is a method call!" and the result of defined? on that is "method".
Then when you check against nil?, the string "method" clearly isn't nil.
In addition to #tadmans answer, what you actually did in your example was to check, if the string "some" is included in the string "hello" which is stored in your hash at the position "one".
a = {}
a['one'] = 'hello'
a['one']['some'] # searches the string "some" in the hash at key "one"
A more simple example:
b = 'hello'
b['he'] # => 'he'
b['ha'] # => nil
That's why the defined? method did not return nil, as you expected, but "method".

Accessing the last key–value pair in a Ruby (1.9) hash

As of Ruby 1.9, hashes retain insertion order which is very cool. I want to know the best way to access the last key–value pair.
I've written some code which does this:
hash.values.last
This works and is very easy to comprehend, but perhaps it's possible to access the last value directly, rather that via an intermediary (the array of values). Is it?
Hash have a "first" method, but that return the first pair in array mode, for last, you can try:
my_hash.to_a.last
this return last pair in array mode like "first method"
One more alternative that I'm using myself:
hash[hash.keys.last]
which works out better when you want to directly assign a value onto the last element of the hash:
2.4.1 :001 > hash = {foo: 'bar'}
=> {:foo=>"bar"}
2.4.1 :002 > hash[hash.keys.last] = 'baz'
=> "baz"
2.4.1 :003 > hash.values.last = 'bar'
NoMethodError: undefined method `last=' for ["baz"]:Array
Did you mean? last
from (irb):3
from /home/schuylr/.rvm/rubies/ruby-2.4.1/bin/irb:11:in `<main>'
Nothing built in, no. But you could monkey-patch one if you were so inclined (not usually recommended, of course):
class Hash
def last_value
values.last
end
end
And then:
hash.last_value
I just did this for a very large hash:
hash.reverse_each.with_index do |(_, value), index|
break value if (index == 0)
end

Resources