Ruby (try_if)_respond_to. Does it exist? - ruby

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

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

Chaining methods in Ruby, and injecting a block into the chain

Consider the following Ruby expression:
y=x.a.b.c.d.e.f
Of course, x is an object and a to f are methods defined for a class which matches the return value of the previous method in the chain. Now say that I want to replace the invocation of method c by a custom block, i.e. I would like to achieve the effect of
temp=x.a.b
temp1=.... (calculate something based on the value of temp)
y=temp1.d.e.f
but with using method chaining.
It is of course trivial to define a suitable method to achieve this:
class Object
def pass
yield(self)
end
end
which would allow me to write something like
y=x.a.b.pass {|the_b| .....}.d.e.f
Now to my question:
Given that Ruby already has a method for a similar problem (Object#tap), I wonder why it does not have a method similar to the Object#pass which I just explained. I suspect, that either
(a) Ruby already offers a feature like this, and I'm just to stupid to find it, or
(b) What I want to achieve would be considered bad programming style (but then, why?)
Is (a) or (b) correct, or did I miss something here?
(a) Yes. Ruby already has that. It is called yield_self.
(b) No. It is not a bad style.

Recursively check if nested elements exist

Just to give you a background, I'm using Ruby for creating automated tests along with Selenium, Cucumber, Capybara and SitePrism. I have some tests that need to check the text of a certain element on the page, for example:
def get_section_id
return section.top.course.section_id.text
end
However, I would like to check if all the parent elements exist before calling .text on the nested course_and_section_id element. For example, to check the text of this particular element I would do:
if(has_section? && section.has_top? && section.top.has_course? && section.top.course.has_section_id?)
return section.top.course.section_id.text
end
Is there any way to recursively check if something exists in Ruby like this? Something that could be called like: has_text?(section.top.course.section_id) maybe?
There is nothing builtin to ruby that would do this because the methods you're calling return the element, or raise an exception. If they returned the element or nil then the suggestion of Cary Swoveland to use &. would be the answer.
The critical thing to remember here is what you're actually trying to do. Since you're writing automated tests, you're (most likely) not trying to check whether or not the elements exist (tests should be predictable and repeatable so you should know the elements are going to exist) but rather just wait for the elements to exist before getting the text. This means what you really want is probably more like
def get_section_id
wait_until_section_visible
section.wait_until_top_visible
section.top.wait_until_course_visible
section.top.course.wait_until_section_id_visible
return section.top.course.section_id.text
end
You can write a helper method to make that easier, something like
def get_text_from_nested_element(*args)
args.reduce(self) do |scope, arg|
scope.send("wait_until_#{arg}_visible")
scope.send(arg)
end.text
end
which could be called as
def get_section_id
get_text_from_nested_element(:section, :top, :course, :section_id)
end
It sounds like you may want something like the following.
arr = [section, :top, :course, :section_id, :text]
arr.reduce { |e,m| e && e.respond_to?(m) && e.public_send(m) }
Because reduce has no argument the initial value of the memo e is section. If e becomes nil or false it will remain that value.
Whilst this is a bit outdated, the fact that &. won't work here when it is the most elegant perhaps gives rise for this being a useful feature
If you can raise it on GH with a sample page where this would be useful then we could look at getting it introduced
Luke

Ruby, evaluate one ruby function or another using the same DO block

In ERB or HAML, I need to be able to evaluate one function, or a different one, based on the output of a conditional, while using the same HTML block for either one.
In HAML, it would like something like this:
- is_a = #thing.is_a?
= (is_a ? f.method_a : f.method_b) arg_1, |block_arg1| do
#thing
.blah
.inner-thing
= block_arg1.some_method
Notice how, on line 2 of my pseudocode, I am trying to evaluate either one function, or the other, based upon a conditional. But I need pass the same arguments to either, especially since I don't want to have to re-type the DO block.
Maybe I could avoid that problem ("I don't want to re-type...") by making that DO block a named function? How does one turn a HAML or ERB block into a named Ruby function?
I'm using Ruby-on-Rails 4. Not that it matters; this looks like more of a ruby syntax question than a framework question.
You seem to be looking for #send
- is_a = #thing.is_a?
= f.send(is_a ? :method_a : :method_b, arg_1) do |block_arg|
(...)

what is a variable/object/thing starting with a # in ruby

I am new to Ruby, so go easy :).
Anyway, I am trying to work out some stuff in the chef-provisioning-aws gem. One thing in particular is that there is an object called:
new_resource.driver.ec2_client
When I do an inspect on that object it returns exactly this:
#<Aws::EC2::Client>
So, my question is: what is this? What sort of "thing" in Ruby starts with a pound sign (hash) and has <...> in it?
Much appreciated.
The output of Ruby's inspect method is roughly like this unless you override it:
"#<#{self.class}:0x#{self.object_id.to_s(16)}"
So in other words: "#<ClassName:0xobject_id in hex>".
class Foo ; end
Foo.new.inspect
#=> "#<Foo:0x007ffe0eeea520>"
It seems that for Aws::EC2::Client it was overriden and does not include the object id.
It's the default implementation of the inspect method (although then the object ID should be included). It may be useful in some situations to implement your own method, so you can get an instant overview of what you want to know about this object. The # is just a random character and has no further meaning here.

Resources