apartment 0.11.0 → 0.11.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.
data/HISTORY.md CHANGED
@@ -1,3 +1,11 @@
1
+ # 0.11.1
2
+ * Sep 22, 2011
3
+
4
+ - Better use of Railties for initializing apartment
5
+ - The following changes were necessary as I haven't figured out how to properly hook into Rails reloading
6
+ - Added reloader middleware in development to init Apartment on each request
7
+ - Override `reload!` in console to also init Apartment
8
+
1
9
  # 0.11.0
2
10
  * Sep 20, 2011
3
11
 
data/lib/apartment.rb CHANGED
@@ -9,7 +9,6 @@ module Apartment
9
9
  # configure apartment with available options
10
10
  def configure
11
11
  yield self if block_given?
12
- Database.init
13
12
  end
14
13
 
15
14
  # Be careful not to use `return` here so both Proc and lambda can be used without breaking
@@ -30,6 +29,7 @@ module Apartment
30
29
 
31
30
  autoload :Database, 'apartment/database'
32
31
  autoload :Migrator, 'apartment/migrator'
32
+ autoload :Reloader, 'apartment/reloader'
33
33
 
34
34
  module Adapters
35
35
  autoload :AbstractAdapter, 'apartment/adapters/abstract_adapter'
@@ -46,6 +46,7 @@ module Apartment
46
46
  current_db = current_database
47
47
  switch(database)
48
48
  yield if block_given?
49
+
49
50
  ensure
50
51
  switch(current_db)
51
52
  end
@@ -87,6 +88,8 @@ module Apartment
87
88
  database
88
89
  end
89
90
 
91
+ # Load the rails seed file into the db
92
+ #
90
93
  def seed_data
91
94
  load_or_abort("#{Rails.root}/db/seeds.rb")
92
95
  end
@@ -7,6 +7,7 @@ module Apartment
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
  end
11
12
 
12
13
  module Adapters
@@ -18,15 +19,18 @@ module Apartment
18
19
  # Separate Adapter for Postgresql when using schemas
19
20
  class PostgresqlSchemaAdapter < AbstractAdapter
20
21
 
21
- # Set schema path or connect to new db
22
- # TODO sanitize method doesn't work with schemas as the default schema uses "$user", stripping out the quotes makes it fail
22
+ # Set schema path or connect to new db
23
+ #
23
24
  def connect_to_new(database = nil)
24
25
  return reset if database.nil?
25
26
  ActiveRecord::Base.connection.schema_search_path = database
27
+
26
28
  rescue ActiveRecord::StatementInvalid => e
27
29
  raise SchemaNotFound, "The Schema #{database.inspect} cannot be found."
28
30
  end
29
31
 
32
+ # Create a db schema
33
+ #
30
34
  def create(database)
31
35
  ActiveRecord::Base.connection.execute("CREATE SCHEMA #{database}")
32
36
 
@@ -36,10 +40,15 @@ module Apartment
36
40
  # Seed data if appropriate
37
41
  seed_data if Apartment.seed_after_create
38
42
  end
43
+
39
44
  rescue ActiveRecord::StatementInvalid => e
40
45
  raise SchemaExists, "The schema #{database} already exists."
41
46
  end
42
47
 
48
+ # Get the current schema search path
49
+ #
50
+ # @return {String} current schema search path
51
+ #
43
52
  def current_database
44
53
  ActiveRecord::Base.connection.schema_search_path
45
54
  end
@@ -47,17 +56,26 @@ module Apartment
47
56
  # Set the table_name to always use the public namespace for excluded models
48
57
  #
49
58
  def process_excluded_models
50
-
59
+
51
60
  Apartment.excluded_models.each do |excluded_model|
52
- # some models (such as delayed_job) seem to load and cache their column names before this,
53
- # so would never get the public prefix, so reset first
54
- excluded_model.reset_column_information
61
+ # Note that due to rails reloading, we now take string references to classes rather than
62
+ # actual object references. This way when we contantize, we always get the proper class reference
63
+ if excluded_model.is_a? Class
64
+ warn "[Deprecation Warning] Passing class references to excluded models is now deprecated, please use a string instead"
65
+ excluded_model = excluded_model.name
66
+ end
67
+
68
+ excluded_model.constantize.tap do |klass|
69
+ # some models (such as delayed_job) seem to load and cache their column names before this,
70
+ # so would never get the public prefix, so reset first
71
+ klass.reset_column_information
55
72
 
56
- # Ensure that if a schema *was* set, we override
57
- table_name = excluded_model.table_name.split('.', 2).last
73
+ # Ensure that if a schema *was* set, we override
74
+ table_name = klass.table_name.split('.', 2).last
58
75
 
59
- # Not sure why, but Delayed::Job somehow ignores table_name_prefix... so we'll just manually set table name instead
60
- excluded_model.table_name = "public.#{table_name}"
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
61
79
  end
62
80
  end
63
81
 
@@ -0,0 +1,12 @@
1
+ # A workaraound to get `reload!` to also call Apartment::Database.init
2
+ # This is unfortunate, but I haven't figured out how to hook into the reload process *after* files are reloaded
3
+
4
+ # reloads the environment
5
+ def reload!(print=true)
6
+ puts "Reloading..." if print
7
+ # This triggers the to_prepare callbacks
8
+ ActionDispatch::Callbacks.new(Proc.new {}, false).call({})
9
+ # Manually init Apartment again once classes are reloaded
10
+ Apartment::Database.init
11
+ true
12
+ end
@@ -1,49 +1,57 @@
1
1
  require 'active_support/core_ext/module/delegation'
2
2
 
3
3
  module Apartment
4
+
5
+ # The main entry point to Apartment functions
4
6
  module Database
5
7
 
6
- class << self
8
+ extend self
7
9
 
8
- # pass these methods to our adapter
9
- delegate :create, :current_database, :process, :process_excluded_models, :reset, :seed, :switch, :to => :adapter
10
+ delegate :create, :current_database, :process, :process_excluded_models, :reset, :seed, :switch, :to => :adapter
10
11
 
11
- # Call init to establish a connection to the public schema on all excluded models
12
- # This must be done before creating any new schemas or switching
13
- def init
14
- process_excluded_models
15
- end
16
-
17
- def adapter
18
- @adapter ||= begin
19
- adapter_method = "#{config[:adapter]}_adapter"
20
-
21
- begin
22
- require "apartment/adapters/#{adapter_method}"
23
- rescue LoadError => e
24
- raise "The adapter `#{config[:adapter]}` is not yet supported"
25
- end
12
+ # Initialize Apartment config options such as excluded_models
13
+ #
14
+ def init
15
+ process_excluded_models
16
+ end
17
+
18
+ # Fetch the proper multi-tenant adapter based on Rails config
19
+ #
20
+ # @return {subclass of Apartment::AbstractAdapter}
21
+ #
22
+ def adapter
23
+ @adapter ||= begin
24
+ adapter_method = "#{config[:adapter]}_adapter"
25
+
26
+ begin
27
+ require "apartment/adapters/#{adapter_method}"
28
+ rescue LoadError => e
29
+ raise "The adapter `#{config[:adapter]}` is not yet supported"
30
+ end
26
31
 
27
- unless respond_to?(adapter_method)
28
- raise AdapterNotFound, "database configuration specifies nonexistent #{config[:adapter]} adapter"
29
- end
30
-
31
- send(adapter_method, config)
32
+ unless respond_to?(adapter_method)
33
+ raise AdapterNotFound, "database configuration specifies nonexistent #{config[:adapter]} adapter"
32
34
  end
33
- end
34
-
35
- def reload!
36
- @adapter = nil
37
- @config = nil
38
- end
39
35
 
40
- private
41
-
42
- def config
43
- @config ||= Rails.configuration.database_configuration[Rails.env].symbolize_keys
36
+ send(adapter_method, config)
44
37
  end
45
38
  end
46
-
47
- end
39
+
40
+ # Reset config and adapter so they are regenerated
41
+ #
42
+ def reload!
43
+ @adapter = nil
44
+ @config = nil
45
+ end
46
+
47
+ private
48
+
49
+ # Fetch the rails database configuration
50
+ #
51
+ def config
52
+ @config ||= Rails.configuration.database_configuration[Rails.env].symbolize_keys
53
+ end
54
+
55
+ end
48
56
 
49
57
  end
@@ -3,23 +3,44 @@ require 'rails'
3
3
  module Apartment
4
4
  class Railtie < Rails::Railtie
5
5
 
6
- # Ensure that active_record is loaded, then run default config
7
- initializer 'apartment.configure' do
8
- # ActiveSupport.on_load(:active_record) do
9
-
10
- Apartment.configure do |config|
11
- config.excluded_models = []
12
- config.use_postgres_schemas = true
13
- config.database_names = []
14
- config.seed_after_create = false
15
- config.prepend_environment = true
16
- end
17
-
18
- # end
6
+ # Set up our default config options
7
+ # Do this before the app initializers run so we don't override custom settings
8
+ config.before_initialize do
9
+ Apartment.configure do |config|
10
+ config.excluded_models = []
11
+ config.use_postgres_schemas = true
12
+ config.database_names = []
13
+ config.seed_after_create = false
14
+ config.prepend_environment = true
15
+ end
16
+ end
17
+
18
+ # Hook into ActionDispatch::Reloader to ensure Apartment is properly initialized
19
+ # Note that this doens't entirely work as expected in Development, because this is called before classes are reloaded
20
+ # See the above middleware declaration to help with this. Hope to fix that soon.
21
+ config.to_prepare do
22
+ Apartment::Database.init
19
23
  end
20
24
 
21
25
  rake_tasks do
22
26
  load 'tasks/apartment.rake'
23
27
  end
28
+
29
+ # The following initializers are a workaround to the fact that I can't properly hook into the rails reloader
30
+ # Note this is technically valid for any environment where cache_classes is false, for us, it's just development
31
+ if Rails.env.development?
32
+
33
+ # Apartment::Reloader is middleware to initialize things properly on each requestion dev
34
+ initializer 'apartment.init' do |app|
35
+ app.config.middleware.use "Apartment::Reloader"
36
+ end
37
+
38
+ # Overrides reload! to also call Apartment::Database.init as well so that the reloaded classes have the proper table_names
39
+ console do
40
+ require 'apartment/console'
41
+ end
42
+
43
+ end
44
+
24
45
  end
25
46
  end
@@ -0,0 +1,23 @@
1
+ module Apartment
2
+
3
+ class Reloader
4
+
5
+ # Middleware used in development to init Apartment for each request
6
+ # Necessary due to code reload (annoying). I couldn't figure out how to properly hook into
7
+ # the Rails reload process *after* files are reloaded, so I've used this in the meantime.
8
+ #
9
+ # Note that this has one MAJOR caveat. Doing `reload!` in the console in development WILL NOT run init again
10
+ # Thus, excluded models will not be processed again and will be queried from the current_schema rather than public.
11
+ # I hope to fix this soon
12
+ def initialize(app)
13
+ @app = app
14
+ end
15
+
16
+ def call(env)
17
+ Database.init
18
+ @app.call(env)
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -1,3 +1,3 @@
1
1
  module Apartment
2
- VERSION = "0.11.0"
2
+ VERSION = "0.11.1"
3
3
  end
@@ -29,6 +29,8 @@ describe Apartment::Database do
29
29
  config.excluded_models = [Company]
30
30
  end
31
31
 
32
+ Apartment::Database.init
33
+
32
34
  Company.table_name.should == "public.companies"
33
35
  end
34
36
 
@@ -158,8 +160,11 @@ describe Apartment::Database do
158
160
 
159
161
  context "with excluded models" do
160
162
 
161
- Apartment.configure do |config|
162
- config.excluded_models = [Company]
163
+ before do
164
+ Apartment.configure do |config|
165
+ config.excluded_models = [Company]
166
+ end
167
+ Apartment::Database.init
163
168
  end
164
169
 
165
170
  it "should ignore excluded models" do
data/spec/spec_helper.rb CHANGED
@@ -23,8 +23,8 @@ RSpec.configure do |config|
23
23
  config.include RSpec::Integration::CapybaraSessions, :type => :request
24
24
 
25
25
  config.before(:all) do
26
- # Ensure that each test starts with a clean connect
27
- # Necessary as some tests will leak things like current_schema into the next
26
+ # Ensure that each test starts with a clean connection
27
+ # Necessary as some tests will leak things like current_schema into the next test
28
28
  ActiveRecord::Base.clear_all_connections!
29
29
  end
30
30
 
@@ -4,7 +4,6 @@ module Apartment
4
4
  extend self
5
5
 
6
6
  def reset
7
- Apartment::Database.instance_variable_set :@initialized, nil
8
7
  Apartment.excluded_models = nil
9
8
  Apartment.use_postgres_schemas = nil
10
9
  end
@@ -29,11 +28,5 @@ module Apartment
29
28
  ActiveRecord::Migrator.rollback(Rails.root + ActiveRecord::Migrator.migrations_path)
30
29
  end
31
30
 
32
- private
33
-
34
- def sanitize(database)
35
- database.gsub(/[\W]/,'')
36
- end
37
-
38
31
  end
39
32
  end
@@ -17,11 +17,6 @@ describe Apartment do
17
17
  end
18
18
  end
19
19
 
20
- it "should initialize Database" do
21
- Apartment::Database.should_receive(:init).once
22
- Apartment.configure
23
- end
24
-
25
20
  it "should set excluded models" do
26
21
  Apartment.configure do |config|
27
22
  config.excluded_models = excluded_models
@@ -44,7 +39,6 @@ describe Apartment do
44
39
  end
45
40
  Apartment.seed_after_create.should be_true
46
41
  end
47
-
48
42
 
49
43
  context "databases" do
50
44
  it "should return object if it doesnt respond_to call" do
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Apartment::Reloader do
4
+
5
+ context "using postgresql schemas" do
6
+
7
+ before do
8
+ Apartment.excluded_models = ["Company"]
9
+ Company.reset_table_name # ensure we're clean
10
+ end
11
+
12
+ subject{ Apartment::Reloader.new(mock("Rack::Application", :call => nil)) }
13
+
14
+ it "should initialize apartment when called" do
15
+ Company.table_name.should_not include('public.')
16
+ subject.call(mock('env'))
17
+ Company.table_name.should include('public.')
18
+ end
19
+ end
20
+
21
+
22
+ end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: apartment
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.11.0
5
+ version: 0.11.1
6
6
  platform: ruby
7
7
  authors:
8
8
  - Ryan Brunner
@@ -147,6 +147,7 @@ files:
147
147
  - lib/apartment/adapters/abstract_adapter.rb
148
148
  - lib/apartment/adapters/mysql_adapter.rb
149
149
  - lib/apartment/adapters/postgresql_adapter.rb
150
+ - lib/apartment/console.rb
150
151
  - lib/apartment/database.rb
151
152
  - lib/apartment/delayed_job/active_record.rb
152
153
  - lib/apartment/delayed_job/enqueue.rb
@@ -155,6 +156,7 @@ files:
155
156
  - lib/apartment/elevators/subdomain.rb
156
157
  - lib/apartment/migrator.rb
157
158
  - lib/apartment/railtie.rb
159
+ - lib/apartment/reloader.rb
158
160
  - lib/apartment/version.rb
159
161
  - lib/tasks/apartment.rake
160
162
  - spec/adapters/mysql_adapter_spec.rb
@@ -209,6 +211,7 @@ files:
209
211
  - spec/unit/config_spec.rb
210
212
  - spec/unit/middleware/subdomain_elevator_spec.rb
211
213
  - spec/unit/migrator_spec.rb
214
+ - spec/unit/reloader_spec.rb
212
215
  has_rdoc: true
213
216
  homepage: http://github.com/bradrobertson/apartment
214
217
  licenses:
@@ -223,7 +226,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
223
226
  requirements:
224
227
  - - ">="
225
228
  - !ruby/object:Gem::Version
226
- hash: -1056095168951008340
229
+ hash: 3705726283453355884
227
230
  segments:
228
231
  - 0
229
232
  version: "0"
@@ -232,7 +235,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
232
235
  requirements:
233
236
  - - ">="
234
237
  - !ruby/object:Gem::Version
235
- hash: -1056095168951008340
238
+ hash: 3705726283453355884
236
239
  segments:
237
240
  - 0
238
241
  version: "0"