polymorphic_constraints 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +10 -0
  5. data/Appraisals +15 -0
  6. data/Gemfile +15 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.md +254 -0
  9. data/README.rdoc +3 -0
  10. data/Rakefile +61 -0
  11. data/gemfiles/rails_3.1.gemfile +8 -0
  12. data/gemfiles/rails_3.2.gemfile +8 -0
  13. data/gemfiles/rails_4.0.gemfile +8 -0
  14. data/gemfiles/rails_4.1.gemfile +8 -0
  15. data/lib/polymorphic_constraints.rb +24 -0
  16. data/lib/polymorphic_constraints/adapter.rb +34 -0
  17. data/lib/polymorphic_constraints/connection_adapters/abstract/schema_statements.rb +23 -0
  18. data/lib/polymorphic_constraints/connection_adapters/mysql2_adapter.rb +167 -0
  19. data/lib/polymorphic_constraints/connection_adapters/postgresql_adapter.rb +147 -0
  20. data/lib/polymorphic_constraints/connection_adapters/sqlite3_adapter.rb +166 -0
  21. data/lib/polymorphic_constraints/migration/command_recorder.rb +20 -0
  22. data/lib/polymorphic_constraints/railtie.rb +27 -0
  23. data/lib/polymorphic_constraints/utils/polymorphic_error_handler.rb +19 -0
  24. data/lib/polymorphic_constraints/utils/polymorphic_model_finder.rb +19 -0
  25. data/lib/polymorphic_constraints/utils/sql_string.rb +9 -0
  26. data/lib/polymorphic_constraints/version.rb +3 -0
  27. data/polymorphic_constraints.gemspec +30 -0
  28. data/spec/dummy/Rakefile +6 -0
  29. data/spec/dummy/app/assets/images/.keep +0 -0
  30. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  31. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  32. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  33. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  34. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  35. data/spec/dummy/app/mailers/.keep +0 -0
  36. data/spec/dummy/app/models/.keep +0 -0
  37. data/spec/dummy/app/models/concerns/.keep +0 -0
  38. data/spec/dummy/app/models/employee.rb +3 -0
  39. data/spec/dummy/app/models/picture.rb +3 -0
  40. data/spec/dummy/app/models/product.rb +3 -0
  41. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  42. data/spec/dummy/bin/bundle +3 -0
  43. data/spec/dummy/bin/rails +4 -0
  44. data/spec/dummy/bin/rake +4 -0
  45. data/spec/dummy/config.ru +4 -0
  46. data/spec/dummy/config/application.rb +30 -0
  47. data/spec/dummy/config/boot.rb +5 -0
  48. data/spec/dummy/config/database.mysql.yml +6 -0
  49. data/spec/dummy/config/database.postgresql.yml +6 -0
  50. data/spec/dummy/config/database.sqlite.yml +5 -0
  51. data/spec/dummy/config/database.yml +5 -0
  52. data/spec/dummy/config/environment.rb +5 -0
  53. data/spec/dummy/config/environments/development.rb +37 -0
  54. data/spec/dummy/config/environments/production.rb +78 -0
  55. data/spec/dummy/config/environments/test.rb +39 -0
  56. data/spec/dummy/config/initializers/assets.rb +8 -0
  57. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  58. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  59. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  60. data/spec/dummy/config/initializers/inflections.rb +16 -0
  61. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  62. data/spec/dummy/config/initializers/secret_token.rb +2 -0
  63. data/spec/dummy/config/initializers/session_store.rb +3 -0
  64. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  65. data/spec/dummy/config/locales/en.yml +23 -0
  66. data/spec/dummy/config/routes.rb +56 -0
  67. data/spec/dummy/config/secrets.yml +22 -0
  68. data/spec/dummy/db/migrate/20141002195532_polymorphic_tables.rb +18 -0
  69. data/spec/dummy/lib/assets/.keep +0 -0
  70. data/spec/dummy/log/.keep +0 -0
  71. data/spec/dummy/public/404.html +67 -0
  72. data/spec/dummy/public/422.html +67 -0
  73. data/spec/dummy/public/500.html +66 -0
  74. data/spec/dummy/public/favicon.ico +0 -0
  75. data/spec/integration/active_record_integration_spec.rb +117 -0
  76. data/spec/lib/polymorphic_constraints/connection_adapters/mysql2_adapter_spec.rb +166 -0
  77. data/spec/lib/polymorphic_constraints/connection_adapters/postgresql_adapter_spec.rb +159 -0
  78. data/spec/lib/polymorphic_constraints/connection_adapters/sqlite3_adapter_spec.rb +150 -0
  79. data/spec/lib/polymorphic_constraints/utils/polymorphic_error_handler_spec.rb +45 -0
  80. data/spec/spec_helper.rb +52 -0
  81. data/spec/support/adapter_helper.rb +16 -0
  82. 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