activerecord-connections 0.0.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/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in activerecord-connections.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem 'activerecord', '3.1.0.rc6'
8
+ gem 'activesupport', '3.1.0.rc6'
9
+ end
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Gabriel Sobrinho
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.
data/README.markdown ADDED
@@ -0,0 +1,66 @@
1
+ # ActiveRecord::Connections - Easy multiple databases for ActiveRecord
2
+
3
+ ActiveRecord::Connections provides a new way to manage multi-tenant applications based on multiples databases.
4
+
5
+ ## Install
6
+
7
+ Install the activerecord-connections gem:
8
+
9
+ gem install activerecord-connections
10
+
11
+ Add this line to Gemfile:
12
+
13
+ gem 'activerecord-connections'
14
+
15
+ Runs a bundle install:
16
+
17
+ bundle install
18
+
19
+ ## Usage
20
+
21
+ ActiveRecord::Connections add this syntax to ActiveRecord::Base:
22
+
23
+ ActiveRecord::Base.using_connection(connection_name, connection_spec) do
24
+ # Use database connection inside this block
25
+ end
26
+
27
+ If you are using Rails, you could use this way:
28
+
29
+ class ApplicationController < ActionController::Base
30
+ around_filter :handle_customer
31
+
32
+ protected
33
+
34
+ def handle_customer(&block)
35
+ customer = Customer.find_by_domain!(request.domain)
36
+ customer.using_connection(&block)
37
+ end
38
+ end
39
+
40
+ class Customer < ActiveRecord::Base
41
+ serialize :connection_spec
42
+
43
+ def using_connection(&block)
44
+ ActiveRecord::Base.using_connection(id, connection_spec, &block)
45
+ end
46
+ end
47
+
48
+ ### Known issues
49
+
50
+ * Do not work with multi-threads
51
+ * Do not manage activerecord migrations for different databases
52
+ * Similar objects of different connections do not differ
53
+ * Coverage is not the best of the world (you could help easily)
54
+ * You will lost the connection if you call using_connection nested
55
+
56
+ ### Sharding and replication
57
+
58
+ Need sharding or replication? Check out one of these bad boys:
59
+
60
+ * [ar-octopus](https://github.com/tchandy/octopus)
61
+ * [db-charmer](https://github.com/kovyrin/db-charmer)
62
+ * [data-fabric](https://github.com/mperham/data_fabric)
63
+
64
+ ## Copyright
65
+
66
+ Copyright (c) 2011 Gabriel Sobrinho, released under the MIT license.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << 'test'
6
+ t.pattern = 'test/**/*_test.rb'
7
+ end
8
+
9
+ task :default => :test
@@ -0,0 +1,22 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "activerecord-connections"
3
+ s.version = "0.0.1"
4
+ s.authors = ["Gabriel Sobrinho"]
5
+ s.email = ["gabriel.sobrinho@gmail.com"]
6
+ s.homepage = "https://github.com/sobrinho/activerecord-connections"
7
+ s.summary = %q{A new way to manage multi-tenant applications based on multiples databases}
8
+ s.description = %q{A new way to manage multi-tenant applications based on multiples databases}
9
+
10
+ s.files = `git ls-files`.split("\n")
11
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
12
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
13
+ s.require_paths = ["lib"]
14
+
15
+ s.add_dependency 'activesupport', '>= 3.0'
16
+ s.add_dependency 'activerecord', '>= 3.0'
17
+
18
+ s.add_development_dependency 'rake', '>= 0.8.7'
19
+ s.add_development_dependency 'minitest', '>= 2.3.1'
20
+ s.add_development_dependency 'minitest-colorize', '>= 0.0.4'
21
+ s.add_development_dependency 'sqlite3', '>= 1.3.4'
22
+ end
@@ -0,0 +1,64 @@
1
+ require 'active_support/concern'
2
+
3
+ module ActiveRecord
4
+ module Connections
5
+ autoload :ConnectionFactory, 'active_record/connections/connection_factory'
6
+
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ cattr_accessor :proxy_connection
11
+ end
12
+
13
+ module ClassMethods
14
+ # Using on ApplicationController:
15
+ #
16
+ # class ApplicationController < ActionController::Base
17
+ # before_filter :handle_customer
18
+ #
19
+ # protected
20
+ #
21
+ # def handle_customer(&block)
22
+ # customer = Customer.find_by_domain!(request.domain)
23
+ # ActiveRecord::Base.using_connection(customer.id, customer.connection_spec, &block)
24
+ # end
25
+ # end
26
+ #
27
+ # Using directly on models:
28
+ #
29
+ # customer = Customer.first
30
+ #
31
+ # ActiveRecord::Base.using_connection(customer.id, customer.connection_spec) do
32
+ # User.count # => 3
33
+ # end
34
+ #
35
+ def using_connection(connection_name, connection_spec)
36
+ self.proxy_connection = ConnectionFactory.establish_connection(connection_name, connection_spec)
37
+
38
+ def self.connection_pool
39
+ connection_handler.retrieve_connection_pool(proxy_connection)
40
+ end
41
+
42
+ def self.retrieve_connection
43
+ connection_handler.retrieve_connection(proxy_connection)
44
+ end
45
+
46
+ yield
47
+ ensure
48
+ self.proxy_connection = nil
49
+
50
+ def self.connection_pool
51
+ connection_handler.retrieve_connection_pool(self)
52
+ end
53
+
54
+ def self.retrieve_connection
55
+ connection_handler.retrieve_connection(self)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ ActiveSupport.on_load(:active_record) do
63
+ include ActiveRecord::Connections
64
+ end
@@ -0,0 +1,43 @@
1
+ #
2
+ # This class is used to automatically generate small abstract ActiveRecord classes
3
+ # that would then be used as a source of database connections for ActiveRecord::Connections.
4
+ # This way we do not need to re-implement all the connection establishing code
5
+ # that ActiveRecord already has and we make our code less dependant on Rails versions.
6
+ #
7
+ module ActiveRecord
8
+ module Connections
9
+ class ConnectionFactory
10
+ cattr_accessor :connection_classes
11
+ self.connection_classes = {}
12
+
13
+ # Establishes connection or return an existing one from cache
14
+ def self.establish_connection(connection_name, connection_spec)
15
+ connection_classes[connection_name] ||= make_connection_klass(connection_name, connection_spec)
16
+ end
17
+
18
+ protected
19
+
20
+ # Generate an abstract AR class with specified connection established
21
+ def self.make_connection_klass(connection_name, connection_spec)
22
+ # Generate class
23
+ klass = generate_connection_klass(connection_name)
24
+
25
+ # Establish connection
26
+ klass.establish_connection(connection_spec)
27
+
28
+ # Return the class
29
+ return klass
30
+ end
31
+
32
+ def self.generate_connection_klass(connection_name)
33
+ class_eval <<-RUBY
34
+ class AbstractConnection#{connection_name} < ActiveRecord::Base
35
+ self.abstract_class = true
36
+
37
+ self
38
+ end
39
+ RUBY
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1 @@
1
+ require 'active_record/connections'
@@ -0,0 +1,76 @@
1
+ require 'test_helper'
2
+ require 'active_record'
3
+ require 'active_record/connections'
4
+ require 'support/customer'
5
+ require 'support/contact'
6
+
7
+ class ActiveRecord::ConnectionsTest < MiniTest::Unit::TestCase
8
+ MIGRATIONS_PATH = File.expand_path('../../db/migrate', __FILE__)
9
+
10
+ def setup
11
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
12
+
13
+ ActiveRecord::Migration.verbose = false
14
+ ActiveRecord::Migrator.migrate(MIGRATIONS_PATH)
15
+
16
+ @customer_1 = Customer.create!(:name => 'Customer 1')
17
+ @customer_2 = Customer.create!(:name => 'Customer 2')
18
+ @customer_3 = Customer.create!(:name => 'Customer 3')
19
+
20
+ self.each_customer do
21
+ ActiveRecord::Migrator.migrate(MIGRATIONS_PATH)
22
+ end
23
+
24
+ self.each_customer do |customer|
25
+ Contact.create!(:name => "Gabriel Sobrinho")
26
+ end
27
+ end
28
+
29
+ def teardown
30
+ self.each_customer do
31
+ Contact.destroy_all
32
+ end
33
+
34
+ Customer.destroy_all
35
+ end
36
+
37
+ def test_count
38
+ self.each_customer do |customer|
39
+ assert_equal 1, Contact.count, "#{customer.name} should have one contact"
40
+ end
41
+ end
42
+
43
+ def test_create
44
+ self.each_customer do |customer|
45
+ assert Contact.first, "#{customer.name} should have Gabriel Sobrinho as contact"
46
+ end
47
+ end
48
+
49
+ def test_update
50
+ self.each_customer do |customer|
51
+ Contact.update_all(:name => customer.name)
52
+ end
53
+
54
+ self.each_customer do |customer|
55
+ assert_equal customer.name, Contact.first.name, "#{customer.name} should have itself as contact"
56
+ end
57
+ end
58
+
59
+ def test_destroy
60
+ self.each_customer do
61
+ Contact.destroy_all
62
+ end
63
+
64
+ self.each_customer do |customer|
65
+ assert_equal 0, Contact.count, "#{customer.name} should have no contacts"
66
+ end
67
+ end
68
+
69
+ protected
70
+
71
+ def each_customer(&block)
72
+ Customer.find_each do |customer|
73
+ customer.using_connection(&block)
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,7 @@
1
+ class CreateCustomers < ActiveRecord::Migration
2
+ def change
3
+ create_table :customers do |t|
4
+ t.string :name
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class CreateContacts < ActiveRecord::Migration
2
+ def change
3
+ create_table :contacts do |t|
4
+ t.string :name
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,2 @@
1
+ class Contact < ActiveRecord::Base
2
+ end
@@ -0,0 +1,11 @@
1
+ class Customer < ActiveRecord::Base
2
+ def using_connection
3
+ ActiveRecord::Base.using_connection(id, database_spec) do
4
+ yield self
5
+ end
6
+ end
7
+
8
+ def database_spec
9
+ { :adapter => 'sqlite3', :database => ':memory:' }
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ require 'bundler/setup'
2
+ require 'minitest/autorun'
3
+ require 'minitest/colorize'
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-connections
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Gabriel Sobrinho
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-08-22 00:00:00 -03:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activesupport
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "3.0"
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: "3.0"
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: rake
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 0.8.7
47
+ type: :development
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: minitest
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: 2.3.1
58
+ type: :development
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: minitest-colorize
62
+ prerelease: false
63
+ requirement: &id005 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 0.0.4
69
+ type: :development
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: sqlite3
73
+ prerelease: false
74
+ requirement: &id006 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: 1.3.4
80
+ type: :development
81
+ version_requirements: *id006
82
+ description: A new way to manage multi-tenant applications based on multiples databases
83
+ email:
84
+ - gabriel.sobrinho@gmail.com
85
+ executables: []
86
+
87
+ extensions: []
88
+
89
+ extra_rdoc_files: []
90
+
91
+ files:
92
+ - .gitignore
93
+ - Gemfile
94
+ - MIT-LICENSE
95
+ - README.markdown
96
+ - Rakefile
97
+ - activerecord-connections.gemspec
98
+ - lib/active_record/connections.rb
99
+ - lib/active_record/connections/connection_factory.rb
100
+ - lib/activerecord-connections.rb
101
+ - test/active_record/connections_test.rb
102
+ - test/db/migrate/20110822114329_create_customers.rb
103
+ - test/db/migrate/20110822114413_create_contacts.rb
104
+ - test/support/contact.rb
105
+ - test/support/customer.rb
106
+ - test/test_helper.rb
107
+ has_rdoc: true
108
+ homepage: https://github.com/sobrinho/activerecord-connections
109
+ licenses: []
110
+
111
+ post_install_message:
112
+ rdoc_options: []
113
+
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: "0"
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: "0"
128
+ requirements: []
129
+
130
+ rubyforge_project:
131
+ rubygems_version: 1.5.2
132
+ signing_key:
133
+ specification_version: 3
134
+ summary: A new way to manage multi-tenant applications based on multiples databases
135
+ test_files:
136
+ - test/active_record/connections_test.rb
137
+ - test/db/migrate/20110822114329_create_customers.rb
138
+ - test/db/migrate/20110822114413_create_contacts.rb
139
+ - test/support/contact.rb
140
+ - test/support/customer.rb
141
+ - test/test_helper.rb