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, ActiveSupport::ArrayInquirer class .

If you read the previous article you might have already guessed what ArrayInquirer does. Directly speaking, it gives superpowers to Array objects.

What does ActiveSupport::ArrayInquirer do?

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 module :

names.include?("Igor") => true

According to a comment placed directly inside ActiveSupport::ArrayInquirer class:

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

ArrayInquirer inherits from Array so all its methods are available to ArrayInquirer objects. Igor? method is not defined in Array class nor in ArrayInquirer , so method_missing is executed as a fallback. method_missing checks if a method name ends with ? . In our case, it ends with ? , so any? public method is called with Igor as an argument ( Igor[0..-2] ) Because any? method received an argument else part of if block is executed. The else block checks if the array includes the passed argument(s) either as a string or as a symbol. The ['Tom', 'Adam', 'Igor'] array includes 'Igor' string so names_with_superpowers.Igor? returns true finally.

Behind the scenes, the class uses the same include? method I used initially 🙂

Similarly to String class, Array also defines inquiry method which wraps the current array in the ArrayInquirer class.

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

Summary

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 StringInquirer or ArrayInquirer . They may be slow.

Interestingly enough, Rails uses the ArrayInquirer class only in one place so far. request.variant returns ArrayInquirier object, so instead of writing:

request.variant.include?(:phone)

You can write:

request.variant.phone?

Or:

request.variant.any?(:phone)