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.
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