presence_in Explained

The method presence_in, which is part of ActiveSupport, is used to check if a value is present in a given collection and returns the value if it is present; otherwise, it returns nil. However, it can also raise an ArgumentError if the argument does not respond to the include? method. In this blog post, we will explore the presence_in method, how it works, and common pitfalls to avoid.

How presence_in Works

The presence_in method is designed to simplify the process of checking whether a value exists within a collection. Here’s a simple example:

1
2
3
4
5
6
7
8
9
valid_values = %w( project calendar task )

value = 'calendar'
result = value.presence_in(valid_values)
# result will be 'calendar' because 'calendar' is present in valid_values

value = 'unknown'
result = value.presence_in(valid_values)
# result will be nil because 'unknown' is not present in valid_values

In this example, value.presence_in(valid_values) returns the value itself if it is found in the valid_values array, otherwise, it returns nil.

Practical Usage

A common use case for presence_in is to validate parameters in a Rails controller:

1
2
3
4
5
6
7
8
9
10
11
class ProjectsController < ApplicationController
  def create
    bucket_type = params[:bucket_type].presence_in(%w( project calendar task ))
    
    if bucket_type
      # Proceed with bucket_type
    else
      # Handle invalid bucket_type
    end
  end
end

In this scenario, params[:bucket_type] is checked against a list of valid bucket types. If it is found in the list, it proceeds; otherwise, it handles the invalid case.

Avoiding ArgumentError

While presence_in is very useful, it can raise an ArgumentError if the argument passed does not respond to the include? method. Let’s look at an example that would cause this error:

1
2
3
4
5
6
7
params[:bucket_type] = 'calendar'

# Suppose we pass an object that does not respond to the `include?` method
invalid_collection = 12345

# This will raise an ArgumentError because 12345 does not respond to `include?`
result = params[:bucket_type].presence_in(invalid_collection)

In this example, invalid_collection is an integer, which does not respond to the include? method. When you try to use presence_in with such an argument, Rails raises an ArgumentError.

Full Example with Error Handling

Here’s a complete script demonstrating both correct usage and error handling:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
params = { bucket_type: 'calendar' }

# Correct usage
valid_values = %w( project calendar task )
result = params[:bucket_type].presence_in(valid_values)
puts result  # Outputs 'calendar'

# Incorrect usage leading to ArgumentError
invalid_collection = 12345

begin
  result = params[:bucket_type].presence_in(invalid_collection)
rescue ArgumentError => e
  puts "Error captured: #{e.message}"  # Outputs 'Error captured: argument doesn’t respond to #include?'
end

The presence_in method is a handy tool for checking the presence of a value within a collection in Ruby on Rails. However, it’s important to ensure that the argument passed is a valid collection that responds to the include? method to avoid unexpected errors.