Ruby check if variable has length - ruby

Ok, this might sound like a silly question, but I'm having all sorts of problems with it and I'm sure there has to be an easy way to do this:
How do you check in ruby if a variable has length?
If I use #dvds.length? I get an error undefined method length for ... when it doesn't have a length.
Basically I've got some searches that mostly retrieve an ActiveRecord:Relation, but one or two simply return one entry since they search by id. I have things like:
#total = #dvds.length
To show how many we've found.
When #dvds does not contain any length, I get an error for undefined method. I did something like:
if #dvds.class == 'ActiveRecord:Relation'
to get around one error, but this is horrible and prone to more errors.
There has got to be a Ruby way of checking whether a variable has length or not that is a lot prettier and easier no?
Ideally I'm looking for something like:
#total = #dvds.length if #dvds.length
Or similar.
Thanks.

You can check whether an object responds to specified method using Object#respond_to? method:
obj.respond_to? :length # true or false

Related

Ruby idiom to shovel into a string or nil? (e.g. shovel or assign / safe shovel)

I'd like to do this:
summary << reason
In my case, summary is a string, containing several sentences, and reason is one such sentence.
This works fine if the target already has a value, but sometimes summary can be nil. In that case, this raises:
NoMethodError: undefined method `<<' for nil:NilClass
So, I could write something like this:
if summary
summary << reason
else
summary = reason
end
This is cumbersome and ugly. I can hide it away in a new method like append(summary, reason), but I'm hoping there's a ruby idiom that can wrap this up concisely.
I've optimistically tried a few variants, without success:
summary += reason
summary &<< reason
In other scenarios, I might build an array of reasons (you can shovel into an empty array just fine), then finally join them into a summary...but that's not viable in my current project.
I also can't seed summary with an empty string (shoveling into an empty string also works fine), as other code depends on it being nil at times.
So, is there a "safe shovel" or simple "shovel or assign" idiom in Ruby, particularly for strings that might be nil?
I prefer #Oto Brglez's answer, but it inspired another solution that might be useful to someone:
summary = [summary, reason].join
This may or may not be easier to read, and probably is less performant. But it handles the nil summary problem without explicit alternation.
You can solve this with something like this; with the help of ||.
summary = (summary || '') + reason
Or like so with the help of ||= and <<:
(summary ||= '') << reason

What is a cleaner way of accessing a nested array+hash in Ruby?

I have the following code, and I feel like there is probably a cleaner way to access the objects that I want:
id = job.args.size > 0 && job.args[0]['arguments'].size > 0 ? job.args[0]['arguments'][0] : nil
This is what dig is for:
id = job.args.dig(0, 'arguments', 0)
dig is defined for Array, Hash, and Struct so it can deal with most kinds of nested structures.
It's hard to give advice on how to better organise the code if we can only see one line of it! Dealing with a messy object like this indicates you may have a wider design issue that could improve the code quality. However...
Based on the above, a "happy scenario" is if:
job.args == [{"arguments"=>["foo"]}]
i.e. An array whose first element is a hash with key 'arguments', which maps to a non-empty array. This looks very messy!
However, you can simplify this to:
job.args.dig(0, 'arguments', 0)
This is applying Array#dig (note: there's also Hash#dig) to chain the method calls and gracefully respond with nil if any fail.
This answer assumes you are using ruby version >= 2.3.0, since this is when dig was added to the language. If you are running an older version, you could also use this gem to back-port the feature.

ruby setting variable versus using variable

I'm somewhat new to ruby so there may be an easy solution to this.
But basically I want to reuse an object #result, so that when I execute a method on it (filter) I continue to be using the original object. However, as I run the method, the object itself seems to be changing.
The object (#result) is RDF::Query::Solutions class
http://rdf.rubyforge.org/RDF/Query/Solutions.html#filter-instance_method
#result = rdf_query(query) # solutions object
At this point the #result contains all the solutions, approximately 30 results
#pubinfo = #result.filter(:ptype => RDF::URI("http://scta.info/pubInfo"))
At this point #result becomes equivalent to what I want only #pubinfo to be. There are only 5 or so results
#contentinfo = #result.filter(:ptype => RDF::URI("http://scta.info/contentInfo"))
at this point #contentinfo comes up nil because the filter is actually on the solutions left from the previous filter. But i wanted to run this filter on the original contents of #result
#linkinginfo = #result.filter(:ptype => RDF::URI("http://scta.info/linkingInfo"))
Again predictable the #linking is 'nil' because #result was set to nil in the previous filter. But I don't want #result changing.
Please help.
update
Look what happens if i try the following
#pubinfo = #result
#pubinfo2 = #pubinfo.filter(:ptype => RDF::URI("http://scta.info/pubInfo"))
binding.pry
At this point #result = has been filtered. Why should should #result be affected at all by what I do to #pubinfo. In other words, how do i make #pubinfo a mere copy or duplicate of #result so that one is not affected by the other??
If you read the documentation:
This method returns an undefined value.
Filters this solution sequence by the given criteria.
This is quite vague, I agree, but one thing stands out - it returns an undefined value, from this I conclude that this is a destructive method, which changes the current object rather than returns a new object with the result of the filter. Another hint to this is that it is Also known as: filter!, since methods ending in ! are by convention destructive in ruby.
Looking at the source code verified this conclusion, as it uses reject! in the code.
As to solutions on how to do it properly - I'm not familiar with this library, and it has proven quite hard to try and figure it out from the documentation, I suggest you find a way to do one of the following (ordered from most recommended, down to last fallback):
Find a non-destructive API
Find a dup or clone API
Re-query before each filter...
And maybe try to contact the author to provide his own recommendation...

Ruby (try_if)_respond_to. Does it exist?

I'm trying to find a shorthand method for doing the following:
if row.respond_to?(:to_varbind_list)
result << row.to_varbind_list.to_hash
else
result << row.to_hash
end
And achieve it with something like this
row.try_if_respond_to(:to_varbind_list).to_hash
Basically row tries to call a method on itself, if that method doesn't exist then just return itself.
Maybe by overriding the Object class or something similar. I'm assuming it's pretty simple how to create my own.
Does Ruby already provide something that does this?
No, ruby does not provide something like this. Also, the Rails try method does not do what you want, since it returns either nil or the method result, but never the original object.
I would say such a method would lead to ambivalent and rather unreadable code since the object that gets the message would be ambivalent. You can surely roll your own, but I find your original code is to the point. If you want to make it shorter in terms of code lines, use ternary operators:
result << (row.respond_to?(:to_varbind_list) ? row.to_varbind_list : row).to_hash

Ruby: Best way to parse a conditional array element

I'm doing API calls that will conditionally return a couple different elements. My code is currently:
if array['productId']
value = array['productId'][0]
end
I feel like there is a more succinct way of doing this. Some Ruby magic.
A better way :
value = array['productId'][0] if array['productId']
However, array['productId'][0] is not ruby natural. What does your array consist of ?
Since Ruby 2.3.0, the shortest way is Array#dig:
array.dig('productId', 0)
http://ruby-doc.org/core-2.3.0_preview1/Array.html#method-i-dig
I am not sure if you are using value just temporarily or actually using it later, and what you want to do with value when the condition is not met. If you want to return nil for missing keys, then
array['productId'].to_a[0]
can work. Otherwise, SpyrosP's answer will be the best.
This might be a bit pedantic, but to make sure it works in all circumstances, you should not check 'not-nil', but rather that it is indexable; something like this:
value = array['productId'][0] if array['productId'].is_a? Array
Or even better:
value = array['productId'][0] if array['productId'].respond_to? '[]'
Otherwise your code will fail if array['productId'] == 2 (which on the other hand seems reasonable, given the key used - I would have gone product_ids instead).
You could use a ternary:
value = array['productId'].nil? ? nil : array['productId'][0]
Your code pattern looks OK; it's possible to be slightly shorter...
value = (t = x['productId']) && t[0]
Using the maybe pattern of Ick, terse and explicit:
value = array['productId'].maybe[0]
While I think your code is fine (although I'd prefer SpyrosP's one-line version), you have some possibilities:
Rails has Object#try, which would let you do either array['productId'].try(:[], 0) or array['productId'].try(:at, 0).
Some people like the andand gem, which defines Object#andand and is used like array['productId'].andand[0].
Ha, I love all the options here. But since I didn't see what I use most, I'll add one more!
value = array['productId'] && array['productId'].first
(I prefer .first to [0] because it reads a little better)
This presumes you'll have an array in array['productId'], which is the right way to do it (rather than type-checking).
Otherwise, the biggest diference between this and your original code, is that this results in value having nil assigned to it if the array doesn't have anything, whereas your original results in value not being defined (which may cause errors, depending on how you use it down the road).
Hope that helps!

Resources