Skip to content

About

Conditional updates for ActiveRecord records

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

ActiveRecord::UpdateWhen

Updates ActiveRecord records only when they match a condition in the database.

The condition is part of the UPDATE statement itself, so it can't change between checking it and updating the record (eg. by a concurrent update).

Installation

Add this line to your application's Gemfile:

gem 'active_record-update_when'

And then execute:

$ bundle

Then include ActiveRecord::UpdateWhen in your models (typically in ApplicationRecord, so all models get it):

class ApplicationRecord < ActiveRecord::Base
  primary_abstract_class

  include ActiveRecord::UpdateWhen
end

Usage

update_when updates a record (as update would) if it matches the condition in the database:

order = Order.find(...)

if order.update_when(condition: Order.checked_out, attributes: { status: 'cancelled' })
  # order was cancelled
elsif order.errors.any?
  # order was invalid
else
  # order wasn't checked-out
end
UPDATE "orders" SET "status" = 'cancelled', "updated_at" = ...
WHERE "orders"."id" = 1 AND "orders"."status" = 'checked_out'

It returns false if the record is invalid (with errors), or if it doesn't match the condition (without errors). When the condition isn't met, the update is rolled back, including any changes made by its callbacks (even within another transaction).

update_when! updates the record as update! would. It returns false if the record doesn't match the condition, and raises whenever update! would (eg. ActiveRecord::RecordInvalid):

order.update_when!(condition: Order.checked_out, attributes: { status: 'cancelled' })

Conditions

The condition is a relation of the record's model. Its WHERE clause is added to the UPDATE:

order.update_when(condition: Order.where(status: %w[pending checked_out]), attributes: { status: 'cancelled' })

Conditions can also have joins, GROUP BY and HAVING clauses (including for models with composite primary keys), in which case the record is matched by an EXISTS subquery:

order.update_when(condition: Order.checked_out.joins(:lines), attributes: { status: 'cancelled' })

If the attributes don't change the record, nothing is updated (as with update) and the condition isn't checked.

Development

The specs run against a dummy Rails application in spec/dummy, using SQLite by default:

$ bin/rspec
$ bin/rubocop

PostgreSQL and MySQL are selected with the DATABASE_ADAPTER and DATABASE_URL environment variables (see .github/workflows/ci.yml).

License

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

About

Conditional updates for ActiveRecord records

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages