You can check if an array contains a particular element using the include?(obj) method.
browsers = ['chrome', 'safari', 'firefox']
browsers.include?('chrome') # true
browsers.include?('explorer') # falseThis code is already very simple and readable. But, can we go one step further and call a method named chrome? on the array itself?
browsers = ['chrome', 'safari', 'firefox']
browsers.chrome? # should return true
browsers.explorer? # should return falseWith a little bit of metaprogramming in Ruby, we absolutely can.
Extending Array
Let's create a new class that extends Ruby's Array class and override the method_missing method on it. This method is defined on the BasicObject class, which all Ruby classes inherit from, and the Ruby interpreter calls it when it can't find the method on an object.
class ArrayChecker < Array
def method_missing(name, *args)
if name.end_with?("?")
any?(name[0..-2])
else
super
end
end
endBy default, the interpreter raises an error when this method is called. However, you can override this method to provide more dynamic behavior and handle undefined methods.
class Person
def method_missing(symbol, *args)
puts "You called '#{symbol}' method which doesn't exist."
end
end
ak = Person.new
ak.name # You called 'name' method which doesn't exist.The ArrayChecker class overrides the method_missing method and checks if the method that was called ends with ?. If it does, then it calls the any? method on the array, passing the method name without the ?. This method returns true if the array contains the value passed.
For example, if you call browsers.chrome? where browsers is an instance of the ArrayChecker class, the Ruby interpreter will first check if there's a method named chrome? on ArrayChecker.
Since ArrayChecker doesn't include chrome? method, Ruby will call the method_missing, which in turn calls browsers.any?('chrome') , checking if the array includes a value named 'chrome'. If yes, it returns true, returning false otherwise.
This allows us to write the following code.
browsers = [:chrome, :safari, :firefox]
browsers.chrome? # true
browsers.explorer? # falseUse ActiveSupport::ArrayInquirer
Luckily, if you're using Ruby on Rails, you don't have to write the above code yourself. The Active Support library provides an ArrayInquirer class which handles it for you. It also supports both string and symbol values.
Wrapping an array in ArrayInquirer provides a friendlier way to check its contents:
browsers = ActiveSupport::ArrayInquirer.new([:chrome, :firefox])
browsers.chrome? # => true
browsers.firefox? # => true
browsers.explorer? # => falseIn addition, this class also overrides the any? method, so it checks for both the stringified and symbolized form of any element in the array.
browsers = ActiveSupport::ArrayInquirer.new([:chrome, :firefox])
browsers.any? # true
browsers.any?(:chrome, :firefox) # true
browsers.any?(:explorer, :firefox) # true
browsers.any?(:explorer, :desktop) # false
browsers.any?('chrome') # true
browsers.any?('explorer') # falseYou can check the implementation of this class on Github.
The inquiry Helper
In addition, Active Support opens the Array class and adds the inquiry method to it. That way, you don't have to write ActiveSupport::ArrayInquirer.new(array) every time. Instead, you can simply call array.inquiry method to get the same effect.
pets = [:cat, :dog].inquiry
pets.cat? # => true
pets.horse? # => false
pets.any?(:cat, :horse) # => true
pets.any?(:horse, :cow) # => falseHere's the implementation of the Array#inquiry method. It instantiates the ArrayInquirer class, passing self which is the array itself.
class Array
def inquiry
ActiveSupport::ArrayInquirer.new(self)
end
endSo this concludes our exploration of the ArrayInquirer class. You can use this class as a readable way to check the contents of an array.
Hope you found this post helpful.