In the previous article, we dived into
ActiveSupport::StringInquirer class and superpowers it gives to
String objects. After I had published that article I decided to take another look at the
ActiveSupport module and to my surprise, I found something even more interesting,
If you read the previous article you might have already guessed what
ArrayInquirer does. Directly speaking, it gives superpowers to
To understand it better, let’s start with a simple array of names:
names = ['Tom', 'Adam', 'Igor']
If I wanted to check if the array contains my name I would use one of the handy methods from
Enumerable Ruby module2:
names.include?("Igor") => true
According to a comment3 placed directly inside
Wrapping an array in an `ArrayInquirer` gives a friendlier way to check its string-like contents.
Let’s check if using the class would make finding my name easier:
names_with_superpowers = ActiveSupport::ArrayInquirer.new(names) => NameError: uninitialized constant ActiveSupport::ArrayInquirer from (pry):2:in `<main>'
The class has been officially present in Ruby on Rails source code since version 5.0.0, so to make the above code working I had to switch to an up-to-date version of Rails first:
names = ['Tom', 'Adam', 'Igor'] => ['Tom', 'Adam', 'Igor'] names_with_superpowers = ActiveSupport::ArrayInquirer.new(names) => ['Tom', 'Adam', 'Igor'] names_with_superpowers.class => ActiveSupport::ArrayInquirer names_with_superpowers.Igor? => true names_with_superpowers.Rob? => false
And Voila! Similarly to
ActiveSupport::StringInquirer the class adds some useful methods to
Array objects using metaprogramming.
To understand it fully let’s analyse its body step-by-step:
module ActiveSupport class ArrayInquirer < Array def any?(*candidates) if candidates.none? super else candidates.any? do |candidate| include?(candidate.to_sym) || include?(candidate.to_s) end end end private def respond_to_missing?(name, include_private = false) (name[-1] == "?") || super end def method_missing(name, *args) if name[-1] == "?" any?(name[0..-2]) else super end end end end
Arrayso all its methods are available to
Igor?method is not defined in
Arrayclass nor in
method_missingis executed as a fallback.
method_missingchecks if a method name ends with
- In our case, it ends with
any?public method is called with
Igoras an argument (
any?method received an argument
ifblock is executed.
elseblock checks if the array includes the passed argument(s) either as a string or as a symbol.
['Tom', 'Adam', 'Igor']array includes
names_with_superpowers.Igor?returns true finally.
Behind the scenes, the class uses the same
include? method I used initially 🙂
inquirymethod4 which wraps the current array in the
In addition to magic methods like
Igor? you can also use the public
any? method on
ArrayInquirer object to check if at least one of passed arguments is present in an array:
names_with_superpowers.any?("Igor") => true names_with_superpowers.any?("Rob", "Igor") => true names_with_superpowers.any?("Rob", "Bob") => false # Neither "Rob", nor "Bob" is present in the array
Thanks to spending some time on analysing Ruby on Rails source code I found two new classes that I potentially can use.
At the same time, I would like to give you the very same advice I gave in the previous article. Please always do some benchmarking before you decide to use either
ArrayInquirer. They may be slow.
Interestingly enough, Rails uses the
ArrayInquirer class only in one place5 so far.
ArrayInquirier object, so instead of writing:
You can write:
I do not recommend using
any? in this very case, though 🙂