Recursively check if nested elements exist - ruby

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

Related

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

Is there no convenience method for !nil?

I think I would see my code better if I would ask myself object.not_nil? vs !object.nil?. So my question: Is there really no convenience method for !nil? to sugar things up? Is it in front of my eyes and I cannot see it or am I just missing an important point?
How about this?
not object.nil?
But the easier thing to do would be to check for the "truthiness" of by testing the variable itself. Since nil is implicitly false you can just check object.
You can introduce the sugar at an upper level. Instead of:
if not object.nil?
you can write:
unless object.nil?
What about this ?
if object
# sth
end
It is not the same as it will not be executed if object is false but depending on you code, it could be better.
Another solution (which is not the same either), as you tagged your question with ruby-on-rails-3 : using present? which will not execute the block for [] or {} unlike !object.nil?.
Again another one depending of the case : using unless which won't be really nice if your condition is more complex (with && and/or ||).
If your condition is of this form :
if !object.nil? && object.something?
# sth
end
You can use try, as you are using Rails, like this :
if object.try(:something?)
# sth
end
In all the other cases, !object.nil? or not object.nil? stays the best solution I guess.
When convenience around #nil? is discussed, Activesupport's methods #blank? and #present? shouldn't be forgotten either.
Not that you'd necessarily want to, but you can introduce not_nil? yourself:
class Object
def not_nil?
!self.nil?
end
end
then you can do things like:
nil.not_nil?
==> false
3.not_nil?
==> true
a = []
a.not_nil?
==> true

Ruby's 'is_a?' is returning false for built-in types, what's happening?

I've only been learning the deep parts of Ruby for a few months so apologies if this is a bit of a dumb question. I'm attempting to recursively iterate through an Array that may contain nested Arrays and as such I need to check the type of the current element. I have the following code as a small test:
arr = [ 1..2, [3..4, [5..6]], [7..8, [9..10]] ]
arr.each do |node|
p node.class
p node.instance_of? Array
end
When I run it, I get the following output:
Range
false
Array
false
Array
false
I expected the last two to return True, given I have an Array containing a Range and two nested Arrays.
What's even weirder, is if I write the following:
node.class.name == "Array"
It returns True, as it should.
What's happening here?
Ruby Version: MRI 1.9.3-p194
Note: I eventually realised that this was occurring due to the way I namespace my code using modules to avoid code-collision, like so, but also verify object identity in a naughty way:
module MyProg
class MyClass
attr_reader :my_array
def initialize(size)
#my_array = Array.new(size)
end
end
end
MyProg::MyClass.new
Doing this isolates your code but has the downfall of causing all class lookups to be resolved starting from under your namespace. This means that in the above example, my_array.class would actually resolve to MyProg::Array instead of the global Array class.
If you namespace like this and you still want to use this method, you can remedy it by using the double-colon global identifier before the class to force Ruby to begin lookup from the global namespace:
arr.is_a? ::Array
arr.is_a? ::String
Given Ruby's Duck Typing abilities however (and for better code maintenance later on), you should really be testing the behaviour of the object as-per Peter's suggestion below. As such I'm marking his answer as correct for some excellent help given to a learner!
I wrote another answer, but one major question is - why are you doing this? Why not, instead, just call flatten on the array so you just get the entries? Or, check for the behavior of the objects instead. You might need to give more detail about what you require.
You really mean is_a?, which is a more general test to see if the node is of type Array, rather than a specific instance of the specific Array class that you mention. See here for more details. But if you just use is_a? everything will make sense.
I ran your code and got these results.
Range
false
Array
true
Array
true
I'm running ruby 1.9.3p125

Skip iteration from yield block in ruby

Trying to use an ill-conceived framework which collects a list of results from a passed-in block, effectively this:
def sigh(&block)
r = (1..3).collect do |i|
yield(i)
end
# do something with r
end
I want the block I pass in to filter the items, but to skip the collection iteration rather than adding nil to the results like next would (since the framework doesn't compact them.) What's a simple way other than patching the gem? I.e.,
sigh {|i| next unless i == 1 } # results in [1,nil,nil] rather than just [1]
The bad news is that you'll have to patch the gem. Having your code block called by the gem doesn't give your code any special powers to affect how the calling code processes the block's return values.
The good news is that patching the gem can usually be done with a "monkey patch," where your program reopens the gem's class or module and makes the change. In this made-up example, we'll show the class nested in a module, since many gems make use of nested classes and modules:
require 'somegem'
# Monkey patch to cause Somegem's do_something_cool method
# to ignore the SomethingBadHappened exception
module SomeGem
class SomeClass
alias_method :orig_do_something_cool, :do_something_cool
def do_something_cool
orig_do_something_cool
rescue SomethingBadHappened
end
end
end
There is no way to do what you are asking for. If you post more details on the framework you are using, though, someone here may be able to help you think of a different way to work around the problem.
You'll need to patch, like others said. If you want a collection of i which satisfy some condition, the best choice would to replace collect with find_all, and then you could use:
sigh { |i| i == 1 } #=> [1]

How to get argument names using reflection

I would like to do some fairly heavy-duty reflection in Ruby. I want to create a function that returns the names of the arguments of various calling functions higher up the call stack (just one higher would be enough but why stop there?). I could use Kernel.caller, go to the file and parse the argument list but that would be ugly and unreliable.
The function that I would like would work in the following way:
module A
def method1( tuti, fruity)
foo
end
def method2(bim, bam, boom)
foo
end
def foo
print caller_args[1].join(",") #the "1" mean one step up the call stack
end
end
A.method1
#prints "tuti,fruity"
A.method2
#prints "bim, bam, boom"
I would not mind using ParseTree or some similar tool for this task but looking at Parsetree, it is not obvious how to use it for this purpose. Creating a C extension like this is another possibility but it would be nice if someone had already done it for me.
I can see that I'll probably need some kind of C extension. I suppose that means my question is what combination of C extension would work most easily. I don't think caller+ParseTree would be enough by themselves.
As far as why I would like to do this goes, rather than saying "automatic debugging", perhaps I should say that I would like to use this functionality to do automatic checking of the calling and return conditions of functions:
def add x, y
check_positive
return x + y
end
Where check_positive would throw an exception if x and y weren't positive. Obviously, there would be more to it than that but hopefully this gives enough motivation.
In Ruby 1.9.2, you can trivially get the parameter list of any Proc (and thus of course also of any Method or UnboundMethod) with Proc#parameters:
A.instance_method(:method1).parameters # => [[:req, :tuti], [:req, :fruity]]
The format is an array of pairs of symbols: type (required, optional, rest, block) and name.
For the format you want, try
A.instance_method(:method1).parameters.map(&:last).map(&:to_s)
# => ['tuti', 'fruity']
Of course, that still doesn't give you access to the caller, though.
I suggest you take a look at Merb's action-args library.
require 'rubygems'
require 'merb'
include GetArgs
def foo(bar, zed=42)
end
method(:foo).get_args # => [[[:bar], [:zed, 42]], [:zed]]
If you don't want to depend on Merb, you can choose and pick the best parts from the source code in github.
I have a method that is quite expensive and only almost works.
$shadow_stack = []
set_trace_func( lambda {
|event, file, line, id, binding, classname|
if event == "call"
$shadow_stack.push( eval("local_variables", binding) )
elsif event == "return"
$shadow_stack.pop
end
} )
def method1( tuti, fruity )
foo
end
def method2(bim, bam, boom)
foo
x = 10
y = 3
end
def foo
puts $shadow_stack[-2].join(", ")
end
method1(1,2)
method2(3,4,4)
Outputs
tuti, fruity
bim, bam, boom, x, y
I'm curious as to why you'd want such functionality in such a generalized manner.
I'm curious how you think this functionality would allow for automatic debugging? You'd still need to inject calls to your "foo" function. In fact, something based on set_trace_func is more able to be automatic, as you don't need to touch existing code. Indeed this is how debug.rb is implemented, in terms of set_trace_func.
The solutions to your precise question are indeed basically, as you outlined. use caller + parsetree, or open the file and grab the data that way. There is no reflection capability that I am aware of that will let you get the names of arguments. You can approve upon my solution by grabbing the associated method object and calling #arity to then infer what of local_variables are arguments, but though it appears the result of that function is ordered, I'm not sure it is safe to rely on that. If you don't mind me asking, once you have the data and the interface you describe, what are you going to do with it? Automatic debugging was not what initially came to mind when I imagined uses for this functionality, although perhaps it is failing of imagination on my part.
Aha!
I would approach this differently then. There are several ruby libraries for doing design by contract already, including ruby-contract, rdbc, etc.
Another option is to write something like:
def positive
lambda { |x| x >= 0 }
end
def any
lambda { |x| true }
end
class Module
def define_checked_method(name, *checkers, &body)
define_method(name) do |*args|
unless checkers.zip(args).all? { |check, arg| check[arg] }
raise "bad argument"
end
body.call(*args)
end
end
end
class A
define_checked_method(:add, positive, any) do |x, y|
x + y
end
end
a = A.new
p a.add(3, 2)
p a.add(3, -1)
p a.add(-4, 2)
Outputs
5
2
checked_rb.rb:13:in `add': bad argument (RuntimeError)
from checked_rb.rb:29
Of course this can be made much more sophisticated, and indeed that's some of what the libraries I mentioned provided, but perhaps this is a way to get you where you want to go without necessarily taking the path you planned to use to get there?
if you want the value for the default values, too, there's the "arguments" gem
$ gem install rdp-arguments
$ irb
>> require 'arguments'
>> require 'test.rb' # class A is defined here
>> Arguments.names(A, :go)
In fact, the method you describe clearly fails to distinguish arguments from local variables while also failing to work automatically
That's because what you're trying to do is not something which is supported. It's possible (everything is possible in ruby), but there's no documented or known way to do it.
Either you can eval the backtrace like what logan suggested, or you can bust out your C compiler and hack sourcecode for ruby. I'm reasonably confident there aren't any other ways to do this.

Resources