Skip to content

Commit

Permalink
now support a different default schema_search_path than public
Browse files Browse the repository at this point in the history
  • Loading branch information
bradrobertson committed May 31, 2012
1 parent 2c15e0a commit 505ea1f
Show file tree
Hide file tree
Showing 11 changed files with 121 additions and 54 deletions.
3 changes: 3 additions & 0 deletions .pryrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
if defined?(Rails) && Rails.env
extend Rails::ConsoleMethods
end
23 changes: 15 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,20 +85,19 @@ To set config options, add this to your initializer:

### Excluding models

If you have some models that should always access the 'root' database, you can specify this by configuring
Apartment using `Apartment.configure`. This will yield a config object for you. You can set excluded models like so:
If you have some models that should always access the 'root' database, you can specify this by configuring Apartment using `Apartment.configure`. This will yield a config object for you. You can set excluded models like so:

config.excluded_models = ["User", "Company"] # these models will not be multi-tenanted, but remain in the global (public) namespace

Note that a string representation of the model name is now the standard so that models are properly constantized when reloaded in development

### Handling Environments
### Providing a default schema

By default, when not using postgresql schemas, Apartment will prepend the environment to the database name
to ensure there is no conflict between your environments. This is mainly for the benefit of your development
and test environments. If you wish to turn this option off in production, you could do something like:
By default, ActiveRecord will use `"$user", public` as the default `schema_search_path`. This can be modified if you wish to use a different default schema be setting:

config.prepend_environment = !Rails.env.production?
config.default_schema = "some_other_schema"

With that set, all excluded models will use this schema as the table name prefix instead of `public` and `reset` on `Apartment::Database` will return to this schema also

### Managing Migrations

Expand All @@ -119,7 +118,15 @@ You can then migration your databases using the rake task:
This basically invokes `Apartment::Database.migrate(#{db_name})` for each database name supplied
from `Apartment.database_names`

### Delayed::Job
### Handling Environments

By default, when not using postgresql schemas, Apartment will prepend the environment to the database name
to ensure there is no conflict between your environments. This is mainly for the benefit of your development
and test environments. If you wish to turn this option off in production, you could do something like:

config.prepend_environment = !Rails.env.production?

## Delayed::Job

If using Rails ~> 3.2, you *must* use `delayed_job ~> 3.0`. It has better Rails 3 support plus has some major changes that affect the serialization of models.
I haven't been able to get `psych` working whatsoever as the YAML parser, so to get things to work properly, you must explicitly set the parser to `syck` *before* requiring `delayed_job`
Expand Down
5 changes: 4 additions & 1 deletion lib/apartment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Apartment

class << self
attr_accessor :use_postgres_schemas, :seed_after_create, :prepend_environment
attr_writer :database_names, :excluded_models
attr_writer :database_names, :excluded_models, :default_schema

# configure apartment with available options
def configure
Expand All @@ -21,6 +21,9 @@ def excluded_models
@excluded_models || []
end

def default_schema
@default_schema || "public"
end
end

autoload :Database, 'apartment/database'
Expand Down
10 changes: 5 additions & 5 deletions lib/apartment/adapters/postgresql_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Database

def self.postgresql_adapter(config)
Apartment.use_postgres_schemas ?
Adapters::PostgresqlSchemaAdapter.new(config, :schema_search_path => ActiveRecord::Base.connection.schema_search_path) :
Adapters::PostgresqlSchemaAdapter.new(config) :
Adapters::PostgresqlAdapter.new(config)
end
end
Expand Down Expand Up @@ -53,7 +53,7 @@ def drop(database)
end

# Reset search path to default search_path
# Set the table_name to always use the public namespace for excluded models
# Set the table_name to always use the default namespace for excluded models
#
def process_excluded_models
Apartment.excluded_models.each do |excluded_model|
Expand All @@ -66,14 +66,14 @@ def process_excluded_models

excluded_model.constantize.tap do |klass|
# some models (such as delayed_job) seem to load and cache their column names before this,
# so would never get the public prefix, so reset first
# so would never get the default prefix, so reset first
klass.reset_column_information

# Ensure that if a schema *was* set, we override
table_name = klass.table_name.split('.', 2).last

# Not sure why, but Delayed::Job somehow ignores table_name_prefix... so we'll just manually set table name instead
klass.table_name = "public.#{table_name}"
klass.table_name = "#{Apartment.default_schema}.#{table_name}"
end
end
end
Expand All @@ -83,7 +83,7 @@ def process_excluded_models
# @return {String} default schema search path
#
def reset
ActiveRecord::Base.connection.schema_search_path = @defaults[:schema_search_path]
ActiveRecord::Base.connection.schema_search_path = Apartment.default_schema
end

protected
Expand Down
1 change: 1 addition & 0 deletions lib/apartment/database.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
module Apartment

# The main entry point to Apartment functions
#
module Database

extend self
Expand Down
1 change: 1 addition & 0 deletions spec/config/database.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ connections:
database: apartment_postgresql_test
min_messages: WARNING
username: postgres
schema_search_path: public
password:

mysql:
Expand Down
48 changes: 24 additions & 24 deletions spec/database_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,42 +26,42 @@
end

context "using postgresql" do

# See apartment.yml file in dummy app config

let(:config){ Apartment::Test.config['connections']['postgresql'].symbolize_keys }
let(:database){ Apartment::Test.next_db }
let(:database2){ Apartment::Test.next_db }

before do
Apartment.use_postgres_schemas = true
ActiveRecord::Base.establish_connection config
Apartment::Test.load_schema # load the Rails schema in the public db schema
subject.stub(:config).and_return config # Use postgresql database config for this test
end

describe "#adapter" do
before do
subject.reload!
end

it "should load postgresql adapter" do
subject.adapter
Apartment::Adapters::PostgresqlAdapter.should be_a(Class)
end

it "should raise exception with invalid adapter specified" do
subject.stub(:config).and_return config.merge(:adapter => 'unkown')

expect {
Apartment::Database.adapter
}.to raise_error
end

end

context "with schemas" do

before do
Apartment.configure do |config|
config.excluded_models = []
Expand All @@ -70,25 +70,25 @@
end
subject.create database
end

after{ subject.drop database }

describe "#create" do
it "should seed data" do
subject.switch database
User.count.should be > 0
end
end

describe "#switch" do

let(:x){ rand(3) }

context "creating models" do

before{ subject.create database2 }
after{ subject.drop database2 }

it "should create a model instance in the current schema" do
subject.switch database2
db2_count = User.count + x.times{ User.create }
Expand All @@ -103,31 +103,31 @@
User.count.should == db_count
end
end

context "with excluded models" do

before do
Apartment.configure do |config|
config.excluded_models = ["Company"]
end
subject.init
end

it "should create excluded models in public schema" do
subject.reset # ensure we're on public schema
count = Company.count + x.times{ Company.create }

subject.switch database
x.times{ Company.create }
Company.count.should == count + x
subject.reset
Company.count.should == count + x
end
end

end

end

end
end
39 changes: 36 additions & 3 deletions spec/examples/schema_adapter_examples.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,26 @@

describe "#init" do

it "should process model exclusions" do
before do
Apartment.configure do |config|
config.excluded_models = ["Company"]
end
end

it "should process model exclusions" do
Apartment::Database.init

Company.table_name.should == "public.companies"
end

context "with a default_schema", :default_schema => true do

it "should set the proper table_name on excluded_models" do
Apartment::Database.init

Company.table_name.should == "#{default_schema}.companies"
end
end
end

#
Expand Down Expand Up @@ -91,15 +102,23 @@

it "should reset" do
subject.process(schema1)
connection.schema_search_path.should == public_schema
connection.schema_search_path.should start_with public_schema
end
end

describe "#reset" do
it "should reset connection" do
subject.switch(schema1)
subject.reset
connection.schema_search_path.should == public_schema
connection.schema_search_path.should start_with public_schema
end

context "with default_schema", :default_schema => true do
it "should reset to the default schema" do
subject.switch(schema1)
subject.reset
connection.schema_search_path.should start_with default_schema
end
end
end

Expand Down Expand Up @@ -134,6 +153,20 @@

after{ subject.drop(db) }
end

describe "with default_schema specified", :default_schema => true do
before do
subject.switch(schema1)
end

it "should switch out the default schema rather than public" do
connection.schema_search_path.should_not include default_schema
end

it "should still switch to the switched schema" do
connection.schema_search_path.should start_with schema1
end
end
end

describe "#current_database" do
Expand Down
21 changes: 12 additions & 9 deletions spec/support/apartment_helpers.rb
Original file line number Diff line number Diff line change
@@ -1,38 +1,41 @@
module Apartment
module Test

extend self

def reset
Apartment.excluded_models = nil
Apartment.use_postgres_schemas = nil
Apartment.seed_after_create = nil
Apartment.default_schema = nil
end

def next_db
@x ||= 0
"db_#{@x += 1}"
end

def drop_schema(schema)
ActiveRecord::Base.connection.execute("DROP SCHEMA IF EXISTS #{schema} CASCADE") rescue true
end


# Use this if you don't want to import schema.rb etc... but need the postgres schema to exist
# basically for speed purposes
def create_schema(schema)
ActiveRecord::Base.connection.execute("CREATE SCHEMA #{schema}")
end

def load_schema
silence_stream(STDOUT){ load(Rails.root.join('db', 'schema.rb')) }
end

def migrate
ActiveRecord::Migrator.migrate(Rails.root + ActiveRecord::Migrator.migrations_path)
end

def rollback
ActiveRecord::Migrator.rollback(Rails.root + ActiveRecord::Migrator.migrations_path)
end

end
end
16 changes: 16 additions & 0 deletions spec/support/contexts.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Some shared contexts for specs

shared_context "with default schema", :default_schema => true do
let(:default_schema){ Apartment::Test.next_db }

before do
Apartment::Test.create_schema(default_schema)
Apartment.default_schema = default_schema
end

after do
# resetting default_schema so we can drop and any further resets won't try to access droppped schema
Apartment.default_schema = nil
Apartment::Test.drop_schema(default_schema)
end
end
Loading

0 comments on commit 505ea1f

Please sign in to comment.