apartment 0.1.3

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.
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_DISABLE_SHARED_GEMS: "1"
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <projectDescription>
3
+ <name>apartment</name>
4
+ <comment></comment>
5
+ <projects>
6
+ </projects>
7
+ <buildSpec>
8
+ </buildSpec>
9
+ <natures>
10
+ </natures>
11
+ </projectDescription>
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+
5
+ gem "activesupport", ">= 2.3.5"
6
+
7
+
8
+ # Add dependencies to develop your gem here.
9
+ # Include everything needed to run rake, tests, features, etc.
10
+ group :development do
11
+ gem "shoulda", ">= 0"
12
+ gem "bundler", "~> 1.0.0"
13
+ gem "jeweler", "~> 1.5.1"
14
+ gem "rcov", ">= 0"
15
+ end
@@ -0,0 +1,22 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activesupport (3.0.3)
5
+ git (1.2.5)
6
+ jeweler (1.5.1)
7
+ bundler (~> 1.0.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ rake (0.8.7)
11
+ rcov (0.9.9)
12
+ shoulda (2.11.3)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ activesupport (>= 2.3.5)
19
+ bundler (~> 1.0.0)
20
+ jeweler (~> 1.5.1)
21
+ rcov
22
+ shoulda
@@ -0,0 +1,95 @@
1
+ # Apartment
2
+ *Multitenancy for Rails 3*
3
+
4
+ Apartment provides tools to help you deal with multiple databases in your Rails
5
+ environment. If you need to have certain data sequestered based on account or company,
6
+ but still allow some data to exist in a common database, Apartment can help.
7
+
8
+ ## Caveats
9
+
10
+ Apartment was built to deal with a very particular use-case - the need to spin up
11
+ multiple databases within the same application instance on-demand while Rails is running.
12
+ If your setup can accomodate creating new databases on deploy (by adding a new database to your
13
+ database.yml), or doesn't need 100% database isolation, other solutions might be far simpler
14
+ for your use case.
15
+
16
+ ## Installation
17
+
18
+ ### Rails 3
19
+
20
+ Add the following to your Gemfile:
21
+
22
+ gem 'apartment'
23
+
24
+ That's all you need to set up the Apartment libraries. If you want to switch databases
25
+ on a per-user basis, look under "Usage - Switching databases per request", below.
26
+
27
+ ## Usage
28
+
29
+ ### Creating new Databases
30
+
31
+ Before you can switch to a new apartment database, you will need to create it. Whenever
32
+ you need to create a new database, you can run the following command:
33
+
34
+ Apartment::Database.create('database_name')
35
+
36
+ Apartment will create a new database in the following format: "environment_database_name".
37
+ In the case of a sqlite database, this will be created in your 'db/migrate' foler. With
38
+ other databases, the database will be created as a new DB within the system.
39
+
40
+ When you create a new database, all migrations will be run against that database, so it will be
41
+ up to date when create returns.
42
+
43
+ #### Notes on PostgreSQL
44
+
45
+ PostgreSQL works slightly differently than other databases when creating a new DB. If you
46
+ are using PostgreSQL, Apartment will set up a new **schema** and migrate into there. This
47
+ provides better performance, and allows Apartment to work on systems like Heroku, which
48
+ would not allow a full new database to be created.
49
+
50
+ ### Switching Databases
51
+
52
+ To switch databases using Apartment, use the following command:
53
+
54
+ Apartment::Database.switch('database_name')
55
+
56
+ When switch is called, all requests coming to ActiveRecord will be routed to the database
57
+ you specify (with the exception of excluded models, see below). To return to the 'root'
58
+ database, call switch with no arguments.
59
+
60
+ ### Switching Databases per request
61
+
62
+ You can have Apartment route to the appropriate database per request by adding a warden task
63
+ in application.rb. For instance, if you want to route to a particular database depending on
64
+ user, and you have a database attribute on your user model, you could do the following:
65
+
66
+ Warden::Manager.on_request do |proxy|
67
+ if session[:user_id]
68
+ u = User.find(session[:user_id])
69
+ Apartment::Database.switch(u.database)
70
+ end
71
+ end
72
+
73
+ ### Excluding models
74
+
75
+ If you have some models that should always access the 'root' database, you can specify this in
76
+ a configuration file. Apartment will look for a config file called 'apartment.yml' in the /config
77
+ directory of your Rails application. To exclude certain models, do the following:
78
+
79
+ excluded_models: [User, Company]
80
+
81
+ ### Managing Migrations
82
+
83
+ Currently, you will need to migrate each database individually. I'll be working on code to
84
+ migrate all known databases soon. You can migrate any database up to the current version by
85
+ calling:
86
+
87
+ Apartment::Database.migrate('database_name')
88
+
89
+ ## TODO
90
+
91
+ * Migration support
92
+ * Other rake task support
93
+ * Cross-database associations
94
+
95
+ ## Contributing
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "apartment"
16
+ gem.homepage = "http://github.com/ryanbrunner/apartment"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{A Ruby gem for managing multitenancy in Rails applications}
19
+ gem.description = %Q{Apartment allows Rails applications to deal with
20
+ multitenancy.}
21
+ gem.email = "ryan@ryanbrunner.com"
22
+ gem.authors = ["Ryan Brunner"]
23
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
24
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
25
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
26
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
27
+ end
28
+ Jeweler::RubygemsDotOrgTasks.new
29
+
30
+ require 'rake/testtask'
31
+ Rake::TestTask.new(:test) do |test|
32
+ test.libs << 'lib' << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+
37
+ require 'rcov/rcovtask'
38
+ Rcov::RcovTask.new do |test|
39
+ test.libs << 'test'
40
+ test.pattern = 'test/**/test_*.rb'
41
+ test.verbose = true
42
+ end
43
+
44
+ task :default => :test
45
+
46
+ require 'rake/rdoctask'
47
+ Rake::RDocTask.new do |rdoc|
48
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
49
+
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = "apartment #{version}"
52
+ rdoc.rdoc_files.include('README*')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.3
@@ -0,0 +1,68 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{apartment}
8
+ s.version = "0.1.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Ryan Brunner"]
12
+ s.date = %q{2011-03-04}
13
+ s.description = %q{Apartment allows Rails applications to deal with
14
+ multitenancy.}
15
+ s.email = %q{ryan@ryanbrunner.com}
16
+ s.extra_rdoc_files = [
17
+ "README.markdown"
18
+ ]
19
+ s.files = [
20
+ ".bundle/config",
21
+ ".project",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "README.markdown",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "apartment.gemspec",
28
+ "lib/apartment.rb",
29
+ "lib/apartment/associations/multi_tenant_association.rb",
30
+ "lib/apartment/config.rb",
31
+ "lib/apartment/config/default_config.yml",
32
+ "lib/apartment/database.rb",
33
+ "lib/apartment/railtie.rb",
34
+ "lib/tasks/multi_tenant_migrate.rake",
35
+ "pkg/apartment-0.1.3.gem"
36
+ ]
37
+ s.homepage = %q{http://github.com/ryanbrunner/apartment}
38
+ s.licenses = ["MIT"]
39
+ s.require_paths = ["lib"]
40
+ s.rubygems_version = %q{1.3.7}
41
+ s.summary = %q{A Ruby gem for managing multitenancy in Rails applications}
42
+
43
+ if s.respond_to? :specification_version then
44
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
45
+ s.specification_version = 3
46
+
47
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
48
+ s.add_runtime_dependency(%q<activesupport>, [">= 3.0.5"])
49
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
50
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
51
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
52
+ s.add_development_dependency(%q<rcov>, [">= 0"])
53
+ else
54
+ s.add_dependency(%q<activesupport>, [">= 3.0.5"])
55
+ s.add_dependency(%q<shoulda>, [">= 0"])
56
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
57
+ s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
58
+ s.add_dependency(%q<rcov>, [">= 0"])
59
+ end
60
+ else
61
+ s.add_dependency(%q<activesupport>, [">= 3.0.5"])
62
+ s.add_dependency(%q<shoulda>, [">= 0"])
63
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
64
+ s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
65
+ s.add_dependency(%q<rcov>, [">= 0"])
66
+ end
67
+ end
68
+
@@ -0,0 +1,11 @@
1
+
2
+ module Apartment
3
+ require 'apartment/railtie'
4
+ require 'apartment/config'
5
+ require 'apartment/database'
6
+
7
+ def self.included(base)
8
+ base.extend Apartment::ClassMethods
9
+ end
10
+ end
11
+
@@ -0,0 +1,10 @@
1
+ module Apartment
2
+ config = {:excluded_models => ["User"],
3
+ :use_postgres_schemas => true
4
+ }
5
+
6
+ if File.exists? "config/apartment.yml"
7
+ config = config.merge YAML.load_file "config/apartment.yml"
8
+ Config = OpenStruct.new(config)
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ # Apartment configuration
2
+
3
+ # Excluded models
4
+ # Indicate which models should be excluded from multi-tenancy. If you have information
5
+ # that does not vary between databases, or information that should be considered global,
6
+ # specify it here. Any classes involved in determining which database to access MUST be
7
+ # specified here
8
+
9
+ excluded_models: [User]
10
+
11
+ # Postgres integration mode
12
+ # By default, apartment switches databases in Postgres by changing the schema search path
13
+ # of the current environment database. If preferred, this can be turned off and Postgres will
14
+ # look in different databases for multi-tenanted models.
15
+
16
+ use_postgres_schemas: true
17
+
@@ -0,0 +1,80 @@
1
+
2
+ module Apartment
3
+ class Database
4
+ def self.switch(database)
5
+
6
+ config = get_default_database
7
+
8
+ if database.nil?
9
+ ActiveRecord::Base.establish_connection(config)
10
+ return
11
+ end
12
+
13
+ switched_config = multi_tenantify(config, database)
14
+
15
+ puts switched_config.to_yaml
16
+
17
+ ActiveRecord::Base.establish_connection(switched_config)
18
+
19
+ puts Apartment::Config.excluded_models
20
+
21
+ Apartment::Config.excluded_models.each do |m|
22
+ klass = Kernel
23
+ m.split("::").each do |i|
24
+ klass = klass.const_get(i)
25
+ end
26
+
27
+ raise "Excluded class #{klass} could not be found." if klass.nil?
28
+
29
+ puts "Excluding class #{m}"
30
+
31
+ klass.establish_connection(config)
32
+ end
33
+ end
34
+
35
+ def self.create(database)
36
+ config = get_default_database
37
+
38
+ switched_config = multi_tenantify(config, database)
39
+
40
+ ActiveRecord::Base.establish_connection(switched_config)
41
+
42
+ if config["adapter"] == "postgresql"
43
+ ActiveRecord::Base.connection.execute('create table schema_migrations(version varchar(255))')
44
+ end
45
+
46
+ migrate(database)
47
+ end
48
+
49
+ def self.migrate(database)
50
+
51
+ config = get_default_database
52
+ switched_config = multi_tenantify(config, database)
53
+
54
+ ActiveRecord::Base.establish_connection(switched_config)
55
+
56
+
57
+ ActiveRecord::Migrator.migrate(File.join(Rails.root, 'db', 'migrate'))
58
+
59
+ ActiveRecord::Base.establish_connection(config)
60
+ end
61
+
62
+ protected
63
+ def self.get_default_database
64
+ Rails.configuration.database_configuration[Rails.env]
65
+ end
66
+
67
+ def self.multi_tenantify(configuration, database)
68
+ new_config = configuration.clone
69
+
70
+ if new_config['adapter'] == "postgresql"
71
+ new_config['schema_search_path'] = database
72
+ else
73
+ new_config['database'] = new_config['database'].gsub(Rails.env.to_s, "#{database}_#{Rails.env}")
74
+ end
75
+
76
+ new_config
77
+ end
78
+ end
79
+
80
+ end
@@ -0,0 +1,10 @@
1
+ require 'apartment'
2
+ require 'rails'
3
+
4
+ module Apartment
5
+ class Railtie < Rails::Railtie
6
+ rake_tasks do
7
+ load 'tasks/multi_tenant_migrate.rake'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ namespace :apartment do
2
+ desc "Apply database changes to all multi-tenant databases"
3
+ task :migrate => :environment do |t, args|
4
+
5
+ puts "[Migrating to default environment]"
6
+ ActiveRecord::Migrator.migrate(File.join(Rails.root, 'db', 'migrate'))
7
+
8
+ Admin::Company.where("database is not null").select("distinct database").each do |u|
9
+ puts "[Migrating to #{u.database}]"
10
+
11
+ Apartment::Database.migrate u.database
12
+ end
13
+ end
14
+ end
Binary file
metadata ADDED
@@ -0,0 +1,153 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: apartment
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 3
9
+ version: 0.1.3
10
+ platform: ruby
11
+ authors:
12
+ - Ryan Brunner
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-04-18 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activesupport
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 2
29
+ - 3
30
+ - 5
31
+ version: 2.3.5
32
+ type: :runtime
33
+ prerelease: false
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: shoulda
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ segments:
43
+ - 0
44
+ version: "0"
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: bundler
50
+ requirement: &id003 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 1
57
+ - 0
58
+ - 0
59
+ version: 1.0.0
60
+ type: :development
61
+ prerelease: false
62
+ version_requirements: *id003
63
+ - !ruby/object:Gem::Dependency
64
+ name: jeweler
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ~>
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 1
72
+ - 5
73
+ - 1
74
+ version: 1.5.1
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: *id004
78
+ - !ruby/object:Gem::Dependency
79
+ name: rcov
80
+ requirement: &id005 !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *id005
91
+ description: |-
92
+ Apartment allows Rails applications to deal with
93
+ multitenancy.
94
+ email: ryan@ryanbrunner.com
95
+ executables: []
96
+
97
+ extensions: []
98
+
99
+ extra_rdoc_files:
100
+ - README.markdown
101
+ files:
102
+ - .bundle/config
103
+ - .project
104
+ - Gemfile
105
+ - Gemfile.lock
106
+ - README.markdown
107
+ - Rakefile
108
+ - VERSION
109
+ - apartment-0.1.3.gem
110
+ - apartment.gemspec
111
+ - lib/apartment.rb
112
+ - lib/apartment/associations/multi_tenant_association.rb
113
+ - lib/apartment/config.rb
114
+ - lib/apartment/config/default_config.yml
115
+ - lib/apartment/database.rb
116
+ - lib/apartment/railtie.rb
117
+ - lib/tasks/multi_tenant_migrate.rake
118
+ - pkg/apartment-0.1.3.gem
119
+ has_rdoc: true
120
+ homepage: http://github.com/ryanbrunner/apartment
121
+ licenses:
122
+ - MIT
123
+ post_install_message:
124
+ rdoc_options: []
125
+
126
+ require_paths:
127
+ - lib
128
+ required_ruby_version: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ hash: -190181027694817836
134
+ segments:
135
+ - 0
136
+ version: "0"
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ segments:
143
+ - 0
144
+ version: "0"
145
+ requirements: []
146
+
147
+ rubyforge_project:
148
+ rubygems_version: 1.3.7
149
+ signing_key:
150
+ specification_version: 3
151
+ summary: A Ruby gem for managing multitenancy in Rails applications
152
+ test_files: []
153
+