Permalink
Browse files

schemas can now be always included in the schema_search_path using pe…

…rsistent_schemas
  • Loading branch information...
1 parent b9d75d9 commit f766a3e065d88cabb25226df5f53a27bba2930f8 @bradrobertson bradrobertson committed Jun 1, 2012
Showing with 92 additions and 11 deletions.
  1. +15 −1 README.md
  2. +5 −1 lib/apartment.rb
  3. +20 −9 lib/apartment/adapters/postgresql_adapter.rb
  4. +38 −0 spec/examples/schema_adapter_examples.rb
  5. +14 −0 spec/support/contexts.rb
View
@@ -115,14 +115,28 @@ If you have some models that should always access the 'root' database, you can s
Note that a string representation of the model name is now the standard so that models are properly constantized when reloaded in development
-### Providing a default schema
+### Postgresql Schemas
+**Providing a Different default_schema**
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.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
+**Persistent Schemas**
+Apartment will normally just switch the schema_search_path whole hog to the one passed in. This can lead to problems if you want other schemas to always be searched as well. Enter `persistent_schemas`. You can configure a list of other schemas that will always remain in the search path, while the default gets swapped out:
+
+ config.persistent_schemas = ['some', 'other', 'schemas']
+
+This has numerous useful applications. [Hstore](http://www.postgresql.org/docs/9.1/static/hstore.html), for instance, is a popular storage engine for Postgresql. In order to use Hstore, you have to install to a specific schema and have that always in the `schema_search_path`. This could be achieved like so:
+
+ # In a rake task, or on the console...
+ ActiveRecord::Base.connection.execute("CREATE SCHEMA hstore; CREATE EXTENSION HSTORE SCHEMA hstore")
+
+ # configure Apartment as usual
+ config.persistent_schemas = ['hstore']
+
### Managing Migrations
In order to migrate all of your databases (or posgresql schemas) you need to provide a list
View
@@ -4,7 +4,7 @@ module Apartment
class << self
ACCESSOR_METHODS = [:use_postgres_schemas, :seed_after_create, :prepend_environment]
- WRITER_METHODS = [:database_names, :excluded_models, :default_schema]
+ WRITER_METHODS = [:database_names, :excluded_models, :default_schema, :persistent_schemas]
attr_accessor(*ACCESSOR_METHODS)
attr_writer(*WRITER_METHODS)
@@ -28,6 +28,10 @@ def default_schema
@default_schema || "public"
end
+ def persistent_schemas
+ @persistent_schemas || []
+ end
+
# Reset all the config for Apartment
def reset
(ACCESSOR_METHODS + WRITER_METHODS).each{|method| instance_variable_set(:"@#{method}", nil) }
@@ -33,13 +33,7 @@ def connect_to_new(database)
# Separate Adapter for Postgresql when using schemas
class PostgresqlSchemaAdapter < AbstractAdapter
- # Get the current schema search path
- #
- # @return {String} current schema search path
- #
- def current_database
- ActiveRecord::Base.connection.schema_search_path
- end
+ attr_reader :current_database
# Drop the database schema
#
@@ -83,7 +77,8 @@ def process_excluded_models
# @return {String} default schema search path
#
def reset
- ActiveRecord::Base.connection.schema_search_path = Apartment.default_schema
+ @current_database = Apartment.default_schema
+ ActiveRecord::Base.connection.schema_search_path = full_search_path
end
protected
@@ -92,7 +87,9 @@ def reset
#
def connect_to_new(database = nil)
return reset if database.nil?
- ActiveRecord::Base.connection.schema_search_path = database.to_s
+
+ @current_database = database.to_s
+ ActiveRecord::Base.connection.schema_search_path = full_search_path
rescue ActiveRecord::StatementInvalid
raise SchemaNotFound, "The schema #{database.inspect} cannot be found."
@@ -107,6 +104,20 @@ def create_database(database)
raise SchemaExists, "The schema #{database} already exists."
end
+ private
+
+ # Generate the final search path to set including persistent_schemas
+ #
+ def full_search_path
+ persistent_schemas = persistent_schema_string
+ @current_database.to_s + (persistent_schemas.empty? ? "" : ", #{persistent_schemas}")
+ end
+
+ # Cached persistent schemas joined in a valid search path format (comma separated)
+ #
+ def persistent_schema_string
+ Apartment.persistent_schemas.join(', ')
+ end
end
end
end
@@ -120,6 +120,24 @@
connection.schema_search_path.should start_with default_schema
end
end
+
+ context "persistent_schemas", :persistent_schemas => true do
+ before do
+ subject.switch(schema1)
+ subject.reset
+ end
+
+ it "maintains the persistent schemas in the schema_search_path" do
+ connection.schema_search_path.should end_with persistent_schemas.join(', ')
+ end
+
+ context "with default_schema", :default_schema => true do
+ it "prioritizes the switched schema to front of schema_search_path" do
+ subject.reset # need to re-call this as the default_schema wasn't set at the time that the above reset ran
+ connection.schema_search_path.should start_with default_schema
+ end
+ end
+ end
end
describe "#switch" do
@@ -167,13 +185,33 @@
connection.schema_search_path.should start_with schema1
end
end
+
+ context "persistent_schemas", :persistent_schemas => true do
+
+ before{ subject.switch(schema1) }
+
+ it "maintains the persistent schemas in the schema_search_path" do
+ connection.schema_search_path.should end_with persistent_schemas.join(', ')
+ end
+
+ it "prioritizes the switched schema to front of schema_search_path" do
+ connection.schema_search_path.should start_with schema1
+ end
+ end
end
describe "#current_database" do
it "should return the current schema name" do
subject.switch(schema1)
subject.current_database.should == schema1
end
+
+ context "persistent_schemas", :persistent_schemas => true do
+ it "should exlude persistent_schemas" do
+ subject.switch(schema1)
+ subject.current_database.should == schema1
+ end
+ end
end
end
View
@@ -37,4 +37,18 @@
api.drop(database1)
api.drop(database2)
end
+end
+
+shared_context "persistent_schemas", :persistent_schemas => true do
+ let(:persistent_schemas){ ['hstore', 'postgis'] }
+
+ before do
+ persistent_schemas.map{|schema| subject.create(schema) }
+ Apartment.persistent_schemas = persistent_schemas
+ end
+
+ after do
+ Apartment.persistent_schemas = []
+ persistent_schemas.map{|schema| subject.drop(schema) }
+ end
end

0 comments on commit f766a3e

Please sign in to comment.