All Projects → toptal → Database_validations

toptal / Database_validations

Licence: mit
Database validations for ActiveRecord

Programming Languages

ruby
36898 projects - #4 most used programming language

Projects that are alternatives of or similar to Database validations

Database consistency
The tool to find inconsistency between models schema and database constraints.
Stars: ✭ 418 (+52.55%)
Mutual labels:  activerecord, database, ruby-on-rails
Active record doctor
Identify database issues before they hit production.
Stars: ✭ 865 (+215.69%)
Mutual labels:  activerecord, database, performance
Goldiloader
Just the right amount of Rails eager loading
Stars: ✭ 1,074 (+291.97%)
Mutual labels:  activerecord, ruby-on-rails, performance
active record-updated at
Touch `updated_at` by default with calls to `update_all` and `update_column(s)`
Stars: ✭ 27 (-90.15%)
Mutual labels:  activerecord, ruby-on-rails
Occams Record
The missing high-efficiency query API for ActiveRecord
Stars: ✭ 240 (-12.41%)
Mutual labels:  activerecord, performance
Scenic
Scenic is maintained by Derek Prior, Caleb Hearth, and you, our contributors.
Stars: ✭ 2,856 (+942.34%)
Mutual labels:  activerecord, database
Querybuilder
SQL query builder, written in c#, helps you build complex queries easily, supports SqlServer, MySql, PostgreSql, Oracle, Sqlite and Firebird
Stars: ✭ 2,111 (+670.44%)
Mutual labels:  activerecord, database
nxt state machine
A simple but powerful state machine implementation.
Stars: ✭ 14 (-94.89%)
Mutual labels:  activerecord, ruby-on-rails
prefixed ids
Friendly Prefixed IDs for your Ruby on Rails models
Stars: ✭ 159 (-41.97%)
Mutual labels:  activerecord, ruby-on-rails
activerecord-cockroachdb-adapter
CockroachDB adapter for ActiveRecord.
Stars: ✭ 90 (-67.15%)
Mutual labels:  activerecord, ruby-on-rails
filtered
Filters ActiveRecord queries in a nice way
Stars: ✭ 28 (-89.78%)
Mutual labels:  activerecord, ruby-on-rails
Octopus
Database Sharding for ActiveRecord
Stars: ✭ 2,496 (+810.95%)
Mutual labels:  activerecord, ruby-on-rails
Seamless database pool
Add support for master/slave database clusters in ActiveRecord to improve performance.
Stars: ✭ 222 (-18.98%)
Mutual labels:  activerecord, database
activerecord-crate-adapter
Ruby on Rails ActiveRecord adapter for CrateDB
Stars: ✭ 27 (-90.15%)
Mutual labels:  activerecord, ruby-on-rails
Anima
Minimal database operation library.
Stars: ✭ 210 (-23.36%)
Mutual labels:  activerecord, database
activerecord-setops
Union, Intersect, and Difference set operations for ActiveRecord (also, SQL's UnionAll).
Stars: ✭ 21 (-92.34%)
Mutual labels:  activerecord, ruby-on-rails
rails cursor pagination
Add cursor pagination to your ActiveRecord backed application
Stars: ✭ 21 (-92.34%)
Mutual labels:  activerecord, ruby-on-rails
LocalSupport
A directory of local support services and volunteer opportunities
Stars: ✭ 60 (-78.1%)
Mutual labels:  activerecord, ruby-on-rails
Panko serializer
High Performance JSON Serialization for ActiveRecord & Ruby Objects
Stars: ✭ 266 (-2.92%)
Mutual labels:  activerecord, performance
Counter culture
Turbo-charged counter caches for your Rails app.
Stars: ✭ 1,397 (+409.85%)
Mutual labels:  activerecord, database

DatabaseValidations

CircleCI Gem Version Maintainability

DatabaseValidations helps you to keep the database consistency with better performance. Right now, it supports only ActiveRecord.

The more you use the gem, the more performance increase you have. Try it now!

Installation

Add this line to your application's Gemfile:

gem 'database_validations'

And then execute:

bundle

Or install it yourself as:

gem install database_validations

Have a look at example application for details.

Benchmark (code)

Image, you have User model defines as

class User < ActiveRecord::Base
  validates :email, :full_name, uniqueness: true

  belongs_to :company
  belongs_to :country
end

and then replace with

class User < ActiveRecord::Base
  validates :email, :full_name, db_uniqueness: true
  # OR
  # validates_db_uniqueness_of :email, :full_name

  db_belongs_to :company
  db_belongs_to :country
  # OR
  # belongs_to :company
  # belongs_to :country
  # validates :company, :country, db_presence: true
end

you will get the following performance improvement:

Caveats

  • db_belongs_to doesn't work with SQLite due to a poor error message.
  • In Rails 4, the gem validations work differently than the ActiveRecord ones when validate: false option is passed to save/save!. They incorrectly return a validation message instead of raising a proper constraint violation exception. In Rails >= 5 they correctly raise the exceptions they supposed to.

db_belongs_to

Supported databases are PostgreSQL and MySQL. Note: Unfortunately, SQLite raises a poor error message by which we can not determine exact foreign key which raised an error.

Usage

class User < ActiveRecord::Base
  db_belongs_to :company
end

user = User.create(company_id: nil)
# => false
user.errors.messages
# => {:company=>["must exist"]}

Problem

ActiveRecord's belongs_to has optional: false by default. Unfortunately, this approach does not ensure existence of the related object. For example, we can skip validations or remove the related object after we save the object. After that, our database becomes inconsistent because we assume the object has his relation but it does not.

db_belongs_to solves the problem using foreign key constraints in the database also providing backward compatibility with nice validations errors.

Pros and Cons

Advantages:

  • Ensures relation existence because it uses foreign keys constraints.
  • Checks the existence of proper foreign key constraint at the boot time. Use ENV['SKIP_DB_UNIQUENESS_VALIDATOR_INDEX_CHECK'] = 'true' if you want to skip it in some cases. (For example, when you run migrations.)
  • It's almost two times faster because it skips unnecessary SQL query. See benchmarks below for details.

Disadvantages:

  • Cannot handle multiple database validations at once because database raises only one error per query.

Configuration options

Option name PostgreSQL MySQL
class_name + +
foreign_key + +
foreign_type - -
primary_key + +
dependent + +
counter_cache + +
polymorphic - -
validate + +
autosave + +
touch + +
inverse_of + +
optional - -
required - -
default + +

Benchmarks (code)

validates_db_uniqueness_of

Supported databases are PostgreSQL, MySQL and SQLite.

Usage

class User < ActiveRecord::Base
  validates :email, db_uniqueness: true
  # The same as following:
  # validates :email, uniqueness: {case_sensitive: true, allow_nil: true, allow_blank: false}
end

original = User.create(email: '[email protected]')
dupe = User.create(email: '[email protected]')
# => false
dupe.errors.messages
# => {:email=>["has already been taken"]}
User.create!(email: '[email protected]')
# => ActiveRecord::RecordInvalid Validation failed: email has already been taken

Complete case_sensitive replacement example (for PostgreSQL only):

validates :slug, uniqueness: { case_sensitive: false, scope: :field }

Should be replaced by:

validates :slug, db_uniqueness: {index_name: :unique_index, case_sensitive: false, scope: :field}

Keep in mind: because valid? method uses default validator you should:

  • if your index has many fields, provide proper scope option
  • if your index has lower function, provide case_sensitive option
  • if your index has where condition, provide proper where option

Problem

Unfortunately, ActiveRecord's validates_uniqueness_of approach does not ensure uniqueness. For example, we can skip validations or create two records in parallel queries. After that, our database becomes inconsistent because we assume some uniqueness over the table but it has duplicates.

validates_db_uniqueness_of solves the problem using unique index constraints in the database also providing backward compatibility with nice validations errors.

Advantages

  • Ensures uniqueness because it uses unique constraints.
  • Checks the existence of proper unique index at the boot time. Use ENV['SKIP_DB_UNIQUENESS_VALIDATOR_INDEX_CHECK'] = 'true' if you want to skip it in some cases. (For example, when you run migrations.)
  • It's two times faster in average because it skips unnecessary SQL query. See benchmarks below for details.
  • It has different modes so you can pick up the best for your needs.

Configuration options

Option name PostgreSQL MySQL SQLite
mode + + +
scope + + +
message + + +
if + + +
unless + + +
index_name + + -
where + - -
case_sensitive + - -
allow_nil - - -
allow_blank - - -

Modes

There are 3 mode options:

  • :optimized - the default one. In this mode it turns DB constraint exceptions into proper validation messages.
  • :enhanced - a combination of the standard uniqueness validation and the db uniqueness validation. Runs a query first but also rescues from exception. The preferable mode for user-facing validations.
  • :standard - in this mode works pretty much the same way as validates_uniqueness_of (except the index existence check).

Benchmark (code)

Testing (RSpec)

Add require database_validations/rspec/matchers' to your spec file.

validate_db_uniqueness_of

Example:

class User < ActiveRecord::Base
  validates_db_uniqueness_of :field, message: 'duplicate', where: '(some_field IS NULL)', scope: :another_field, index_name: :unique_index
end

describe 'validations' do
  subject { User }

  it { is_expected.to validate_db_uniqueness_of(:field).with_message('duplicate').with_where('(some_field IS NULL)').scoped_to(:another_field).with_index(:unique_index) }
end

Using with RuboCop

DatabaseValidations provides custom cops for RuboCop to help you consistently apply the improvements. To use all of them, use rubocop --require database_validations/rubocop/cops or add to your .rubocop.yml file:

require:
  - database_validations/rubocop/cops

Or you case use some specific cop directly:

require:
  - database_validations/rubocop/cop/belongs_to
  - database_validations/rubocop/cop/uniqueness_of

Development

You need to have installed and running postgresql and mysql. And for each adapter manually create a database called database_validations_test accessible by your local user.

Then, run rake spec to run the tests.

To check the conformance with the style guides, run:

rubocop

To run benchmarks, run:

ruby -I lib benchmarks/composed_benchmarks.rb

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the DatabaseValidations project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.

Contributors

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].