Reputation: 605
I'm working on a multi-tenant rails application using PostgreSQL schemas for different clients. Rails migrations don't work with multiple schemas out of the box, so I made the following rake task to migrate all schemas and it seems to work. My question is if others have implemented better and more elegant solutions. I would also be really happy with a good tutorial including rails code examples for PostgreSQL using multiple schemas. So far I have only found a good presentation on the subject http://aac2009.confreaks.com/06-feb-2009-14-30-writing-multi-tenant-applications-in-rails-guy-naor.html and an example of what I'm aiming for tomayko.com/writings/rails-multiple-connections
desc 'Migrates all postgres schemas'
task :schemas do
# get all schemas
env = "#{RAILS_ENV}"
config = YAML::load(File.open('config/database.yml'))
ActiveRecord::Base.establish_connection(config[env])
schemas = ActiveRecord::Base.connection.select_values("select * from pg_namespace where nspname != 'information_schema' AND nspname NOT LIKE 'pg%'")
puts "Migrate schemas: #{schemas.inspect}"
# migrate each schema
schemas.each do |schema|
puts "Migrate schema: #{schema}"
config = YAML::load(File.open('config/database.yml'))
config[env]["schema_search_path"] = schema
ActiveRecord::Base.establish_connection(config[env])
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Migrator.migrate('db/migrate', ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
end
end
Upvotes: 17
Views: 6752
Reputation: 1376
Check the apartment
gem that's been built just for that purpose. It's brilliant.
Upvotes: 1
Reputation: 2907
I wrote pg_migrate because of these scenarios, i.e., situations in which multiple applications share the same database. There is probably a Rails way to handle this (engines?) but I too often have another app that's not Rails that needs the database too... then what?
In this case, the key feature of pg_migrate is it can generate a ruby gem; so it becomes possible to maintain your database schema separately from all the downstream applications, but all can reference it.
In your Rails Gemfile, after you've built the ruby gem using pg_migrate's 'package' command, you can do:
gem 'my_db', gem 'jam_db', :path=> "../my_db/gem_package"
Upvotes: 0
Reputation: 610
I have a schema_utils library which I use and has the following method for handling migrations:
def self.with_schema(schema_name, &block)
conn = ActiveRecord::Base.connection
old_schema_search_path = conn.schema_search_path
conn.schema_search_path = schema_name
begin
yield
ensure
conn.schema_search_path = old_schema_search_path
end
end
I then use migrations as normal so I can continue to call rake:migrate Now, in your migrations you can use:
...
schemas.each do |schema|
SchemaUtils.with_schema(schema) do
#Put migration code here
#e.g. add_column :xyz, ...
end
end
Because I tend to be mapping schemas to account codes I do the following:
Account.for_each do |account|
SchemaUtils.with_schema(account.code) do
#Put migration code here
end
end
Upvotes: 12