Every rubist who has worked with
Ruby on Rails is familiar with the
ORM ActiveRecord . Let's discuss one of the validations proposed from the box, namely, validation for uniqueness, and why
database_validations gem will save the consistency of your database.
Suppose you have a user model with a unique identity on the
email field, i.e.
class User < ApplicationRecord validates :email, uniqueness: true end
You may already know that this validation performs the following request.
SELECT 1 FROM users WHERE email = $1
every time we try to save a record to the database.
')
This approach has several drawbacks:
First , the execution of an additional request, and if the model initializes several validations for uniqueness, the request will be executed for each of them. This is not efficient, and also requires the presence of indices if we want these queries to be executed quickly.
Secondly , this solution does not guarantee uniqueness due to the possible
race for data . Several competitive operations can simultaneously find out about the absence of a specific record, as a result of which, keep the same data.
Of course, it is possible to allow rare cases with data race by adding a unique constraint at the database level. But in this case, you will not get a validation error, the query to the database will simply fall and the entire transaction will be rolled back.
Gem
database_validations , which provides compatibility between database constraints and validations, will help in this situation.
The main meaning of gem is presented in the following code:
def save(options = {}) ActiveRecord::Base.connection.transaction(requires_new: true) { super } rescue ActiveRecord::RecordNotUnique => e Helpers.handle_unique_error!(self, e) false end
Thus, we try to save the data, if all other validations are passed, if the transaction drops and rolls back, we parse the error and set the correct values in the
errors
our object.
After reviewing the
documentation and
benchmarks , we can conclude that this gem will speed up the process of saving records in the database at least twice.
With support for databases such as
PostgreSQL ,
SQLite ,
MySQL, and backward compatibility with
validates_uniqueness_of
, the replacement process for
validates_db_uniqueness_of
takes a few minutes.
A convenient matcher for RSpec is also present out of the box:
specify do expect(described_class) .to validate_db_uniqueness_of(:field) .with_message('duplicate') .with_where('(some_field IS NULL)') .scoped_to(:another_field) .with_index(:unique_index) end
When you switch to a new validation, you need to have uniqueness restrictions in the database, but if they are not there yet, gem will indicate this during the launch of the application.
The heme has been tested on an application with 100+ uniqueness validations among 50+ models.
Use gems and share opinions. Any contribution to further development is welcome!