-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #32 from nullobject/feature/unique-constraint
Add unique constraint
- Loading branch information
Showing
7 changed files
with
174 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
require 'rein/util' | ||
|
||
module Rein | ||
module Constraint | ||
# This module contains methods for defining unique constraints. | ||
module Unique | ||
include ActiveRecord::ConnectionAdapters::Quoting | ||
|
||
def add_unique_constraint(*args) | ||
reversible do |dir| | ||
dir.up do _add_unique_constraint(*args) end | ||
dir.down { _remove_unique_constraint(*args) } | ||
end | ||
end | ||
|
||
def remove_unique_constraint(*args) | ||
reversible do |dir| | ||
dir.up do _remove_unique_constraint(*args) end | ||
dir.down { _add_unique_constraint(*args) } | ||
end | ||
end | ||
|
||
private | ||
|
||
def _add_unique_constraint(table, attributes, options = {}) | ||
attributes = [attributes].flatten | ||
name = Util.constraint_name(table, attributes.join('_'), 'unique', options) | ||
initially = options[:deferred] ? 'DEFERRED' : 'IMMEDIATE' | ||
sql = "ALTER TABLE #{table} ADD CONSTRAINT #{name} UNIQUE (#{attributes.join(', ')})" | ||
sql << " DEFERRABLE INITIALLY #{initially}" unless options[:deferrable] == false | ||
execute(sql) | ||
end | ||
|
||
def _remove_unique_constraint(table, attributes, options = {}) | ||
attributes = [attributes].flatten | ||
name = Util.constraint_name(table, attributes.join('_'), 'unique', options) | ||
execute("ALTER TABLE #{table} DROP CONSTRAINT #{name}") | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
require 'spec_helper' | ||
|
||
RSpec.describe Rein::Constraint::Unique do | ||
subject(:adapter) do | ||
Class.new do | ||
include Rein::Constraint::Unique | ||
end.new | ||
end | ||
|
||
let(:dir) { double(up: nil, down: nil) } | ||
|
||
before do | ||
allow(dir).to receive(:up).and_yield | ||
allow(adapter).to receive(:reversible).and_yield(dir) | ||
allow(adapter).to receive(:execute) | ||
end | ||
|
||
describe '#add_unique_constraint' do | ||
context 'given an single attribute' do | ||
it 'adds a constraint' do | ||
expect(adapter).to receive(:execute).with('ALTER TABLE books ADD CONSTRAINT books_call_number_unique UNIQUE (call_number) DEFERRABLE INITIALLY IMMEDIATE') | ||
adapter.add_unique_constraint(:books, :call_number) | ||
end | ||
end | ||
|
||
context 'given an array of attributes' do | ||
it 'adds a deferrable constraint that is initially immediate' do | ||
expect(adapter).to receive(:execute).with('ALTER TABLE books ADD CONSTRAINT books_call_number_title_unique UNIQUE (call_number, title) DEFERRABLE INITIALLY IMMEDIATE') | ||
adapter.add_unique_constraint(:books, %w[call_number title]) | ||
end | ||
end | ||
|
||
context 'given a name option' do | ||
it 'adds a constraint with that name' do | ||
expect(adapter).to receive(:execute).with('ALTER TABLE books ADD CONSTRAINT books_call_number_is_unique UNIQUE (call_number) DEFERRABLE INITIALLY IMMEDIATE') | ||
adapter.add_unique_constraint(:books, :call_number, name: 'books_call_number_is_unique') | ||
end | ||
end | ||
|
||
context 'given a deferred option' do | ||
it 'adds a deferrable constraint that is initially deferred' do | ||
expect(adapter).to receive(:execute).with('ALTER TABLE books ADD CONSTRAINT books_call_number_unique UNIQUE (call_number) DEFERRABLE INITIALLY DEFERRED') | ||
adapter.add_unique_constraint(:books, :call_number, deferred: true) | ||
end | ||
end | ||
|
||
context 'given a deferrable option' do | ||
it 'adds an immediate constraint' do | ||
expect(adapter).to receive(:execute).with('ALTER TABLE books ADD CONSTRAINT books_call_number_unique UNIQUE (call_number)') | ||
adapter.add_unique_constraint(:books, :call_number, deferrable: false) | ||
end | ||
end | ||
end | ||
|
||
describe '#remove_unique_constraint' do | ||
it 'removes a constraint' do | ||
expect(subject).to receive(:execute).with('ALTER TABLE books DROP CONSTRAINT books_state_unique') | ||
subject.remove_unique_constraint(:books, :state) | ||
end | ||
end | ||
end |