polymorphic_constraints 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +10 -0
- data/Appraisals +15 -0
- data/Gemfile +15 -0
- data/MIT-LICENSE +20 -0
- data/README.md +254 -0
- data/README.rdoc +3 -0
- data/Rakefile +61 -0
- data/gemfiles/rails_3.1.gemfile +8 -0
- data/gemfiles/rails_3.2.gemfile +8 -0
- data/gemfiles/rails_4.0.gemfile +8 -0
- data/gemfiles/rails_4.1.gemfile +8 -0
- data/lib/polymorphic_constraints.rb +24 -0
- data/lib/polymorphic_constraints/adapter.rb +34 -0
- data/lib/polymorphic_constraints/connection_adapters/abstract/schema_statements.rb +23 -0
- data/lib/polymorphic_constraints/connection_adapters/mysql2_adapter.rb +167 -0
- data/lib/polymorphic_constraints/connection_adapters/postgresql_adapter.rb +147 -0
- data/lib/polymorphic_constraints/connection_adapters/sqlite3_adapter.rb +166 -0
- data/lib/polymorphic_constraints/migration/command_recorder.rb +20 -0
- data/lib/polymorphic_constraints/railtie.rb +27 -0
- data/lib/polymorphic_constraints/utils/polymorphic_error_handler.rb +19 -0
- data/lib/polymorphic_constraints/utils/polymorphic_model_finder.rb +19 -0
- data/lib/polymorphic_constraints/utils/sql_string.rb +9 -0
- data/lib/polymorphic_constraints/version.rb +3 -0
- data/polymorphic_constraints.gemspec +30 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/models/employee.rb +3 -0
- data/spec/dummy/app/models/picture.rb +3 -0
- data/spec/dummy/app/models/product.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +30 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.mysql.yml +6 -0
- data/spec/dummy/config/database.postgresql.yml +6 -0
- data/spec/dummy/config/database.sqlite.yml +5 -0
- data/spec/dummy/config/database.yml +5 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +78 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/assets.rb +8 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/secret_token.rb +2 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +56 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/db/migrate/20141002195532_polymorphic_tables.rb +18 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/integration/active_record_integration_spec.rb +117 -0
- data/spec/lib/polymorphic_constraints/connection_adapters/mysql2_adapter_spec.rb +166 -0
- data/spec/lib/polymorphic_constraints/connection_adapters/postgresql_adapter_spec.rb +159 -0
- data/spec/lib/polymorphic_constraints/connection_adapters/sqlite3_adapter_spec.rb +150 -0
- data/spec/lib/polymorphic_constraints/utils/polymorphic_error_handler_spec.rb +45 -0
- data/spec/spec_helper.rb +52 -0
- data/spec/support/adapter_helper.rb +16 -0
- metadata +263 -0
File without changes
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Active Record Integration' do
|
4
|
+
before :all do
|
5
|
+
adapter = ActiveRecord::Base.connection_config[:adapter]
|
6
|
+
send("setup_#{adapter}")
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'insertion' do
|
10
|
+
it 'raises an exception creating a polymorphic relation without a corresponding record' do
|
11
|
+
picture = Picture.new
|
12
|
+
picture.imageable_id = 1
|
13
|
+
picture.imageable_type = 'Product'
|
14
|
+
expect { picture.save }.to raise_error(ActiveRecord::StatementInvalid)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'does not allow an insert of a model type that wasn\'t specified in the polymorphic triggers' do
|
18
|
+
product = Product.new
|
19
|
+
product.save
|
20
|
+
product.reload
|
21
|
+
|
22
|
+
picture = Picture.new
|
23
|
+
picture.imageable_id = product.id
|
24
|
+
picture.imageable_type = 'World'
|
25
|
+
|
26
|
+
expect { picture.save }.to raise_error(ActiveRecord::StatementInvalid)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'allows an insert of a model type specified in the polymorphic triggers' do
|
30
|
+
product = Product.new
|
31
|
+
product.save
|
32
|
+
product.reload
|
33
|
+
|
34
|
+
picture = Picture.new
|
35
|
+
picture.imageable_id = product.id
|
36
|
+
picture.imageable_type = product.class.to_s
|
37
|
+
|
38
|
+
expect { picture.save }.to change(Picture, :count).by(1)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'update' do
|
43
|
+
it "does not allow an update to a model type that wasn't specified in the polymorphic triggers" do
|
44
|
+
product = Product.new
|
45
|
+
product.save
|
46
|
+
product.reload
|
47
|
+
|
48
|
+
picture = Picture.new
|
49
|
+
picture.imageable_id = product.id
|
50
|
+
picture.imageable_type = product.class.to_s
|
51
|
+
picture.save
|
52
|
+
picture.reload
|
53
|
+
|
54
|
+
picture.imageable_type = 'Hello'
|
55
|
+
|
56
|
+
expect { picture.save }.to raise_error(ActiveRecord::StatementInvalid)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'deletion' do
|
61
|
+
it 'raises an exception deleting a record that is still referenced by the polymorphic table' do
|
62
|
+
product = Product.new
|
63
|
+
product.save
|
64
|
+
product.reload
|
65
|
+
|
66
|
+
picture = Picture.new
|
67
|
+
picture.imageable_id = product.id
|
68
|
+
picture.imageable_type = product.class.to_s
|
69
|
+
picture.save
|
70
|
+
|
71
|
+
expect { product.delete }.to raise_error(ActiveRecord::StatementInvalid)
|
72
|
+
expect { product.destroy }.to raise_error(ActiveRecord::StatementInvalid)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'doesn\'t get in the way of dependent: :destroy' do
|
76
|
+
employee = Employee.new
|
77
|
+
employee.save
|
78
|
+
employee.reload
|
79
|
+
|
80
|
+
picture = Picture.new
|
81
|
+
picture.imageable_id = employee.id
|
82
|
+
picture.imageable_type = employee.class.to_s
|
83
|
+
picture.save
|
84
|
+
|
85
|
+
expect { employee.destroy }.to change(Employee, :count).by(-1)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'does enforce dependency behaviour if delete is used instead of destroy' do
|
89
|
+
employee = Employee.new
|
90
|
+
employee.save
|
91
|
+
employee.reload
|
92
|
+
|
93
|
+
picture = Picture.new
|
94
|
+
picture.imageable_id = employee.id
|
95
|
+
picture.imageable_type = employee.class.to_s
|
96
|
+
picture.save
|
97
|
+
|
98
|
+
expect { employee.delete }.to raise_error(ActiveRecord::StatementInvalid)
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'allows a delete of a record NOT referenced by the polymorphic table' do
|
102
|
+
employee = Employee.new
|
103
|
+
employee.save
|
104
|
+
employee.reload
|
105
|
+
|
106
|
+
expect { employee.delete }.to change(Employee, :count).by(-1)
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'allows a destroy of a record NOT referenced by the polymorphic table' do
|
110
|
+
employee = Employee.new
|
111
|
+
employee.save
|
112
|
+
employee.reload
|
113
|
+
|
114
|
+
expect { employee.destroy }.to change(Employee, :count).by(-1)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'polymorphic_constraints/connection_adapters/mysql2_adapter'
|
3
|
+
|
4
|
+
describe PolymorphicConstraints::ConnectionAdapters::Mysql2Adapter do
|
5
|
+
|
6
|
+
class MysqlTestAdapter
|
7
|
+
include Support::AdapterHelper
|
8
|
+
include PolymorphicConstraints::ConnectionAdapters::Mysql2Adapter
|
9
|
+
end
|
10
|
+
|
11
|
+
subject { MysqlTestAdapter.new }
|
12
|
+
|
13
|
+
it { is_expected.to respond_to(:supports_polymorphic_constraints?) }
|
14
|
+
it { is_expected.to respond_to(:add_polymorphic_constraints) }
|
15
|
+
it { is_expected.to respond_to(:remove_polymorphic_constraints) }
|
16
|
+
|
17
|
+
describe 'add constraints' do
|
18
|
+
it 'defaults to active_record_descendants search strategy' do
|
19
|
+
expect(subject.add_polymorphic_constraints(:imageable, :pictures)).to eql([drop_create_trigger_sql,
|
20
|
+
drop_update_trigger_sql,
|
21
|
+
drop_employees_delete_trigger_sql,
|
22
|
+
drop_products_delete_trigger_sql,
|
23
|
+
create_trigger_sql,
|
24
|
+
update_trigger_sql,
|
25
|
+
employees_delete_trigger_sql,
|
26
|
+
products_delete_trigger_sql])
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns expected add constraints sql with polymorphic model options' do
|
30
|
+
expect(subject.add_polymorphic_constraints(:imageable, :pictures,
|
31
|
+
polymorphic_models: [:employee])).to eql([drop_create_trigger_sql,
|
32
|
+
drop_update_trigger_sql,
|
33
|
+
drop_employees_delete_trigger_sql,
|
34
|
+
drop_products_delete_trigger_sql,
|
35
|
+
create_trigger_sql_only_employee,
|
36
|
+
update_trigger_sql_only_employee,
|
37
|
+
employees_delete_trigger_sql])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'remove constraints' do
|
42
|
+
it 'defaults to active_record_descendants search strategy' do
|
43
|
+
expect(subject.remove_polymorphic_constraints(:imageable)).to eql([drop_create_trigger_sql,
|
44
|
+
drop_update_trigger_sql,
|
45
|
+
drop_employees_delete_trigger_sql,
|
46
|
+
drop_products_delete_trigger_sql])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
let(:drop_create_trigger_sql) { 'DROP TRIGGER IF EXISTS check_imageable_insert_integrity;' }
|
51
|
+
let(:drop_update_trigger_sql) { 'DROP TRIGGER IF EXISTS check_imageable_update_integrity;' }
|
52
|
+
|
53
|
+
let(:drop_employees_delete_trigger_sql) { 'DROP TRIGGER IF EXISTS check_imageable_employees_delete_integrity;' }
|
54
|
+
|
55
|
+
let(:employees_delete_trigger_sql) do
|
56
|
+
subject.strip_non_essential_spaces(%{
|
57
|
+
CREATE TRIGGER check_imageable_employees_delete_integrity
|
58
|
+
BEFORE DELETE ON employees
|
59
|
+
FOR EACH ROW
|
60
|
+
BEGIN
|
61
|
+
IF EXISTS (SELECT id FROM pictures
|
62
|
+
WHERE imageable_type = 'Employee'
|
63
|
+
AND imageable_id = OLD.id) THEN
|
64
|
+
SIGNAL SQLSTATE '45000'
|
65
|
+
SET MESSAGE_TEXT = 'Polymorphic reference exists.
|
66
|
+
There are records in the pictures table that refer to the table employees.
|
67
|
+
You must delete those records of table pictures first.';
|
68
|
+
END IF;
|
69
|
+
END;
|
70
|
+
})
|
71
|
+
end
|
72
|
+
|
73
|
+
let(:drop_products_delete_trigger_sql) { 'DROP TRIGGER IF EXISTS check_imageable_products_delete_integrity;' }
|
74
|
+
|
75
|
+
let(:products_delete_trigger_sql) do
|
76
|
+
subject.strip_non_essential_spaces(%{
|
77
|
+
CREATE TRIGGER check_imageable_products_delete_integrity
|
78
|
+
BEFORE DELETE ON products
|
79
|
+
FOR EACH ROW
|
80
|
+
BEGIN
|
81
|
+
IF EXISTS (SELECT id FROM pictures
|
82
|
+
WHERE imageable_type = 'Product'
|
83
|
+
AND imageable_id = OLD.id) THEN
|
84
|
+
SIGNAL SQLSTATE '45000'
|
85
|
+
SET MESSAGE_TEXT = 'Polymorphic reference exists.
|
86
|
+
There are records in the pictures table that refer to the table products.
|
87
|
+
You must delete those records of table pictures first.';
|
88
|
+
END IF;
|
89
|
+
END;
|
90
|
+
})
|
91
|
+
end
|
92
|
+
|
93
|
+
let(:create_trigger_sql) do
|
94
|
+
subject.strip_non_essential_spaces(%{
|
95
|
+
CREATE TRIGGER check_imageable_insert_integrity
|
96
|
+
BEFORE INSERT ON pictures
|
97
|
+
FOR EACH ROW
|
98
|
+
BEGIN
|
99
|
+
IF NEW.imageable_type != 'Employee' AND NEW.imageable_type != 'Product' THEN
|
100
|
+
SIGNAL SQLSTATE '45000'
|
101
|
+
SET MESSAGE_TEXT = 'Polymorphic record not found. No model by that name.';
|
102
|
+
ELSEIF NEW.imageable_type = 'Employee' AND NOT EXISTS (SELECT id FROM employees WHERE id = NEW.imageable_id) THEN
|
103
|
+
SIGNAL SQLSTATE '45000'
|
104
|
+
SET MESSAGE_TEXT = 'Polymorphic record not found. No Employee with that id.';
|
105
|
+
ELSEIF NEW.imageable_type = 'Product' AND NOT EXISTS (SELECT id FROM products WHERE id = NEW.imageable_id) THEN
|
106
|
+
SIGNAL SQLSTATE '45000'
|
107
|
+
SET MESSAGE_TEXT = 'Polymorphic record not found. No Product with that id.';
|
108
|
+
END IF;
|
109
|
+
END;
|
110
|
+
})
|
111
|
+
end
|
112
|
+
|
113
|
+
let(:update_trigger_sql) do
|
114
|
+
subject.strip_non_essential_spaces(%{
|
115
|
+
CREATE TRIGGER check_imageable_update_integrity
|
116
|
+
BEFORE UPDATE ON pictures
|
117
|
+
FOR EACH ROW
|
118
|
+
BEGIN
|
119
|
+
IF NEW.imageable_type != 'Employee' AND NEW.imageable_type != 'Product' THEN
|
120
|
+
SIGNAL SQLSTATE '45000'
|
121
|
+
SET MESSAGE_TEXT = 'Polymorphic record not found. No model by that name.';
|
122
|
+
ELSEIF NEW.imageable_type = 'Employee' AND NOT EXISTS (SELECT id FROM employees WHERE id = NEW.imageable_id) THEN
|
123
|
+
SIGNAL SQLSTATE '45000'
|
124
|
+
SET MESSAGE_TEXT = 'Polymorphic record not found. No Employee with that id.';
|
125
|
+
ELSEIF NEW.imageable_type = 'Product' AND NOT EXISTS (SELECT id FROM products WHERE id = NEW.imageable_id) THEN
|
126
|
+
SIGNAL SQLSTATE '45000'
|
127
|
+
SET MESSAGE_TEXT = 'Polymorphic record not found. No Product with that id.';
|
128
|
+
END IF;
|
129
|
+
END;
|
130
|
+
})
|
131
|
+
end
|
132
|
+
|
133
|
+
let(:create_trigger_sql_only_employee) do
|
134
|
+
subject.strip_non_essential_spaces(%{
|
135
|
+
CREATE TRIGGER check_imageable_insert_integrity
|
136
|
+
BEFORE INSERT ON pictures
|
137
|
+
FOR EACH ROW
|
138
|
+
BEGIN
|
139
|
+
IF NEW.imageable_type != 'Employee' THEN
|
140
|
+
SIGNAL SQLSTATE '45000'
|
141
|
+
SET MESSAGE_TEXT = 'Polymorphic record not found. No model by that name.';
|
142
|
+
ELSEIF NEW.imageable_type = 'Employee' AND NOT EXISTS (SELECT id FROM employees WHERE id = NEW.imageable_id) THEN
|
143
|
+
SIGNAL SQLSTATE '45000'
|
144
|
+
SET MESSAGE_TEXT = 'Polymorphic record not found. No Employee with that id.';
|
145
|
+
END IF;
|
146
|
+
END;
|
147
|
+
})
|
148
|
+
end
|
149
|
+
|
150
|
+
let(:update_trigger_sql_only_employee) do
|
151
|
+
subject.strip_non_essential_spaces(%{
|
152
|
+
CREATE TRIGGER check_imageable_update_integrity
|
153
|
+
BEFORE UPDATE ON pictures
|
154
|
+
FOR EACH ROW
|
155
|
+
BEGIN
|
156
|
+
IF NEW.imageable_type != 'Employee' THEN
|
157
|
+
SIGNAL SQLSTATE '45000'
|
158
|
+
SET MESSAGE_TEXT = 'Polymorphic record not found. No model by that name.';
|
159
|
+
ELSEIF NEW.imageable_type = 'Employee' AND NOT EXISTS (SELECT id FROM employees WHERE id = NEW.imageable_id) THEN
|
160
|
+
SIGNAL SQLSTATE '45000'
|
161
|
+
SET MESSAGE_TEXT = 'Polymorphic record not found. No Employee with that id.';
|
162
|
+
END IF;
|
163
|
+
END;
|
164
|
+
})
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'polymorphic_constraints/connection_adapters/postgresql_adapter'
|
3
|
+
|
4
|
+
describe PolymorphicConstraints::ConnectionAdapters::PostgreSQLAdapter do
|
5
|
+
|
6
|
+
class PostgresqlTestAdapter
|
7
|
+
include Support::AdapterHelper
|
8
|
+
include PolymorphicConstraints::ConnectionAdapters::PostgreSQLAdapter
|
9
|
+
end
|
10
|
+
|
11
|
+
subject { PostgresqlTestAdapter.new }
|
12
|
+
|
13
|
+
it { is_expected.to respond_to(:supports_polymorphic_constraints?) }
|
14
|
+
it { is_expected.to respond_to(:add_polymorphic_constraints) }
|
15
|
+
it { is_expected.to respond_to(:remove_polymorphic_constraints) }
|
16
|
+
|
17
|
+
describe 'add constraints' do
|
18
|
+
it 'defaults to active_record_descendants search strategy' do
|
19
|
+
expect(subject.add_polymorphic_constraints(:imageable, :pictures)).to eql([drop_triggers_sql,
|
20
|
+
upsert_triggers_sql,
|
21
|
+
delete_triggers_sql])
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'returns expected add constraints sql with polymorphic model options' do
|
25
|
+
expect(subject.add_polymorphic_constraints(:imageable, :pictures,
|
26
|
+
polymorphic_models: [:employee])).to eql([drop_triggers_sql,
|
27
|
+
upsert_triggers_sql_only_employee,
|
28
|
+
delete_triggers_sql_only_employee])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'remove constraints' do
|
33
|
+
it 'returns expected drop trigger sql' do
|
34
|
+
expect(subject.remove_polymorphic_constraints(:imageable)).to eql(drop_triggers_sql)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
let(:upsert_triggers_sql) do
|
39
|
+
subject.strip_non_essential_spaces(%{
|
40
|
+
CREATE FUNCTION check_imageable_upsert_integrity() RETURNS TRIGGER AS '
|
41
|
+
BEGIN
|
42
|
+
IF NEW.imageable_type = ''Employee'' AND
|
43
|
+
EXISTS (SELECT id FROM employees WHERE id = NEW.imageable_id) THEN
|
44
|
+
RETURN NEW;
|
45
|
+
|
46
|
+
ELSEIF NEW.imageable_type = ''Product'' AND
|
47
|
+
EXISTS (SELECT id FROM products WHERE id = NEW.imageable_id) THEN
|
48
|
+
RETURN NEW;
|
49
|
+
|
50
|
+
ELSE
|
51
|
+
RAISE EXCEPTION ''Polymorphic record not found.
|
52
|
+
No % model with id %.'', NEW.imageable_type, NEW.imageable_id;
|
53
|
+
RETURN NULL;
|
54
|
+
END IF;
|
55
|
+
END'
|
56
|
+
LANGUAGE plpgsql;
|
57
|
+
|
58
|
+
CREATE TRIGGER check_imageable_upsert_integrity_trigger
|
59
|
+
BEFORE INSERT OR UPDATE ON pictures
|
60
|
+
FOR EACH ROW EXECUTE PROCEDURE check_imageable_upsert_integrity();
|
61
|
+
})
|
62
|
+
end
|
63
|
+
|
64
|
+
let(:delete_triggers_sql) do
|
65
|
+
subject.strip_non_essential_spaces(%{
|
66
|
+
CREATE FUNCTION check_imageable_delete_integrity()
|
67
|
+
RETURNS TRIGGER AS '
|
68
|
+
BEGIN
|
69
|
+
IF TG_TABLE_NAME = ''employees'' AND
|
70
|
+
EXISTS (SELECT id FROM pictures
|
71
|
+
WHERE imageable_type = ''Employee'' AND imageable_id = OLD.id) THEN
|
72
|
+
|
73
|
+
RAISE EXCEPTION ''Polymorphic reference exists.
|
74
|
+
There are records in pictures that refer to the table % with id %.
|
75
|
+
You must delete those records of table pictures first.'', TG_TABLE_NAME, OLD.id;
|
76
|
+
RETURN NULL;
|
77
|
+
|
78
|
+
ELSEIF TG_TABLE_NAME = ''products'' AND
|
79
|
+
EXISTS (SELECT id FROM pictures
|
80
|
+
WHERE imageable_type = ''Product'' AND imageable_id = OLD.id) THEN
|
81
|
+
|
82
|
+
RAISE EXCEPTION ''Polymorphic reference exists.
|
83
|
+
There are records in pictures that refer to the table % with id %.
|
84
|
+
You must delete those records of table pictures first.'', TG_TABLE_NAME, OLD.id;
|
85
|
+
RETURN NULL;
|
86
|
+
|
87
|
+
ELSE
|
88
|
+
RETURN OLD;
|
89
|
+
END IF;
|
90
|
+
END'
|
91
|
+
LANGUAGE plpgsql;
|
92
|
+
|
93
|
+
CREATE TRIGGER check_imageable_employees_delete_integrity_trigger
|
94
|
+
BEFORE DELETE ON employees
|
95
|
+
FOR EACH ROW EXECUTE PROCEDURE check_imageable_delete_integrity();
|
96
|
+
|
97
|
+
CREATE TRIGGER check_imageable_products_delete_integrity_trigger
|
98
|
+
BEFORE DELETE ON products
|
99
|
+
FOR EACH ROW EXECUTE PROCEDURE check_imageable_delete_integrity();
|
100
|
+
})
|
101
|
+
end
|
102
|
+
|
103
|
+
let(:upsert_triggers_sql_only_employee) do
|
104
|
+
subject.strip_non_essential_spaces(%{
|
105
|
+
CREATE FUNCTION check_imageable_upsert_integrity() RETURNS TRIGGER AS '
|
106
|
+
BEGIN
|
107
|
+
IF NEW.imageable_type = ''Employee'' AND
|
108
|
+
EXISTS (SELECT id FROM employees WHERE id = NEW.imageable_id) THEN
|
109
|
+
RETURN NEW;
|
110
|
+
|
111
|
+
ELSE
|
112
|
+
RAISE EXCEPTION ''Polymorphic record not found.
|
113
|
+
No % model with id %.'', NEW.imageable_type, NEW.imageable_id;
|
114
|
+
RETURN NULL;
|
115
|
+
END IF;
|
116
|
+
END'
|
117
|
+
LANGUAGE plpgsql;
|
118
|
+
|
119
|
+
CREATE TRIGGER check_imageable_upsert_integrity_trigger
|
120
|
+
BEFORE INSERT OR UPDATE ON pictures
|
121
|
+
FOR EACH ROW EXECUTE PROCEDURE check_imageable_upsert_integrity();
|
122
|
+
})
|
123
|
+
end
|
124
|
+
|
125
|
+
let(:delete_triggers_sql_only_employee) do
|
126
|
+
subject.strip_non_essential_spaces(%{
|
127
|
+
CREATE FUNCTION check_imageable_delete_integrity()
|
128
|
+
RETURNS TRIGGER AS '
|
129
|
+
BEGIN
|
130
|
+
IF TG_TABLE_NAME = ''employees'' AND
|
131
|
+
EXISTS (SELECT id FROM pictures
|
132
|
+
WHERE imageable_type = ''Employee'' AND imageable_id = OLD.id) THEN
|
133
|
+
|
134
|
+
RAISE EXCEPTION ''Polymorphic reference exists.
|
135
|
+
There are records in pictures that refer to the table % with id %.
|
136
|
+
You must delete those records of table pictures first.'', TG_TABLE_NAME, OLD.id;
|
137
|
+
RETURN NULL;
|
138
|
+
|
139
|
+
ELSE
|
140
|
+
RETURN OLD;
|
141
|
+
END IF;
|
142
|
+
END'
|
143
|
+
LANGUAGE plpgsql;
|
144
|
+
|
145
|
+
CREATE TRIGGER check_imageable_employees_delete_integrity_trigger
|
146
|
+
BEFORE DELETE ON employees
|
147
|
+
FOR EACH ROW EXECUTE PROCEDURE check_imageable_delete_integrity();
|
148
|
+
})
|
149
|
+
end
|
150
|
+
|
151
|
+
let(:drop_triggers_sql) do
|
152
|
+
subject.strip_non_essential_spaces(%{
|
153
|
+
DROP FUNCTION IF EXISTS check_imageable_upsert_integrity()
|
154
|
+
CASCADE;
|
155
|
+
DROP FUNCTION IF EXISTS check_imageable_delete_integrity()
|
156
|
+
CASCADE;
|
157
|
+
})
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'polymorphic_constraints/connection_adapters/sqlite3_adapter'
|
3
|
+
|
4
|
+
describe PolymorphicConstraints::ConnectionAdapters::SQLite3Adapter do
|
5
|
+
|
6
|
+
class SqliteTestAdapter
|
7
|
+
include Support::AdapterHelper
|
8
|
+
include PolymorphicConstraints::ConnectionAdapters::SQLite3Adapter
|
9
|
+
end
|
10
|
+
|
11
|
+
subject { SqliteTestAdapter.new }
|
12
|
+
|
13
|
+
it { is_expected.to respond_to(:supports_polymorphic_constraints?) }
|
14
|
+
it { is_expected.to respond_to(:add_polymorphic_constraints) }
|
15
|
+
it { is_expected.to respond_to(:remove_polymorphic_constraints) }
|
16
|
+
|
17
|
+
describe 'add constraints' do
|
18
|
+
it 'defaults to active_record_descendants search strategy' do
|
19
|
+
expect(subject.add_polymorphic_constraints(:imageable, :pictures)).to eql([drop_create_trigger_sql,
|
20
|
+
drop_update_trigger_sql,
|
21
|
+
drop_employees_delete_trigger_sql,
|
22
|
+
drop_products_delete_trigger_sql,
|
23
|
+
create_trigger_sql,
|
24
|
+
update_trigger_sql,
|
25
|
+
employees_delete_trigger_sql,
|
26
|
+
products_delete_trigger_sql])
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns expected add constraints sql with polymorphic model options' do
|
30
|
+
expect(subject.add_polymorphic_constraints(:imageable, :pictures,
|
31
|
+
polymorphic_models: [:employee])).to eql([drop_create_trigger_sql,
|
32
|
+
drop_update_trigger_sql,
|
33
|
+
drop_employees_delete_trigger_sql,
|
34
|
+
drop_products_delete_trigger_sql,
|
35
|
+
create_trigger_sql_only_employee,
|
36
|
+
update_trigger_sql_only_employee,
|
37
|
+
employees_delete_trigger_sql])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'remove constraints' do
|
42
|
+
it 'defaults to active_record_descendants search strategy' do
|
43
|
+
expect(subject.remove_polymorphic_constraints(:imageable)).to eql([drop_create_trigger_sql,
|
44
|
+
drop_update_trigger_sql,
|
45
|
+
drop_employees_delete_trigger_sql,
|
46
|
+
drop_products_delete_trigger_sql])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
let(:drop_create_trigger_sql) { 'DROP TRIGGER IF EXISTS check_imageable_insert_integrity;' }
|
51
|
+
let(:drop_update_trigger_sql) { 'DROP TRIGGER IF EXISTS check_imageable_update_integrity;' }
|
52
|
+
|
53
|
+
let(:drop_employees_delete_trigger_sql) { 'DROP TRIGGER IF EXISTS check_imageable_employees_delete_integrity;' }
|
54
|
+
|
55
|
+
let(:employees_delete_trigger_sql) do
|
56
|
+
subject.strip_non_essential_spaces(%{
|
57
|
+
CREATE TRIGGER check_imageable_employees_delete_integrity
|
58
|
+
BEFORE DELETE ON employees
|
59
|
+
BEGIN
|
60
|
+
SELECT CASE
|
61
|
+
WHEN EXISTS (SELECT id FROM pictures WHERE imageable_type = 'Employee' AND imageable_id = OLD.id) THEN
|
62
|
+
RAISE(ABORT, 'Polymorphic reference exists.
|
63
|
+
There are records in the pictures table that refer to the table employees.
|
64
|
+
You must delete those records of table pictures first.')
|
65
|
+
END;
|
66
|
+
END;
|
67
|
+
})
|
68
|
+
end
|
69
|
+
|
70
|
+
let(:drop_products_delete_trigger_sql) { 'DROP TRIGGER IF EXISTS check_imageable_products_delete_integrity;' }
|
71
|
+
|
72
|
+
let(:products_delete_trigger_sql) do
|
73
|
+
subject.strip_non_essential_spaces(%{
|
74
|
+
CREATE TRIGGER check_imageable_products_delete_integrity
|
75
|
+
BEFORE DELETE ON products
|
76
|
+
BEGIN
|
77
|
+
SELECT CASE
|
78
|
+
WHEN EXISTS (SELECT id FROM pictures WHERE imageable_type = 'Product' AND imageable_id = OLD.id) THEN
|
79
|
+
RAISE(ABORT, 'Polymorphic reference exists.
|
80
|
+
There are records in the pictures table that refer to the table products.
|
81
|
+
You must delete those records of table pictures first.')
|
82
|
+
END;
|
83
|
+
END;
|
84
|
+
})
|
85
|
+
end
|
86
|
+
|
87
|
+
let(:create_trigger_sql) do
|
88
|
+
subject.strip_non_essential_spaces(%{
|
89
|
+
CREATE TRIGGER check_imageable_insert_integrity
|
90
|
+
BEFORE INSERT ON pictures
|
91
|
+
BEGIN
|
92
|
+
SELECT CASE
|
93
|
+
WHEN ( NEW.imageable_type != 'Employee' AND NEW.imageable_type != 'Product' ) THEN
|
94
|
+
RAISE(ABORT, 'Polymorphic record not found. No model by that name.')
|
95
|
+
WHEN ((NEW.imageable_type = 'Employee') AND NOT EXISTS (SELECT id FROM employees WHERE id = NEW.imageable_id)) THEN
|
96
|
+
RAISE(ABORT, 'Polymorphic record not found. No Employee with that id.')
|
97
|
+
WHEN ((NEW.imageable_type = 'Product') AND NOT EXISTS (SELECT id FROM products WHERE id = NEW.imageable_id)) THEN
|
98
|
+
RAISE(ABORT, 'Polymorphic record not found. No Product with that id.')
|
99
|
+
END;
|
100
|
+
END;
|
101
|
+
})
|
102
|
+
end
|
103
|
+
|
104
|
+
let(:update_trigger_sql) do
|
105
|
+
subject.strip_non_essential_spaces(%{
|
106
|
+
CREATE TRIGGER check_imageable_update_integrity
|
107
|
+
BEFORE UPDATE ON pictures
|
108
|
+
BEGIN
|
109
|
+
SELECT CASE
|
110
|
+
WHEN ( NEW.imageable_type != 'Employee' AND NEW.imageable_type != 'Product' ) THEN
|
111
|
+
RAISE(ABORT, 'Polymorphic record not found. No model by that name.')
|
112
|
+
WHEN ((NEW.imageable_type = 'Employee') AND NOT EXISTS (SELECT id FROM employees WHERE id = NEW.imageable_id)) THEN
|
113
|
+
RAISE(ABORT, 'Polymorphic record not found. No Employee with that id.')
|
114
|
+
WHEN ((NEW.imageable_type = 'Product') AND NOT EXISTS (SELECT id FROM products WHERE id = NEW.imageable_id)) THEN
|
115
|
+
RAISE(ABORT, 'Polymorphic record not found. No Product with that id.')
|
116
|
+
END;
|
117
|
+
END;
|
118
|
+
})
|
119
|
+
end
|
120
|
+
|
121
|
+
let(:create_trigger_sql_only_employee) do
|
122
|
+
subject.strip_non_essential_spaces(%{
|
123
|
+
CREATE TRIGGER check_imageable_insert_integrity
|
124
|
+
BEFORE INSERT ON pictures
|
125
|
+
BEGIN
|
126
|
+
SELECT CASE
|
127
|
+
WHEN ( NEW.imageable_type != 'Employee' ) THEN
|
128
|
+
RAISE(ABORT, 'Polymorphic record not found. No model by that name.')
|
129
|
+
WHEN ((NEW.imageable_type = 'Employee') AND NOT EXISTS (SELECT id FROM employees WHERE id = NEW.imageable_id)) THEN
|
130
|
+
RAISE(ABORT, 'Polymorphic record not found. No Employee with that id.')
|
131
|
+
END;
|
132
|
+
END;
|
133
|
+
})
|
134
|
+
end
|
135
|
+
|
136
|
+
let(:update_trigger_sql_only_employee) do
|
137
|
+
subject.strip_non_essential_spaces(%{
|
138
|
+
CREATE TRIGGER check_imageable_update_integrity
|
139
|
+
BEFORE UPDATE ON pictures
|
140
|
+
BEGIN
|
141
|
+
SELECT CASE
|
142
|
+
WHEN ( NEW.imageable_type != 'Employee' ) THEN
|
143
|
+
RAISE(ABORT, 'Polymorphic record not found. No model by that name.')
|
144
|
+
WHEN ((NEW.imageable_type = 'Employee') AND NOT EXISTS (SELECT id FROM employees WHERE id = NEW.imageable_id)) THEN
|
145
|
+
RAISE(ABORT, 'Polymorphic record not found. No Employee with that id.')
|
146
|
+
END;
|
147
|
+
END;
|
148
|
+
})
|
149
|
+
end
|
150
|
+
end
|