Skip to content

Instantly share code, notes, and snippets.

@rosiehoyem
Last active August 29, 2015 14:11
Show Gist options
  • Save rosiehoyem/3a068619ee9056af6fac to your computer and use it in GitHub Desktop.
Save rosiehoyem/3a068619ee9056af6fac to your computer and use it in GitHub Desktop.
Ruby Transactions
class Person < ActiveRecord::Base
...
def destroy_door_access
if self.door_accesses.first
self.door_accesses.first.destroy
else
true
end
end
def remove_location
self.location_id = nil
end
def destroy_authorizations
if self.authorizations.first
self.authorizations.each {|authorization| authorization.destroy}
else
true
end
end
def update_historical
self.current_employee = nil
self.terminated_date = Date.today
end
def convert_to_historical
transaction do
update_historical
destroy_door_access
remove_location
destroy_authorizations
save
end
end
end
def convert_to_historical
transaction do
update_historical
destroy_door_access
remove_location
destroy_authorizations
save
end
end
require 'rails_helper'
describe Person do
it "is invalid without a first name" do
person = FactoryGirl.build(:person, first_name: nil)
person.save
expect(person.errors.count).to eq(1)
end
it "is invalid without a last name" do
person = FactoryGirl.build(:person, last_name: nil)
person.save
expect(person.errors.count).to eq(1)
end
it "updates current_employee and terminated_date" do
person = FactoryGirl.create(:person, current_employee: true, terminated_date: nil)
door_access = person.door_accesses.create!
person.update_historical
expect(person.current_employee).to eq(false)
expect(person.terminated_date).to eq(Date.today)
end
it "destroys door access" do
person = FactoryGirl.create(:person)
door_access = person.door_accesses.create!
person.destroy_door_access
expect(person.door_accesses.first).to eq(nil)
end
it "destroys authorizations" do
person = FactoryGirl.create(:person)
permission = FactoryGirl.create(:permission)
authorization = person.authorizations.create!(permission_id: 1, read: true, write: true)
person.destroy_authorizations
expect(person.authorizations.count).to eq(0)
end
it "remove locations" do
person = FactoryGirl.create(:person)
location = FactoryGirl.create(:location)
person.location_id = 1
person.remove_location
expect(person.location_id).to eq(nil)
end
end
it "completes full historical transaction historical transaction" do
person = FactoryGirl.create(:person, current_employee: true, terminated_date: nil)
door_access = person.door_accesses.create!
permission = FactoryGirl.create(:permission)
authorization = person.authorizations.create(permission_id: permission.id, read: true, write: true)
location = FactoryGirl.create(:location)
person.location_id = 1
person.convert_to_historical
expect(person.current_employee).to eq(false)
expect(person.terminated_date).to eq(Date.today)
expect(person.door_accesses.count).to eq(0)
expect(person.location_id).to eq(nil)
expect(person.authorizations.count).to eq(0)
end
Recently I built a features for ACE, the application our admin staff uses to manage just about everything for the MPC. They requested a simple way to turn a person from a current employee into a past, or historical person in our system. To move someone from one status to the other, could include having to touch almost every module in the application. For example, the person's door access needs to be deleted, the location of their office will need to removed, and a terminated date will need to be recorded among other things. If some of these steps are completed, but not others, it would serve to severely confuse both the staff and the application. This seemed like a good candidate for a transaction.
The classic transaction example is of a financial transaction where one side of the transaction should not be recorded unless the other is also successfully completed. If the second part fails, the whole thing should roll back.
[code language="ruby"]
balance.transaction do
balance.save!
account.save!
end
[/code]
To create the "Historical Button" feature for ACE, I decided to create several small, reuseable methods each capturing one aspect of moving an employee from current to historical. Here are the tests:
[code language="ruby"]
[/code]
Then I wrote the methods:
[code language="ruby"]
[/code]
Then I wrapped these methods in a transaction. The test:
[code language="ruby"]
transaction do
end
[/code]
And the code:
[code language="ruby"]
transaction do
end
[/code]
This is, admittedly, a very simple implementation of a transaction. After digging into the documentation a bit, it appears the complexity of these when dealing with distributed systems...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment