apartment 0.13.0 → 0.13.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY.md CHANGED
@@ -1,13 +1,20 @@
1
+ # 0.13.0.1
2
+ * Feb 9, 2012
3
+
4
+ - Versioning has become a bit of a mess. Will maintain this line for Rails 3.0.x
5
+ - Removing Rails dependency, now just depends on ActiveRecord :)
6
+ - `drop` was somehow missed from the methods proxied through to the adapter, added that in.
7
+
1
8
  # 0.13.0
2
9
  * Oct 25, 2011
3
-
10
+
4
11
  - `process` will now rescue with reset if the previous schema/db is no longer available
5
12
  - `create` now takes an optional block which allows you to process within the newly created db
6
13
  - Fixed Rails version >= 3.0.10 and < 3.1 because there have been significant testing problems with 3.1, next version will hopefully fix this
7
-
14
+
8
15
  # 0.12.0
9
16
  * Oct 4, 2011
10
-
17
+
11
18
  - Added a `drop` method for removing databases/schemas
12
19
  - Refactored abstract adapter to further remove duplication in concrete implementations
13
20
  - Excluded models now take string references so they are properly reloaded in development
@@ -15,7 +22,7 @@
15
22
 
16
23
  # 0.11.1
17
24
  * Sep 22, 2011
18
-
25
+
19
26
  - Better use of Railties for initializing apartment
20
27
  - The following changes were necessary as I haven't figured out how to properly hook into Rails reloading
21
28
  - Added reloader middleware in development to init Apartment on each request
@@ -23,88 +30,88 @@
23
30
 
24
31
  # 0.11.0
25
32
  * Sep 20, 2011
26
-
33
+
27
34
  - Excluded models no longer use a different connection when using postgresql schemas. Instead their table_name is prefixed with `public.`
28
35
 
29
36
  # 0.10.3
30
37
  * Sep 20, 2011
31
-
38
+
32
39
  - Fix improper raising of exceptions on create and reset
33
40
 
34
41
  # 0.10.2
35
42
  * Sep 15, 2011
36
-
43
+
37
44
  - Remove all the annoying logging for loading db schema and seeding on create
38
45
 
39
46
  # 0.10.1
40
47
  * Aug 11, 2011
41
-
48
+
42
49
  - Fixed bug in DJ where new objects (that hadn't been pulled from the db) didn't have the proper database assigned
43
50
 
44
51
  # 0.10.0
45
52
  * July 29, 2011
46
-
53
+
47
54
  - Added better support for Delayed Job
48
55
  - New config option that enables Delayed Job wrappers
49
56
  - Note that DJ support uses a work-around in order to get queues stored in the public schema, not sure why it doesn't work out of the box, will look into it, until then, see documentation on queue'ng jobs
50
-
57
+
51
58
  # 0.9.2
52
59
  * July 4, 2011
53
-
60
+
54
61
  - Migrations now run associated rails migration fully, fixes schema.rb not being reloaded after migrations
55
62
 
56
63
  # 0.9.1
57
64
  * June 24, 2011
58
-
65
+
59
66
  - Hooks now take the payload object as an argument to fetch the proper db for DJ hooks
60
67
 
61
68
  # 0.9.0
62
69
  * June 23, 2011
63
-
70
+
64
71
  - Added module to provide delayed job hooks
65
72
 
66
73
  # 0.8.0
67
74
  * June 23, 2011
68
-
75
+
69
76
  - Added #current_database which will return the current database (or schema) name
70
77
 
71
78
  # 0.7.0
72
79
  * June 22, 2011
73
-
80
+
74
81
  - Added apartment:seed rake task for seeding all dbs
75
82
 
76
83
  # 0.6.0
77
84
  * June 21, 2011
78
-
85
+
79
86
  - Added #process to connect to new db, perform operations, then ensure a reset
80
87
 
81
88
  # 0.5.1
82
89
  * June 21, 2011
83
-
90
+
84
91
  - Fixed db migrate up/down/rollback
85
92
  - added db:redo
86
93
 
87
94
  # 0.5.0
88
95
  * June 20, 2011
89
-
96
+
90
97
  - Added the concept of an "Elevator", a rack based strategy for db switching
91
98
  - Added the Subdomain Elevator middleware to enabled db switching based on subdomain
92
99
 
93
100
  # 0.4.0
94
101
  * June 14, 2011
95
-
102
+
96
103
  - Added `configure` method on Apartment instead of using yml file, allows for dynamic setting of db names to migrate for rake task
97
104
  - Added `seed_after_create` config option to import seed data to new db on create
98
-
105
+
99
106
  # 0.3.0
100
107
  * June 10, 2011
101
-
108
+
102
109
  - Added full support for database migration
103
110
  - Added in method to establish new connection for excluded models on startup rather than on each switch
104
-
111
+
105
112
  # 0.2.0
106
113
  * June 6, 2011 *
107
-
114
+
108
115
  - Refactor to use more rails/active_support functionality
109
116
  - Refactor config to lazily load apartment.yml if exists
110
117
  - Remove OStruct and just use hashes for fetching methods
data/apartment.gemspec CHANGED
@@ -7,25 +7,28 @@ Gem::Specification.new do |s|
7
7
  s.version = Apartment::VERSION
8
8
 
9
9
  s.authors = ["Ryan Brunner", "Brad Robertson"]
10
- s.summary = %q{A Ruby gem for managing database multitenancy in Rails applications}
11
- s.description = %q{Apartment allows Rails applications to deal with database multitenancy}
10
+ s.summary = %q{A Ruby gem for managing database multitenancy in Rack applications using ActiveRecord}
11
+ s.description = %q{Apartment allows applications using ActiveRecord to deal with database multitenancy}
12
12
  s.email = %w{ryan@ryanbrunner.com bradleyrobertson@gmail.com}
13
13
  s.files = `git ls-files`.split("\n")
14
14
  s.test_files = `git ls-files -- {spec}/*`.split("\n")
15
-
15
+
16
16
  s.homepage = %q{http://github.com/bradrobertson/apartment}
17
17
  s.licenses = ["MIT"]
18
18
  s.require_paths = ["lib"]
19
19
  s.rubygems_version = %q{1.3.7}
20
-
21
- s.add_dependency 'rails', '~> 3.0.10' # must be >= 3.0.10 due to poor schema support pre 3.0.10, but < 3.1 because it hasn't been fully tested yet
22
- s.add_development_dependency 'rake', '~> 0.8.7'
20
+
21
+ s.add_dependency 'activerecord', '~> 3.0.10' # must be >= 3.0.10 due to poor schema support pre 3.0.10
22
+ s.add_dependency 'rack', '~> 1.2.5'
23
+
24
+ s.add_development_dependency 'rake', '~> 0.8.7'
25
+ s.add_development_dependency 'rails', '~> 3.0.10'
23
26
  s.add_development_dependency 'sqlite3'
24
- s.add_development_dependency 'rspec', '~> 2.6.0'
25
- s.add_development_dependency 'rspec-rails', '~> 2.6.1'
26
- s.add_development_dependency 'capybara', '1.0.0'
27
- s.add_development_dependency 'pg', '~> 0.11.0'
28
- s.add_development_dependency 'mysql2', '0.2.7'
27
+ s.add_development_dependency 'rspec', '~> 2.6.0'
28
+ s.add_development_dependency 'rspec-rails', '~> 2.6.1'
29
+ s.add_development_dependency 'capybara', '1.0.0'
30
+ s.add_development_dependency 'pg', '~> 0.11.0'
31
+ s.add_development_dependency 'mysql2', '0.2.7'
29
32
  s.add_development_dependency "silent-postgres", "~> 0.1.1"
30
- s.add_development_dependency 'delayed_job', '~> 2.1.4'
33
+ s.add_development_dependency 'delayed_job', '~> 2.1.4'
31
34
  end
data/lib/apartment.rb CHANGED
@@ -1,16 +1,19 @@
1
1
  require 'apartment/railtie' if defined?(Rails)
2
2
 
3
3
  module Apartment
4
-
4
+
5
5
  class << self
6
- attr_accessor :use_postgres_schemas, :seed_after_create, :prepend_environment
7
- attr_writer :database_names, :excluded_models
8
-
6
+ ACCESSOR_METHODS = [:use_postgres_schemas, :seed_after_create, :prepend_environment]
7
+ WRITER_METHODS = [:database_names, :excluded_models, :load_schema]
8
+
9
+ attr_accessor(*ACCESSOR_METHODS)
10
+ attr_writer(*WRITER_METHODS)
11
+
9
12
  # configure apartment with available options
10
13
  def configure
11
14
  yield self if block_given?
12
15
  end
13
-
16
+
14
17
  # Be careful not to use `return` here so both Proc and lambda can be used without breaking
15
18
  def database_names
16
19
  if @database_names.respond_to?(:call)
@@ -19,55 +22,65 @@ module Apartment
19
22
  @database_names
20
23
  end
21
24
  end
22
-
25
+
23
26
  # Default to none
24
27
  def excluded_models
25
28
  @excluded_models || []
26
29
  end
27
-
30
+
31
+ # Defaults to true if not set
32
+ def load_schema
33
+ @load_schema != false
34
+ end
35
+
36
+ # Reset all the config for Apartment
37
+ def reset
38
+ (ACCESSOR_METHODS + WRITER_METHODS).each{|method| instance_variable_set(:"@#{method}", nil) }
39
+ end
40
+
28
41
  end
29
-
42
+
30
43
  autoload :Database, 'apartment/database'
31
44
  autoload :Migrator, 'apartment/migrator'
32
45
  autoload :Reloader, 'apartment/reloader'
33
-
46
+
34
47
  module Adapters
35
48
  autoload :AbstractAdapter, 'apartment/adapters/abstract_adapter'
36
49
  # Specific adapters will be loaded dynamically based on adapter in config
37
50
  end
38
-
51
+
39
52
  module Elevators
40
53
  autoload :Subdomain, 'apartment/elevators/subdomain'
41
54
  end
42
-
55
+
43
56
  module Delayed
44
-
57
+
45
58
  autoload :Requirements, 'apartment/delayed_job/requirements'
46
-
59
+
47
60
  module Job
48
61
  autoload :Hooks, 'apartment/delayed_job/hooks'
49
62
  end
50
63
  end
51
-
64
+
52
65
  # Exceptions
53
66
  class ApartmentError < StandardError; end
54
-
67
+
55
68
  # Raised when apartment cannot find the adapter specified in <tt>config/database.yml</tt>
56
69
  class AdapterNotFound < ApartmentError; end
57
-
70
+
58
71
  # Raised when database cannot find the specified database
59
72
  class DatabaseNotFound < ApartmentError; end
60
-
73
+
61
74
  # Raised when trying to create a database that already exists
62
75
  class DatabaseExists < ApartmentError; end
63
-
76
+
64
77
  # Raised when database cannot find the specified schema
65
78
  class SchemaNotFound < ApartmentError; end
66
-
79
+
67
80
  # Raised when trying to create a schema that already exists
68
81
  class SchemaExists < ApartmentError; end
69
-
82
+
70
83
  # Raised when an ActiveRecord object does not have the required database field on it
71
84
  class DJSerializationError < ApartmentError; end
72
-
85
+
73
86
  end
@@ -1,81 +1,81 @@
1
1
  require 'active_record'
2
2
 
3
3
  module Apartment
4
-
4
+
5
5
  module Adapters
6
-
6
+
7
7
  class AbstractAdapter
8
-
8
+
9
9
  # @constructor
10
10
  # @param {Hash} config Database config
11
11
  # @param {Hash} defaults Some default options
12
- #
12
+ #
13
13
  def initialize(config, defaults = {})
14
14
  @config = config
15
15
  @defaults = defaults
16
16
  end
17
-
17
+
18
18
  # Create a new database, import schema, seed if appropriate
19
- #
19
+ #
20
20
  # @param {String} database Database name
21
- #
21
+ #
22
22
  def create(database)
23
23
  create_database(database)
24
24
 
25
25
  process(database) do
26
- import_database_schema
26
+ import_database_schema if Apartment.load_schema
27
27
 
28
28
  # Seed data if appropriate
29
29
  seed_data if Apartment.seed_after_create
30
-
30
+
31
31
  yield if block_given?
32
32
  end
33
33
  end
34
-
34
+
35
35
  # Get the current database name
36
- #
36
+ #
37
37
  # @return {String} current database name
38
- #
38
+ #
39
39
  def current_database
40
40
  ActiveRecord::Base.connection.current_database
41
41
  end
42
-
42
+
43
43
  # Drop the database
44
- #
44
+ #
45
45
  # @param {String} database Database name
46
- #
46
+ #
47
47
  def drop(database)
48
48
  # ActiveRecord::Base.connection.drop_database note that drop_database will not throw an exception, so manually execute
49
49
  ActiveRecord::Base.connection.execute("DROP DATABASE #{environmentify(database)}" )
50
-
51
- rescue ActiveRecord::StatementInvalid => e
50
+
51
+ rescue ActiveRecord::StatementInvalid
52
52
  raise DatabaseNotFound, "The database #{environmentify(database)} cannot be found"
53
53
  end
54
-
54
+
55
55
  # Prepend the environment if configured and the environment isn't already there
56
- #
56
+ #
57
57
  # @param {String} database Database name
58
58
  # @return {String} database name with Rails environment *optionally* prepended
59
- #
59
+ #
60
60
  def environmentify(database)
61
61
  Apartment.prepend_environment && !database.include?(Rails.env) ? "#{Rails.env}_#{database}" : database
62
- end
63
-
62
+ end
63
+
64
64
  # Connect to db, do your biz, switch back to previous db
65
- #
65
+ #
66
66
  # @param {String?} database Database or schema to connect to
67
- #
67
+ #
68
68
  def process(database = nil)
69
69
  current_db = current_database
70
70
  switch(database)
71
71
  yield if block_given?
72
-
73
- ensure
72
+
73
+ ensure
74
74
  switch(current_db) rescue reset
75
- end
76
-
75
+ end
76
+
77
77
  # Establish a new connection for each specific excluded model
78
- #
78
+ #
79
79
  def process_excluded_models
80
80
  # All other models will shared a connection (at ActiveRecord::Base) and we can modify at will
81
81
  Apartment.excluded_models.each do |excluded_model|
@@ -85,77 +85,77 @@ module Apartment
85
85
  warn "[Deprecation Warning] Passing class references to excluded models is now deprecated, please use a string instead"
86
86
  excluded_model = excluded_model.name
87
87
  end
88
-
88
+
89
89
  excluded_model.constantize.establish_connection @config
90
90
  end
91
91
  end
92
-
92
+
93
93
  # Reset the database connection to the default
94
- #
94
+ #
95
95
  def reset
96
96
  ActiveRecord::Base.establish_connection @config
97
97
  end
98
-
98
+
99
99
  # Switch to new connection (or schema if appopriate)
100
- #
100
+ #
101
101
  # @param {String} database Database name
102
- #
102
+ #
103
103
  def switch(database = nil)
104
104
  # Just connect to default db and return
105
105
  return reset if database.nil?
106
106
 
107
107
  connect_to_new(database)
108
- end
108
+ end
109
109
 
110
110
  # Load the rails seed file into the db
111
- #
112
- def seed_data
111
+ #
112
+ def seed_data
113
113
  silence_stream(STDOUT){ load_or_abort("#{Rails.root}/db/seeds.rb") } # Don't log the output of seeding the db
114
114
  end
115
115
  alias_method :seed, :seed_data
116
-
116
+
117
117
  protected
118
-
118
+
119
119
  # Create the database
120
- #
120
+ #
121
121
  # @param {String} database Database name
122
- #
122
+ #
123
123
  def create_database(database)
124
124
  ActiveRecord::Base.connection.create_database( environmentify(database) )
125
125
 
126
- rescue ActiveRecord::StatementInvalid => e
126
+ rescue ActiveRecord::StatementInvalid
127
127
  raise DatabaseExists, "The database #{environmentify(database)} already exists."
128
128
  end
129
-
129
+
130
130
  # Connect to new database
131
- #
131
+ #
132
132
  # @param {String} database Database name
133
- #
133
+ #
134
134
  def connect_to_new(database)
135
135
  ActiveRecord::Base.establish_connection multi_tenantify(database)
136
136
  ActiveRecord::Base.connection.active? # call active? to manually check if this connection is valid
137
137
 
138
- rescue ActiveRecord::StatementInvalid => e
138
+ rescue ActiveRecord::StatementInvalid
139
139
  raise DatabaseNotFound, "The database #{environmentify(database)} cannot be found."
140
- end
141
-
140
+ end
141
+
142
142
  # Import the database schema
143
- #
143
+ #
144
144
  def import_database_schema
145
145
  ActiveRecord::Schema.verbose = false # do not log schema load output.
146
146
  load_or_abort("#{Rails.root}/db/schema.rb")
147
147
  end
148
-
148
+
149
149
  # Return a new config that is multi-tenanted
150
- #
150
+ #
151
151
  def multi_tenantify(database)
152
152
  @config.clone.tap do |config|
153
153
  config[:database] = environmentify(database)
154
154
  end
155
- end
156
-
155
+ end
156
+
157
157
  # Load a file or abort if it doesn't exists
158
- #
158
+ #
159
159
  def load_or_abort(file)
160
160
  if File.exists?(file)
161
161
  load(file)
@@ -163,14 +163,14 @@ module Apartment
163
163
  abort %{#{file} doesn't exist yet}
164
164
  end
165
165
  end
166
-
166
+
167
167
  # Remove all non-alphanumeric characters
168
- #
169
- def sanitize(database)
168
+ #
169
+ def sanitize(database)
170
170
  warn "[Deprecation Warning] Sanitize is no longer used, client should ensure proper database names"
171
171
  database.gsub(/[\W]/,'')
172
172
  end
173
-
173
+
174
174
  end
175
175
  end
176
176
  end