Skip to content

Commit

Permalink
Cleanup README
Browse files Browse the repository at this point in the history
  • Loading branch information
agios committed Oct 21, 2023
1 parent ef2bcda commit 86afa26
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 57 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Run tests
name: all tests
on: push

jobs:
Expand Down
84 changes: 28 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,75 +1,47 @@
# Unconstrained

Unconstrained for Active Record converts exceptions raised because of
underlying database constraints to appropriate ActiveModel::Errors
underlying foreign key constraints to appropriate ActiveModel::Errors

## Usage

In your Gemfile:
For example, if trying to insert a record in a model `Book`, which
`belongs_to :author` using an invalid `author_id`, instead of getting
an exception such as:

```ruby
gem 'unconstrained'
```

## Notes

Only constraints that have to do with referential integrity are handled.
This is intentional, as these require a roundrip to the database anyway.
Constraints that can be enforced using validations in the application
should be checked that way.

Currently only handles PostgreSQL.

## Reasoning

Rails supports the `dependent` option on `has_one` / `has_many`
associations, which can be used to a similar effect, ie:

```ruby
has_many :children, dependent: :restrict_with_error
PG::ForeignKeyViolation: ERROR: insert or update on table "books"
violates foreign key constraint "fk_rails_xxxxxxxxxx"
DETAIL: Key (author_id)=(xxxxxxxxxx) is not present in table "authors".
```

However, this has drawbacks. In the simple case, it offers no particular
advantage, since it still needs a roundrip to the database, and since no
locking takes place, there exists the possibility of an error in a
system under heavy use.
you will now get a validation error on the field `author_id` with the
message `"author is invalid"`

In more complex scenarios, errors are bound to occur. For example,
consider the following scenario:
Conversely, when if trying to delete an author but books exist, instead of:

```
parents
|
| <- dependent: :destroy
|
---children
|
| <- dependent: :restrict_with_error
|
---grandchildren
PG::ForeignKeyViolation: ERROR: update or delete on table "authors"
violates foreign key constraint "fk_rails_xxxxxxxxxx" on table "books"
DETAIL: Key (id)=(xxxxxxxxxx) is still referenced from table "books".
```

Here ActiveRecord will never have an efficient way to proceed. It would
either have to delete the children en masse, leading to database errors
such as:
you will now get a `base` validation error on the record with the
message `"Cannot delete record because dependent books exist"`

```
ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR:
update or delete on table "children" violates foreign key constraint
"fk_rails_xxxxxxxxxx" on table "grandchildren"
DETAIL: Key (id)=(xxxxxxxxxx) is still referenced from table "grandchildren".
```
![all tests](https://github.com/agios/unconstrained/actions/workflows/run-tests.yml/badge.svg?branch=master)

Otherwise ActiveRecord would need to load and check every child record,
which would be extremely inefficient. On the other hand, a relational
database, with the appropriate constraints defined, would handle the
operation swiftly and efficiently.
## Usage

Now that Rails 4.2 supports foreign key constraints, one would simply
define in the migration:
In your Gemfile:

```ruby
add_foreign_key :children, :parents, on_delete: :cascade
add_foreign_key :grandchildren, :children
gem 'unconstrained'
```
and then the database exceptions would be handled by Unconstrained.

## Notes

Only constraints that have to do with referential integrity are handled.
This is intentional, as these require a roundrip to the database anyway.
Constraints that can be enforced using validations in the application
should be checked that way.

Currently only handles PostgreSQL.

0 comments on commit 86afa26

Please sign in to comment.