apartment 0.13.0.1 → 0.13.1

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.
@@ -1,114 +1,115 @@
1
1
  module Apartment
2
-
2
+
3
3
  module Database
4
-
4
+
5
5
  def self.postgresql_adapter(config)
6
- Apartment.use_postgres_schemas ?
6
+ Apartment.use_postgres_schemas ?
7
7
  Adapters::PostgresqlSchemaAdapter.new(config, :schema_search_path => ActiveRecord::Base.connection.schema_search_path) :
8
8
  Adapters::PostgresqlAdapter.new(config)
9
9
  end
10
-
10
+
11
11
  end
12
-
12
+
13
13
  module Adapters
14
-
14
+
15
15
  # Default adapter when not using Postgresql Schemas
16
16
  class PostgresqlAdapter < AbstractAdapter
17
-
17
+
18
18
  protected
19
-
19
+
20
20
  # Connect to new database
21
21
  # Abstract adapter will catch generic ActiveRecord error
22
22
  # Catch specific adapter errors here
23
- #
23
+ #
24
24
  # @param {String} database Database name
25
- #
25
+ #
26
26
  def connect_to_new(database)
27
27
  super
28
- rescue PGError
28
+ rescue PGError => e
29
29
  raise DatabaseNotFound, "Cannot find database #{environmentify(database)}"
30
- end
31
-
30
+ end
31
+
32
32
  end
33
-
33
+
34
34
  # Separate Adapter for Postgresql when using schemas
35
35
  class PostgresqlSchemaAdapter < AbstractAdapter
36
-
36
+
37
37
  # Get the current schema search path
38
- #
38
+ #
39
39
  # @return {String} current schema search path
40
- #
40
+ #
41
41
  def current_database
42
42
  ActiveRecord::Base.connection.schema_search_path
43
43
  end
44
-
44
+
45
45
  # Drop the database schema
46
- #
46
+ #
47
47
  # @param {String} database Database (schema) to drop
48
- #
48
+ #
49
49
  def drop(database)
50
50
  ActiveRecord::Base.connection.execute("DROP SCHEMA #{database} CASCADE")
51
-
52
- rescue ActiveRecord::StatementInvalid
51
+
52
+ rescue ActiveRecord::StatementInvalid => e
53
53
  raise SchemaNotFound, "The schema #{database.inspect} cannot be found."
54
54
  end
55
55
 
56
56
  # Reset search path to default search_path
57
57
  # Set the table_name to always use the public namespace for excluded models
58
- #
58
+ #
59
59
  def process_excluded_models
60
- Apartment.excluded_models.each do |excluded_model|
60
+ Apartment.excluded_models.each do |excluded_model|
61
61
  # Note that due to rails reloading, we now take string references to classes rather than
62
62
  # actual object references. This way when we contantize, we always get the proper class reference
63
63
  if excluded_model.is_a? Class
64
64
  warn "[Deprecation Warning] Passing class references to excluded models is now deprecated, please use a string instead"
65
65
  excluded_model = excluded_model.name
66
66
  end
67
-
67
+
68
68
  excluded_model.constantize.tap do |klass|
69
- # some models (such as delayed_job) seem to load and cache their column names before this,
69
+ # some models (such as delayed_job) seem to load and cache their column names before this,
70
70
  # so would never get the public prefix, so reset first
71
- klass.reset_column_information
71
+ klass.reset_column_information
72
72
 
73
73
  # Ensure that if a schema *was* set, we override
74
- table_name = klass.table_name.split('.', 2).last
74
+ table_name = klass.table_name.split('.', 2).last
75
75
 
76
76
  # Not sure why, but Delayed::Job somehow ignores table_name_prefix... so we'll just manually set table name instead
77
- klass.table_name = "public.#{table_name}"
78
- end
79
- end
77
+ klass.table_name = "public.#{table_name}"
78
+ end
79
+ end
80
80
  end
81
-
81
+
82
82
  # Reset schema search path to the default schema_search_path
83
- #
83
+ #
84
84
  # @return {String} default schema search path
85
- #
86
- def reset
87
- ActiveRecord::Base.connection.schema_search_path = @defaults[:schema_search_path]
88
- end
85
+ #
86
+ def reset
87
+ ActiveRecord::Base.connection.schema_search_path = @defaults[:schema_search_path]
88
+ end
89
89
 
90
- protected
90
+ protected
91
91
 
92
- # Set schema search path to new schema
93
- #
94
- def connect_to_new(database = nil)
95
- return reset if database.nil?
96
- ActiveRecord::Base.connection.schema_search_path = database
92
+ # Set schema search path to new schema
93
+ #
94
+ def connect_to_new(database = nil)
95
+ return reset if database.nil?
96
+ ActiveRecord::Base.connection.clear_cache!
97
+ ActiveRecord::Base.connection.schema_search_path = database
97
98
 
98
99
  rescue ActiveRecord::StatementInvalid => e
99
100
  raise SchemaNotFound, "The schema #{database.inspect} cannot be found."
100
- end
101
+ end
101
102
 
102
103
  # Create the new schema
103
- #
104
- def create_database(database)
105
- ActiveRecord::Base.connection.execute("CREATE SCHEMA #{database}")
104
+ #
105
+ def create_database(database)
106
+ ActiveRecord::Base.connection.execute("CREATE SCHEMA #{database}")
106
107
 
107
- rescue ActiveRecord::StatementInvalid => e
108
- raise SchemaExists, "The schema #{database} already exists."
108
+ rescue ActiveRecord::StatementInvalid => e
109
+ raise SchemaExists, "The schema #{database} already exists."
109
110
  end
110
-
111
+
111
112
  end
112
-
113
+
113
114
  end
114
115
  end
@@ -1,59 +1,57 @@
1
1
  require 'active_support/core_ext/module/delegation'
2
2
 
3
3
  module Apartment
4
-
4
+
5
5
  # The main entry point to Apartment functions
6
- module Database
7
-
6
+ module Database
7
+
8
8
  extend self
9
9
 
10
- delegate :create, :current_database, :drop, :process, :process_excluded_models, :reset, :seed, :switch, :to => :adapter
11
-
12
- attr_writer :config
10
+ delegate :create, :current_database, :process, :process_excluded_models, :reset, :seed, :switch, :to => :adapter
13
11
 
14
12
  # Initialize Apartment config options such as excluded_models
15
- #
16
- def init
13
+ #
14
+ def init
17
15
  process_excluded_models
18
16
  end
19
-
17
+
20
18
  # Fetch the proper multi-tenant adapter based on Rails config
21
- #
19
+ #
22
20
  # @return {subclass of Apartment::AbstractAdapter}
23
- #
21
+ #
24
22
  def adapter
25
23
  @adapter ||= begin
26
24
  adapter_method = "#{config[:adapter]}_adapter"
27
-
28
- begin
25
+
26
+ begin
29
27
  require "apartment/adapters/#{adapter_method}"
30
- rescue LoadError
28
+ rescue LoadError => e
31
29
  raise "The adapter `#{config[:adapter]}` is not yet supported"
32
30
  end
33
31
 
34
32
  unless respond_to?(adapter_method)
35
33
  raise AdapterNotFound, "database configuration specifies nonexistent #{config[:adapter]} adapter"
36
34
  end
37
-
35
+
38
36
  send(adapter_method, config)
39
37
  end
40
38
  end
41
-
39
+
42
40
  # Reset config and adapter so they are regenerated
43
- #
41
+ #
44
42
  def reload!
45
43
  @adapter = nil
46
44
  @config = nil
47
45
  end
48
-
46
+
49
47
  private
50
-
48
+
51
49
  # Fetch the rails database configuration
52
- #
50
+ #
53
51
  def config
54
52
  @config ||= Rails.configuration.database_configuration[Rails.env].symbolize_keys
55
53
  end
56
-
54
+
57
55
  end
58
-
56
+
59
57
  end
@@ -3,22 +3,22 @@ require 'apartment/delayed_job/enqueue'
3
3
  module Apartment
4
4
  module Delayed
5
5
  module Job
6
-
6
+
7
7
  # Before and after hooks for performing Delayed Jobs within a particular apartment database
8
8
  # Include these in your delayed jobs models and make sure provide a @database attr that will be serialized by DJ
9
9
  # Note also that any models that are being serialized need the Apartment::Delayed::Requirements module mixed in to it
10
10
  module Hooks
11
-
11
+
12
12
  attr_accessor :database
13
-
13
+
14
14
  def before(job)
15
15
  Apartment::Database.switch(job.payload_object.database) if job.payload_object.database
16
16
  end
17
-
17
+
18
18
  def after
19
19
  Apartment::Database.reset
20
20
  end
21
-
21
+
22
22
  end
23
23
  end
24
24
  end
@@ -3,41 +3,25 @@ module Apartment
3
3
  # Provides a rack based db switching solution based on subdomains
4
4
  # Assumes that database name should match subdomain
5
5
  class Subdomain
6
-
6
+
7
7
  def initialize(app)
8
8
  @app = app
9
9
  end
10
-
10
+
11
11
  def call(env)
12
- host = Rack::Request.new(env).host
13
-
14
- database = subdomain(host)
15
-
12
+ request = ActionDispatch::Request.new(env)
13
+
14
+ database = subdomain(request)
15
+
16
16
  Apartment::Database.switch database if database
17
-
17
+
18
18
  @app.call(env)
19
19
  end
20
-
21
- private
22
-
23
- # *Almost* a direct ripoff of ActionDispatch::Request subdomain methods
24
-
25
- # Only care about the first subdomain for the database name
26
- def subdomain(host)
27
- subdomains(host).first
20
+
21
+ def subdomain(request)
22
+ request.subdomain.present? && request.subdomain || nil
28
23
  end
29
-
30
- # Assuming tld_length of 1, might need to make this configurable in Apartment in the future for things like .co.uk
31
- def subdomains(host, tld_length = 1)
32
- return [] unless named_host?(host)
33
-
34
- host.split('.')[0..-(tld_length + 2)]
35
- end
36
-
37
- def named_host?(host)
38
- !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
39
- end
40
-
24
+
41
25
  end
42
26
  end
43
27
  end
@@ -1,3 +1,3 @@
1
1
  module Apartment
2
- VERSION = "0.13.0.1"
2
+ VERSION = "0.13.1"
3
3
  end
@@ -2,76 +2,67 @@ require 'spec_helper'
2
2
  require 'apartment/adapters/postgresql_adapter' # specific adapters get dynamically loaded based on adapter name, so we must manually require here
3
3
 
4
4
  describe Apartment::Adapters::PostgresqlAdapter do
5
-
5
+
6
6
  before do
7
7
  ActiveRecord::Base.establish_connection Apartment::Test.config['connections']['postgresql']
8
8
  @schema_search_path = ActiveRecord::Base.connection.schema_search_path
9
9
  end
10
-
10
+
11
11
  after do
12
12
  ActiveRecord::Base.clear_all_connections!
13
13
  end
14
-
14
+
15
15
  context "using schemas" do
16
-
16
+
17
17
  let(:schema){ 'first_db_schema' }
18
18
  let(:schema2){ 'another_db_schema' }
19
19
  let(:database_names){ ActiveRecord::Base.connection.execute("SELECT nspname FROM pg_namespace;").collect{|row| row['nspname']} }
20
-
20
+
21
21
  subject{ Apartment::Database.postgresql_adapter Apartment::Test.config['connections']['postgresql'].symbolize_keys }
22
-
22
+
23
23
  before do
24
24
  Apartment.use_postgres_schemas = true
25
25
  subject.create(schema)
26
26
  subject.create(schema2)
27
27
  end
28
-
28
+
29
29
  after do
30
30
  # sometimes we manually drop these schemas in testing, dont' care if we can't drop hence rescue
31
- subject.drop(schema) rescue true
31
+ subject.drop(schema) rescue true
32
32
  subject.drop(schema2) rescue true
33
33
  end
34
-
34
+
35
35
  describe "#create" do
36
-
36
+
37
37
  it "should create the new schema" do
38
38
  database_names.should include(schema)
39
39
  end
40
-
40
+
41
41
  it "should load schema.rb to new schema" do
42
42
  ActiveRecord::Base.connection.schema_search_path = schema
43
43
  ActiveRecord::Base.connection.tables.should include('companies')
44
44
  end
45
-
46
- it "should not load schema.rb if load_schema is false" do
47
- Apartment.load_schema = false
48
- subject.create("schemax") do
49
- ActiveRecord::Base.connection.tables.should_not include('companies')
50
- end
51
- # Cleanup
52
- subject.drop("schemax")
53
- end
54
-
45
+
55
46
  it "should reset connection when finished" do
56
47
  ActiveRecord::Base.connection.schema_search_path.should_not == schema
57
48
  end
58
-
49
+
59
50
  it "should yield to block if passed" do
60
- Apartment::Test.migrate # ensure we have latest schema in the public
51
+ Apartment::Test.migrate # ensure we have latest schema in the public
61
52
  subject.drop(schema2) # so we don't get errors on creation
62
-
53
+
63
54
  @count = 0 # set our variable so its visible in and outside of blocks
64
-
55
+
65
56
  subject.create(schema2) do
66
57
  @count = User.count
67
58
  ActiveRecord::Base.connection.schema_search_path.should == schema2
68
59
  User.create
69
60
  end
70
-
61
+
71
62
  subject.process(schema2){ User.count.should == @count + 1 }
72
63
  end
73
64
  end
74
-
65
+
75
66
  describe "#drop" do
76
67
 
77
68
  it "should delete the database" do
@@ -87,31 +78,31 @@ describe Apartment::Adapters::PostgresqlAdapter do
87
78
  }.to raise_error(Apartment::SchemaNotFound)
88
79
  end
89
80
  end
90
-
91
-
81
+
82
+
92
83
  describe "#process" do
93
84
  it "should connect" do
94
85
  subject.process(schema) do
95
86
  ActiveRecord::Base.connection.schema_search_path.should == schema
96
87
  end
97
88
  end
98
-
89
+
99
90
  it "should reset" do
100
91
  subject.process(schema)
101
92
  ActiveRecord::Base.connection.schema_search_path.should == @schema_search_path
102
93
  end
103
-
94
+
104
95
  # We're often finding when using Apartment in tests, the `current_database` (ie the previously attached to schema)
105
96
  # gets dropped, but process will try to return to that schema in a test. We should just reset if it doesnt exist
106
97
  it "should not throw exception if current_database (schema) is no longer accessible" do
107
98
  subject.switch(schema2)
108
-
99
+
109
100
  expect {
110
101
  subject.process(schema){ subject.drop(schema2) }
111
102
  }.to_not raise_error(Apartment::SchemaNotFound)
112
103
  end
113
104
  end
114
-
105
+
115
106
  describe "#reset" do
116
107
  it "should reset connection" do
117
108
  subject.switch(schema)
@@ -119,28 +110,28 @@ describe Apartment::Adapters::PostgresqlAdapter do
119
110
  ActiveRecord::Base.connection.schema_search_path.should == @schema_search_path
120
111
  end
121
112
  end
122
-
113
+
123
114
  describe "#switch" do
124
115
  it "should connect to new schema" do
125
116
  subject.switch(schema)
126
117
  ActiveRecord::Base.connection.schema_search_path.should == schema
127
118
  end
128
-
119
+
129
120
  it "should reset connection if database is nil" do
130
121
  subject.switch
131
122
  ActiveRecord::Base.connection.schema_search_path.should == @schema_search_path
132
123
  end
133
124
  end
134
-
125
+
135
126
  describe "#current_database" do
136
127
  it "should return the current schema name" do
137
128
  subject.switch(schema)
138
129
  subject.current_database.should == schema
139
130
  end
140
131
  end
141
-
132
+
142
133
  end
143
-
134
+
144
135
  context "using databases" do
145
136
  # TODO
146
137
  end