apartment 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.bundle/config +2 -0
- data/.project +11 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +22 -0
- data/README.markdown +95 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/apartment.gemspec +68 -0
- data/lib/apartment.rb +11 -0
- data/lib/apartment/associations/multi_tenant_association.rb +0 -0
- data/lib/apartment/config.rb +10 -0
- data/lib/apartment/config/default_config.yml +17 -0
- data/lib/apartment/database.rb +80 -0
- data/lib/apartment/railtie.rb +10 -0
- data/lib/tasks/multi_tenant_migrate.rake +14 -0
- data/pkg/apartment-0.1.3.gem +0 -0
- metadata +153 -0
data/.bundle/config
ADDED
data/.project
ADDED
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
|
data/Gemfile.lock
ADDED
@@ -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
|
data/README.markdown
ADDED
@@ -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
|
data/Rakefile
ADDED
@@ -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
|
data/apartment.gemspec
ADDED
@@ -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
|
+
|
data/lib/apartment.rb
ADDED
File without changes
|
@@ -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,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
|
+
|