How to write a ruby function that can accept either a string or a regular expression

By splintor

While working on our unit test framework, I wanted to write a function that checks if a given Ext JS windoid is opened. The function should accept a title argument. This was easily implemented as:

	# title is a string with the exact windoid title
	def windoidDisplayed?(title)
		spans = $browser.spans
		t = $browser.length && $browser.detect {|i| \
			i.class_name == 'x-window-header-text' \
			&& title == i.text \
		}
		return t && t.visible?
	end

I then realized that users might not have the full title of the window, and might only want to check for a windoid with a title that matches some regular expression. A simple and naive way to do it is

	# title can be a string or a regular expression
	def windoidDisplayed?(title)
		spans = $browser.spans
		t = $browser.length && $browser.detect {|i| \
			i.class_name == 'x-window-header-text' \
				&& (title.kind_of?(String) \
					? (title == i.text) \
					: (title =~ i.text)) \
		}
		return t && t.visible?
	end

However, I didn’t want to check in every loop iteration if the passed title argument is regular expression. Although performance is really a non-issue in our unit tests, it just seemed to be a cumbersome and ugly code. I thought of using the dynamic nature of ruby to add a method (e.g. “match?”) to both string and regular expression classes, so I can call it in a polymorphic way without having to detect the type of title:

	class String
		def match?(x)
			return self == x
		end
	end

	class Regexp
		def match?(x)
			return self.match(x)
		end
	end

	# title can be a string or a regular expression
	def windoidDisplayed?(title)
		spans = $browser.spans
		t = $browser.length && $browser.detect {|i| \
				i.class_name == 'x-window-header-text' \
				&& title.match?(i.text) \
		}
	return t && t.visible?

Before implementing it, I thought I’d check if a similar function already exists in Ruby’s Standard Library, and indeed, when I looked up the Ruby book for functions that are in both String and Regexp classes, I run into “===” (the Case Equality operator), which is meant exactly for this kind of things. Unlike JavaScript, where the === operator is meant for identity, in Ruby, the === method is usually called in case expressions to match the case target for each when clause. As said in the documentation, for the String class, === method tests for equality and is the same as ==, but for Regexp class, === method tests for matches, and is the same as =~. Exactly what I was looking for. So my little function can simply be implemented like:

	# title can be a string or a regular expression
	def windoidDisplayed?(title)
		spans = $browser.spans
		t = $browser.length && $browser.detect {|i| \
				i.class_name == 'x-window-header-text' \
				&& title === i.text \
		}
		return t && t.visible?
	end

BTW, while searching for that, I took a look at Watir’s sources, hoping to learn from the experts as I’m new to Ruby.
I found that Watir source (watir.rb, lines 50-70) does a similar trick to what I initially thought to do – define a new “matches” method (and not “match?” like the ruby convention that is also used in Watir in methods like “exists?” and “visible?“), and don’t use the === method.
In addition, in several places in the code there are still explicit tests for String class, such as the verify_contains method in input_elements.rb and navigate_to_link_with_id and navigate_to_link_with_url methods in watir_simple.rb.

Leave a Reply