When we have a
Member model where each user has a specific number of coins. When performing operations like capturing or deducting coins, we want to validate:1. The user exists or not.
2. The user has enough coins to complete the operation.
The basic validation will look like this (with
dry-validation gem)Ruby
class CoinsValidator < Dry::Validation::Contract
params do
required(:user).filled(:integer)
required(:coins).filled(:integer, gt?: 0)
end
rule(:user) do
member = Member.find_by(unique_id: value) // querying database for member details
key.failure("does not exist") if member.nil?
end
rule(:coins, :user) do
member = Member.find_by(unique_id: values[:user]) // querying database for member details
if member && member.coins < values[:coins]
key(:coins).failure("are insufficient")
end
end
endBut the problem here is, We query the database twice to fetch the same
Member object once in the :user rule and again in the :coins rule. This redundant querying increases database load and slows down validation, especially in high-traffic applications.To avoid redundant queries, we can cache the
Member object using the values hash provided by the dry-validation gem. The values hash allows us to store intermediate results, making them accessible to other validation rules.The optimized code looks like this:
Ruby
class CoinsValidator < Dry::Validation::Contract
params do
required(:user).filled(:integer)
required(:coins).filled(:integer, gt?: 0)
end
rule(:user) do
values[:member] = Member.find_by(unique_id: value) // Caching the member object
key.failure("does not exist") if values[:member].nil?
end
rule(:coins) do
member = values[:member] // accessing the cached Member from values[:member] rather than querying the database again.
if member && member.coins < value
key.failure("are insufficient")
end
end
end#ruby on rails