Rails 7.0 has introduced two methods sole
and find_sole_by
that are used to query the database for a unique record based on the provided constraints.
Assertions are one of the most useful tools in Ruby, and they’re even more useful when you use them to test your application’s data. By asserting that the data you got back from an object or collection is what you expect, you can ensure that your code works as expected.
For example, if you have a table that contains data on films and their casts, you may want to assert that the cast for a film exists.
This feature can be used in several ways:
-
To verify that the result of calling a model method is an instance of your expected class
-
To verify that the result of calling a model method returns true for your desired attribute name and value pair
-
To verify that a query returns at least one result
Here it is before Rails 7.0
Suppose we want to find a product with said price price
and it only has to be one:
1
2
3
4
5
6
7
8
9
product = Product.where(price: price).count
if product.count == 1
puts product.first
elsif product.count == 0
puts "No products found"
else
puts "More than one product found"
end
The complexity with implementations such as above increases especially when we’ll need to query between multiple models to get our result.
Here it is after Rails 7.0
The find_sole_by
method is similar in function to find_by
, the only difference is that it asserts that the record being retrieved is the only one that exists. From the previous method, we can have a reduced form as below:
1
2
3
4
5
6
product = Product.find_sole_by(price: price)
# => ActiveRecord::RecordNotFound (if no matching Product with the price provided)
# => #<Product ...> (if one Product with the price provided)
# => ActiveRecord::SoleRecordExceeded (if more than one Product with the price provided)
For the method sole
, its functionality is similar to first
, however, it only extracts the results when it’s the only unique one matching the constraints provided.
1
2
3
4
5
6
product = Product.where(price: price).sole
# => ActiveRecord::RecordNotFound (if no matching Product with the price provided)
# => #<Product ...> (if one Product with the price provided)
# => ActiveRecord::SoleRecordExceeded (if more than one Product with the price provided)
So, it takes an options hash that can be used to specify which fields are required and whether or not they should be present in the result.
This method also takes advantage of ActiveCast—a feature introduced in Rails 5 that allows you to access attributes on related models without loading them into memory at all times (which could cause performance issues).
More on this can be read here.