aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/database.yml-example2
-rw-r--r--config/initializers/alaveteli.rb5
-rw-r--r--doc/INSTALL.md3
-rw-r--r--lib/no_constraint_disabling.rb110
4 files changed, 119 insertions, 1 deletions
diff --git a/config/database.yml-example b/config/database.yml-example
index b1597e6fe..e48577f23 100644
--- a/config/database.yml-example
+++ b/config/database.yml-example
@@ -19,6 +19,8 @@ test:
password: <password>
host: localhost
port: 5432
+# Uncomment the following if the user is not a postgres superuser
+# constraint_disabling: false
production:
adapter: postgresql
diff --git a/config/initializers/alaveteli.rb b/config/initializers/alaveteli.rb
index 35d486837..4acc126d0 100644
--- a/config/initializers/alaveteli.rb
+++ b/config/initializers/alaveteli.rb
@@ -59,3 +59,8 @@ require 'quiet_opener.rb'
require 'mail_handler'
require 'public_body_categories'
require 'ability'
+
+# Allow tests to be run under a non-superuser database account if required
+if Rails.env == 'test' and ActiveRecord::Base.configurations['test']['constraint_disabling'] == false
+ require 'no_constraint_disabling'
+end
diff --git a/doc/INSTALL.md b/doc/INSTALL.md
index 09bc8fd43..b6e8d2265 100644
--- a/doc/INSTALL.md
+++ b/doc/INSTALL.md
@@ -130,7 +130,8 @@ username and password of your postgres database.
Make sure that the user specified in database.yml exists, and has full
permissions on these databases. As they need the ability to turn off
constraints whilst running the tests they also need to be a superuser.
-(See http://dev.rubyonrails.org/ticket/9981)
+If you don't want your database user to be a superuser, you can add a line
+`disable_constraints: false` to the test config in database.yml, as seen in database.yml-example
You can create a `foi` user from the command line, thus:
diff --git a/lib/no_constraint_disabling.rb b/lib/no_constraint_disabling.rb
new file mode 100644
index 000000000..d515a959a
--- /dev/null
+++ b/lib/no_constraint_disabling.rb
@@ -0,0 +1,110 @@
+# In order to work around the problem of the database use not having
+# the permission to disable referential integrity when loading fixtures,
+# we redefine disable_referential_integrity so that it doesn't try to
+# disable foreign key constraints, and redefine the
+# ActiveRecord::Fixtures.create_fixtures method to pay attention to the order
+# which fixture tables are passed so that foreign key constraints won't be
+# violated. The only lines that are changed from the initial definition
+# are those between the "***" comments
+require 'active_record/fixtures'
+require 'active_record/connection_adapters/postgresql_adapter'
+module ActiveRecord
+ module ConnectionAdapters
+ class PostgreSQLAdapter < AbstractAdapter
+ def disable_referential_integrity(&block)
+ transaction {
+ yield
+ }
+ end
+ end
+ end
+end
+
+module ActiveRecord
+ class Fixtures
+
+ def self.create_fixtures(fixtures_directory, table_names, class_names = {})
+ table_names = [table_names].flatten.map { |n| n.to_s }
+ table_names.each { |n|
+ class_names[n.tr('/', '_').to_sym] = n.classify if n.include?('/')
+ }
+
+ # FIXME: Apparently JK uses this.
+ connection = block_given? ? yield : ActiveRecord::Base.connection
+
+ files_to_read = table_names.reject { |table_name|
+ fixture_is_cached?(connection, table_name)
+ }
+
+ unless files_to_read.empty?
+ connection.disable_referential_integrity do
+ fixtures_map = {}
+
+ fixture_files = files_to_read.map do |path|
+ table_name = path.tr '/', '_'
+
+ fixtures_map[path] = ActiveRecord::Fixtures.new(
+ connection,
+ table_name,
+ class_names[table_name.to_sym] || table_name.classify,
+ File.join(fixtures_directory, path))
+ end
+
+ all_loaded_fixtures.update(fixtures_map)
+
+ connection.transaction(:requires_new => true) do
+ # Patch - replace this...
+ # ***
+ # fixture_files.each do |ff|
+ # conn = ff.model_class.respond_to?(:connection) ? ff.model_class.connection : connection
+ # table_rows = ff.table_rows
+ #
+ # table_rows.keys.each do |table|
+ # conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
+ # end
+ #
+ # table_rows.each do |table_name,rows|
+ # rows.each do |row|
+ # conn.insert_fixture(row, table_name)
+ # end
+ # end
+ # end
+ # ***
+ # ... with this
+ fixture_files.reverse.each do |ff|
+ conn = ff.model_class.respond_to?(:connection) ? ff.model_class.connection : connection
+ table_rows = ff.table_rows
+
+ table_rows.keys.each do |table|
+ conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
+ end
+ end
+
+ fixture_files.each do |ff|
+ conn = ff.model_class.respond_to?(:connection) ? ff.model_class.connection : connection
+ table_rows = ff.table_rows
+ table_rows.each do |table_name,rows|
+ rows.each do |row|
+ conn.insert_fixture(row, table_name)
+ end
+ end
+ end
+ # ***
+
+ # Cap primary key sequences to max(pk).
+ if connection.respond_to?(:reset_pk_sequence!)
+ table_names.each do |table_name|
+ connection.reset_pk_sequence!(table_name.tr('/', '_'))
+ end
+ end
+ end
+
+ cache_fixtures(connection, fixtures_map)
+ end
+ end
+ cached_fixtures(connection, table_names)
+ end
+
+ end
+
+end