vpd 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "http://rubygems.org"
2
+
3
+
4
+ #gem 'rails', '~> 3.0.7'
5
+ gem 'activesupport', '~> 3.0.7'
6
+ gem 'activerecord', '~> 3.0.7'
7
+ gem 'pg', '~> 0.11.0'
8
+
9
+ # Add dependencies to develop your gem here.
10
+ # Include everything needed to run rake, tests, features, etc.
11
+ group :development do
12
+ gem "shoulda", ">= 0"
13
+ gem "bundler", "~> 1.0.0"
14
+ gem "jeweler", "~> 1.5.2"
15
+ gem "rcov", ">= 0"
16
+ end
@@ -0,0 +1,38 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activemodel (3.0.7)
5
+ activesupport (= 3.0.7)
6
+ builder (~> 2.1.2)
7
+ i18n (~> 0.5.0)
8
+ activerecord (3.0.7)
9
+ activemodel (= 3.0.7)
10
+ activesupport (= 3.0.7)
11
+ arel (~> 2.0.2)
12
+ tzinfo (~> 0.3.23)
13
+ activesupport (3.0.7)
14
+ arel (2.0.9)
15
+ builder (2.1.2)
16
+ git (1.2.5)
17
+ i18n (0.5.0)
18
+ jeweler (1.5.2)
19
+ bundler (~> 1.0.0)
20
+ git (>= 1.2.5)
21
+ rake
22
+ pg (0.11.0)
23
+ rake (0.8.7)
24
+ rcov (0.9.9)
25
+ shoulda (2.11.3)
26
+ tzinfo (0.3.26)
27
+
28
+ PLATFORMS
29
+ ruby
30
+
31
+ DEPENDENCIES
32
+ activerecord (~> 3.0.7)
33
+ activesupport (~> 3.0.7)
34
+ bundler (~> 1.0.0)
35
+ jeweler (~> 1.5.2)
36
+ pg (~> 0.11.0)
37
+ rcov
38
+ shoulda
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Paul Gallagher
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,76 @@
1
+ = vpd
2
+
3
+ Virtual Private Database for Rails
4
+
5
+ This is a quick gemification of the schema partitioning solution for multi-tenancy support in Rails.
6
+ It is EXPERIMENTAL and currently specific to PostgreSQL.
7
+
8
+ It requires Rails 3.0.7+ and pg 0.11.0+.
9
+
10
+ The schema partitioning approach is described by Guy Naor in his definitive presentation:
11
+
12
+ == What it does
13
+
14
+ * patches the postgresql adapter so it is possible to run your rails migrations into a specific schema
15
+ * adds helper methods for activating a selected schema
16
+
17
+ == How to use it
18
+
19
+ To install, add to your Gemfile:
20
+
21
+ gem 'vpd'
22
+
23
+ and run bundle install.
24
+
25
+ Once installed, it provides a number of methods to setup and switch between schemas.
26
+
27
+ === Vpd.activate(schema,verify_migration = false)
28
+
29
+ This is the main method to switch to a specified schema.
30
+ If you pass true for verify_migration, it will create the schema if necessary
31
+ and make sure your rails migrations (in that schema) are up-to-date.
32
+
33
+ How do you know which schema to switch to? Well, that's up to you .. could be
34
+ on the basis of the request subdomain, or the an attribute of the current_user.
35
+
36
+ e.g. we might do this in a before_filter of our ApplicationController...
37
+
38
+ # this may evaluate to 'my_company' for example ..
39
+ schema_to_use = request.subdomains.last
40
+ if schema_to_use.present?
41
+ # now activate the schema
42
+ Vpd.activate(schema_to_use,true)
43
+ else
44
+ # ensure we are running with the default
45
+ Vpd.activate_default
46
+ end
47
+
48
+ === Vpd.activate_default
49
+ Returns to default schema search path. This will either be '"$user",public' (postgres default)
50
+ or the schema_search_path setting from your database.yml
51
+
52
+ === and a bunch of other methods #nodoc
53
+
54
+
55
+ == What it doesn't do / things to do
56
+
57
+ * Provide some syntactic sugar to allow migrations to be selectively included/excluded
58
+ * Genericise the approach to support other db backends
59
+ * Useful tests to ensure this isn't breaking soemthing in Rails that I haven't noticed yet
60
+ * Find a way to do this without monkey-patching
61
+ * Better documentation
62
+
63
+ == Contributing to vpd
64
+
65
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
66
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
67
+ * Fork the project
68
+ * Start a feature/bugfix branch
69
+ * Commit and push until you are happy with your contribution
70
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
71
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
72
+
73
+ == Copyright
74
+
75
+ Copyright (c) 2011 Paul Gallagher. See LICENSE for further details.
76
+
@@ -0,0 +1,61 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+ require 'rake'
12
+ $LOAD_PATH << './lib'
13
+ require 'vpd'
14
+
15
+ begin
16
+ require 'jeweler'
17
+ Jeweler::Tasks.new do |gem|
18
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
19
+ gem.name = %q{vpd}
20
+ gem.version = Vpd::VERSION
21
+ gem.homepage = %q{http://github.com/tardate/vpd}
22
+ gem.license = "MIT"
23
+ gem.summary = %Q{virtual private database for rails}
24
+ gem.description = %Q{implements schema partitioning for multi-tenancy support in rails.
25
+ Currently only supports PostgreSQL and is officially "experimental"!}
26
+ gem.email = "paul@evendis.com"
27
+ gem.authors = ["Paul Gallagher"]
28
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
29
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
30
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
31
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
32
+ end
33
+ Jeweler::RubygemsDotOrgTasks.new
34
+ rescue LoadError
35
+ puts "Jeweler not available. Install it with: sudo gem install jeweler"
36
+ end
37
+
38
+ require 'rake/testtask'
39
+ Rake::TestTask.new(:test) do |test|
40
+ test.libs << 'lib' << 'test'
41
+ test.pattern = 'test/**/test_*.rb'
42
+ test.verbose = true
43
+ end
44
+
45
+ require 'rcov/rcovtask'
46
+ Rcov::RcovTask.new do |test|
47
+ test.libs << 'test'
48
+ test.pattern = 'test/**/test_*.rb'
49
+ test.verbose = true
50
+ end
51
+
52
+ task :default => :test
53
+
54
+ require 'rake/rdoctask'
55
+ Rake::RDocTask.new do |rdoc|
56
+
57
+ rdoc.rdoc_dir = 'rdoc'
58
+ rdoc.title = "vpd #{Vpd::VERSION}"
59
+ rdoc.rdoc_files.include('README*')
60
+ rdoc.rdoc_files.include('lib/**/*.rb')
61
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'vpd'
@@ -0,0 +1,3 @@
1
+ require 'vpd/version'
2
+ require 'vpd/postgresql_adapter'
3
+ require 'vpd/base'
@@ -0,0 +1,42 @@
1
+ module Vpd
2
+
3
+ # record the initial schema_search_path
4
+ def self.initial_search_path
5
+ @@initial_search_path ||= YAML::load(File.open('config/database.yml'))[Rails.env]["schema_search_path"]
6
+ @@initial_search_path ||= '"$user",public'
7
+ end
8
+
9
+ # creates +schema+ and optionally runs rails migrations to it
10
+ def self.create(schema,and_migrate = true)
11
+ conn = ActiveRecord::Base.connection
12
+ conn.execute("CREATE SCHEMA #{schema}") unless conn.schema_exists? schema
13
+ self.migrate(schema) if and_migrate
14
+ end
15
+
16
+ # runs rails migrations into +schema+
17
+ # preserves the current schema search path
18
+ def self.migrate(schema, to_version = nil)
19
+ conn = ActiveRecord::Base.connection
20
+ return false unless conn.schema_exists? schema
21
+ current_search_path = conn.schema_search_path
22
+ conn.schema_search_path = schema
23
+ ActiveRecord::Migrator.migrate('db/migrate', to_version)
24
+ conn.schema_search_path = current_search_path
25
+ end
26
+
27
+ # prepends +schema+ to the schema search path
28
+ # if verify_migration is true, will create the schema if required
29
+ # and ensure that it is migrated up-to-date
30
+ def self.activate(schema,verify_migration = false)
31
+ base_path = self.initial_search_path
32
+ self.create(schema) if verify_migration
33
+ conn = ActiveRecord::Base.connection
34
+ return false unless conn.schema_exists?(schema)
35
+ conn.schema_search_path = [schema, base_path].compact.join(',')
36
+ end
37
+
38
+ def self.activate_default
39
+ ActiveRecord::Base.connection.schema_search_path = self.initial_search_path
40
+ end
41
+
42
+ end
@@ -0,0 +1,61 @@
1
+ require 'active_record'
2
+ require 'active_record/base'
3
+ require 'active_record/connection_adapters/postgresql_adapter'
4
+
5
+ module ActiveRecord
6
+ module ConnectionAdapters
7
+ class PostgreSQLAdapter
8
+
9
+ # get the current schema
10
+ def current_schema
11
+ query('SELECT current_schema', 'SCHEMA')[0][0]
12
+ end
13
+
14
+ # tests it +name+ schema exists
15
+ def schema_exists?(name)
16
+ query(<<-SQL).first[0].to_i > 0
17
+ SELECT COUNT(*)
18
+ FROM pg_namespace
19
+ WHERE nspname = '#{name.gsub(/(^"|"$)/,'')}'
20
+ SQL
21
+ end
22
+
23
+ # Overrides ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#table_exists?
24
+ # Change to only look in the current schema - unless schema is specified in the name
25
+ # This is required to enable rails migrations to run into the currently selected schema
26
+ def table_exists?(name)
27
+ schema, table = extract_schema_and_table(name.to_s)
28
+ schema ||= current_schema
29
+
30
+ binds = [[nil, table.gsub(/(^"|"$)/,'')]]
31
+ binds << [nil, schema] if schema
32
+
33
+ query(<<-SQL, 'SCHEMA').first[0].to_i > 0
34
+ SELECT COUNT(*)
35
+ FROM pg_tables
36
+ WHERE tablename = '#{table.gsub(/(^"|"$)/,'')}'
37
+ #{schema ? "AND schemaname = '#{schema}'" : ''}
38
+ SQL
39
+ end
40
+
41
+ # Extracts the table and schema name from +name+
42
+ # - copied from HEAD but not in 3.0.7 yet
43
+ def extract_schema_and_table(name)
44
+ schema, table = name.split('.', 2)
45
+
46
+ unless table # A table was provided without a schema
47
+ table = schema
48
+ schema = nil
49
+ end
50
+
51
+ if name =~ /^"/ # Handle quoted table names
52
+ table = name
53
+ schema = nil
54
+ end
55
+ [schema, table]
56
+ end
57
+
58
+ end
59
+ end
60
+ end
61
+
@@ -0,0 +1,3 @@
1
+ module Vpd
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,18 @@
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 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'vpd'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestVpd < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
@@ -0,0 +1,76 @@
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{vpd}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Paul Gallagher"]
12
+ s.date = %q{2011-04-22}
13
+ s.description = %q{implements schema partitioning for multi-tenancy support in rails.
14
+ Currently only supports PostgreSQL and is officially "experimental"!}
15
+ s.email = %q{paul@evendis.com}
16
+ s.extra_rdoc_files = [
17
+ "LICENSE",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "init.rb",
27
+ "lib/vpd.rb",
28
+ "lib/vpd/base.rb",
29
+ "lib/vpd/postgresql_adapter.rb",
30
+ "lib/vpd/version.rb",
31
+ "test/helper.rb",
32
+ "test/test_vpd.rb",
33
+ "vpd.gemspec"
34
+ ]
35
+ s.homepage = %q{http://github.com/tardate/vpd}
36
+ s.licenses = ["MIT"]
37
+ s.require_paths = ["lib"]
38
+ s.rubygems_version = %q{1.3.7}
39
+ s.summary = %q{virtual private database for rails}
40
+ s.test_files = [
41
+ "test/helper.rb",
42
+ "test/test_vpd.rb"
43
+ ]
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
50
+ s.add_runtime_dependency(%q<activesupport>, ["~> 3.0.7"])
51
+ s.add_runtime_dependency(%q<activerecord>, ["~> 3.0.7"])
52
+ s.add_runtime_dependency(%q<pg>, ["~> 0.11.0"])
53
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
54
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
55
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
56
+ s.add_development_dependency(%q<rcov>, [">= 0"])
57
+ else
58
+ s.add_dependency(%q<activesupport>, ["~> 3.0.7"])
59
+ s.add_dependency(%q<activerecord>, ["~> 3.0.7"])
60
+ s.add_dependency(%q<pg>, ["~> 0.11.0"])
61
+ s.add_dependency(%q<shoulda>, [">= 0"])
62
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
63
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
64
+ s.add_dependency(%q<rcov>, [">= 0"])
65
+ end
66
+ else
67
+ s.add_dependency(%q<activesupport>, ["~> 3.0.7"])
68
+ s.add_dependency(%q<activerecord>, ["~> 3.0.7"])
69
+ s.add_dependency(%q<pg>, ["~> 0.11.0"])
70
+ s.add_dependency(%q<shoulda>, [">= 0"])
71
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
72
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
73
+ s.add_dependency(%q<rcov>, [">= 0"])
74
+ end
75
+ end
76
+
metadata ADDED
@@ -0,0 +1,181 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vpd
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Paul Gallagher
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-04-22 00:00:00 +08: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
+ - 3
29
+ - 0
30
+ - 7
31
+ version: 3.0.7
32
+ type: :runtime
33
+ prerelease: false
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: activerecord
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ~>
41
+ - !ruby/object:Gem::Version
42
+ segments:
43
+ - 3
44
+ - 0
45
+ - 7
46
+ version: 3.0.7
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: pg
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ segments:
58
+ - 0
59
+ - 11
60
+ - 0
61
+ version: 0.11.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: shoulda
67
+ requirement: &id004 !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: *id004
78
+ - !ruby/object:Gem::Dependency
79
+ name: bundler
80
+ requirement: &id005 !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ segments:
86
+ - 1
87
+ - 0
88
+ - 0
89
+ version: 1.0.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: *id005
93
+ - !ruby/object:Gem::Dependency
94
+ name: jeweler
95
+ requirement: &id006 !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ~>
99
+ - !ruby/object:Gem::Version
100
+ segments:
101
+ - 1
102
+ - 5
103
+ - 2
104
+ version: 1.5.2
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: *id006
108
+ - !ruby/object:Gem::Dependency
109
+ name: rcov
110
+ requirement: &id007 !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ segments:
116
+ - 0
117
+ version: "0"
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: *id007
121
+ description: |-
122
+ implements schema partitioning for multi-tenancy support in rails.
123
+ Currently only supports PostgreSQL and is officially "experimental"!
124
+ email: paul@evendis.com
125
+ executables: []
126
+
127
+ extensions: []
128
+
129
+ extra_rdoc_files:
130
+ - LICENSE
131
+ - README.rdoc
132
+ files:
133
+ - Gemfile
134
+ - Gemfile.lock
135
+ - LICENSE
136
+ - README.rdoc
137
+ - Rakefile
138
+ - init.rb
139
+ - lib/vpd.rb
140
+ - lib/vpd/base.rb
141
+ - lib/vpd/postgresql_adapter.rb
142
+ - lib/vpd/version.rb
143
+ - test/helper.rb
144
+ - test/test_vpd.rb
145
+ - vpd.gemspec
146
+ has_rdoc: true
147
+ homepage: http://github.com/tardate/vpd
148
+ licenses:
149
+ - MIT
150
+ post_install_message:
151
+ rdoc_options: []
152
+
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ none: false
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ hash: 1832961237987341343
161
+ segments:
162
+ - 0
163
+ version: "0"
164
+ required_rubygems_version: !ruby/object:Gem::Requirement
165
+ none: false
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ segments:
170
+ - 0
171
+ version: "0"
172
+ requirements: []
173
+
174
+ rubyforge_project:
175
+ rubygems_version: 1.3.7
176
+ signing_key:
177
+ specification_version: 3
178
+ summary: virtual private database for rails
179
+ test_files:
180
+ - test/helper.rb
181
+ - test/test_vpd.rb