octoshark 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8a42c9187a7bf3516c5462a53b9d66b66571ddce
4
+ data.tar.gz: 6b5a5c331d7f899972277a4badba921b26adf61b
5
+ SHA512:
6
+ metadata.gz: 9916b8edd7ebb3a0c906f7a71434881fcf41ba9f8e483bb389bfdd07dec5b60d96e278ca10a31d6fe223de9c845c9ecae1bb25701ba9814a484d0d4fe5ecd41d
7
+ data.tar.gz: a7e6be40e45b850c0f73933d372bb64a6ffcc2ea6ab74271375f379beb7732300abc1a855f8d508cb21685324b521e7c3bd435a4bc849e9abbe74cae1529a193
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,8 @@
1
+ rvm:
2
+ - 2.1.0
3
+ cache:
4
+ - bundler
5
+ notifications:
6
+ email:
7
+ - dalibor.nasevic@gmail.com
8
+ script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in octoshark.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Dalibor Nasevic
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,129 @@
1
+ ![Travis status](https://travis-ci.org/dalibor/octoshark.png)
2
+
3
+ # Octoshark
4
+
5
+ Octoshark is a simple ActiveRecord connection switcher. It provides a general purpose connection switching that can be used in a multi-database systems such as sharded environments in your Rails application. It **does not** monkey-patch any `ActiveRecord::Base` methods and requires to specify which ActiveRecord models will use the Octoshark connection.
6
+
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```
13
+ gem 'octoshark'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ ```
19
+ $ bundle
20
+ ```
21
+
22
+ Or install it yourself as:
23
+
24
+ ```
25
+ $ gem install octoshark
26
+ ```
27
+
28
+
29
+ ## Usage
30
+
31
+ Specify the connections for Octoshark to manage. This is usually done in an app initializer.
32
+
33
+ ```ruby
34
+ Octoshark.setup({
35
+ db1: { adapter: "sqlite3", database: "db/db1.sqlite" },
36
+ db2: { adapter: "sqlite3", database: "db/db2.sqlite" }
37
+ })
38
+ ```
39
+
40
+ Configure which ActiveRecord models will use the Octoshark connection by overriding the Model.connection method. If there are many models, extract it in a module and include it.
41
+
42
+ ```ruby
43
+ class Post < ActiveRecord::Base
44
+ def self.connection
45
+ Octoshark.current_connection
46
+ end
47
+ end
48
+ ```
49
+
50
+ To use a specific database connection, do:
51
+
52
+ ```ruby
53
+ Octoshark.with_connection(:db1) do
54
+ # work with db1
55
+ Post.first
56
+ end
57
+ ```
58
+
59
+ Octoshark connection is changed for the duration of that block and then reversed back to the previous connection. Multiple connection switch blocks can be nested:
60
+
61
+ ```ruby
62
+ Octoshark.with_connection(:db1) do
63
+ # work with db1
64
+
65
+ Octoshark.with_connection(:db2) do
66
+ # work with db2
67
+ end
68
+
69
+ # work with db1
70
+ end
71
+ ```
72
+
73
+
74
+ ## Sharding Example
75
+
76
+ For example, let's say we have few models that are in the default Rails database (User, Account, etc) and few models that we want to shard (Blog, Post, Comment, etc). For all models in the default database, we can use the default ActiveRecord connection, and for all sharded models we need to the Octoshark connection.
77
+
78
+ We specify the connection for the sharded models based on the shard key (User) in a controller with an around filter:
79
+
80
+ ```ruby
81
+ # before_filter :find_user
82
+ around_filter :select_shard
83
+
84
+ def select_shard(&block)
85
+ Octoshark.with_connection(current_user.shard, &block)
86
+ end
87
+ ```
88
+
89
+ Similarly, in all other application entry-points that start with the default ActiveRecord connection (background jobs for an example), we need to switch the shard connection and then proceed.
90
+
91
+
92
+ ## Database Cleaner
93
+
94
+ Here's an example how to clean default and shard databases using both default connection and Octoshark connections:
95
+
96
+ ```ruby
97
+ config.before(:suite) do
98
+ clean_database_with(:truncation)
99
+ end
100
+
101
+ config.before(:each) do
102
+ DatabaseCleaner.start
103
+ end
104
+
105
+ config.after(:each) do
106
+ clean_database_with(:transaction)
107
+ end
108
+
109
+ def clean_database_with
110
+ # default ActiveRecord::Base connection pool
111
+ DatabaseCleaner[:active_record, {connection: ActiveRecord::Base.connection_pool}]
112
+
113
+ # Octoshark connection pool for the connection
114
+ Octoshark.connection_pools.each_pair do |name, connection_pool|
115
+ DatabaseCleaner[:active_record, {connection: connection_pool}]
116
+ end
117
+
118
+ DatabaseCleaner.clean_with(strategy)
119
+ end
120
+ ```
121
+
122
+
123
+ ## Contributing
124
+
125
+ 1. Fork it ( http://github.com/dalibor/octoshark/fork )
126
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
127
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
128
+ 4. Push to the branch (`git push origin my-new-feature`)
129
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,34 @@
1
+ require "octoshark/version"
2
+ require 'active_record'
3
+
4
+ module Octoshark
5
+ autoload :ConnectionSwitcher, 'octoshark/connection_switcher'
6
+
7
+ class NotConfiguredError < RuntimeError; end
8
+ class NoConnectionError < StandardError; end;
9
+ class NoCurrentConnectionError < StandardError; end;
10
+
11
+ OCTOSHARK = :octoshark
12
+
13
+ class << self
14
+ delegate :current_connection, :with_connection, :connection,
15
+ :connection_pools, :find_connection_pool, to: :switcher
16
+ end
17
+
18
+ def self.setup(configs)
19
+ @switcher = ConnectionSwitcher.new(configs)
20
+ end
21
+
22
+ def self.reset!
23
+ @switcher = nil
24
+ Thread.current[OCTOSHARK] = nil
25
+ end
26
+
27
+ def self.switcher
28
+ if @switcher
29
+ @switcher
30
+ else
31
+ raise NotConfiguredError, "Octoshark is not setup. Setup connections with Octoshark.setup(configs)"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,52 @@
1
+ module Octoshark
2
+ class ConnectionSwitcher
3
+
4
+ attr_reader :connection_pools
5
+
6
+ def initialize(configs = {})
7
+ @default_pool = ActiveRecord::Base.connection_pool
8
+ @connection_pools = { default: @default_pool }.with_indifferent_access
9
+
10
+ configs.each_pair do |name, config|
11
+ spec = ActiveRecord::ConnectionAdapters::ConnectionSpecification.new(
12
+ config, "#{config[:adapter]}_connection"
13
+ )
14
+ @connection_pools[name] = ActiveRecord::ConnectionAdapters::ConnectionPool.new(spec)
15
+ end
16
+ end
17
+
18
+ def current_connection
19
+ Thread.current[OCTOSHARK] || raise(NoCurrentConnectionError, "No current connection.")
20
+ end
21
+
22
+ def connection(name)
23
+ Thread.current[OCTOSHARK] = find_connection_pool(name).connection
24
+ end
25
+
26
+ def with_connection(name, &block)
27
+ result = nil
28
+
29
+ find_connection_pool(name).with_connection do |connection|
30
+ previous_connection = Thread.current[OCTOSHARK]
31
+ Thread.current[OCTOSHARK] = connection
32
+ begin
33
+ result = yield(connection)
34
+ ensure
35
+ Thread.current[OCTOSHARK] = previous_connection
36
+ end
37
+ end
38
+
39
+ result
40
+ end
41
+
42
+ def find_connection_pool(name, &block)
43
+ connection_pool = @connection_pools[name]
44
+
45
+ if connection_pool
46
+ connection_pool
47
+ else
48
+ raise NoConnectionError, "No such database connection '#{name}'"
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,3 @@
1
+ module Octoshark
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'octoshark/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "octoshark"
8
+ spec.version = Octoshark::VERSION
9
+ spec.authors = ["Dalibor Nasevic"]
10
+ spec.email = ["dalibor.nasevic@gmail.com"]
11
+ spec.summary = %q{Octoshark is an ActiveRecord connection switcher}
12
+ spec.description = %q{Octoshark is a library for switching between multiple ActiveRecord connections}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "activerecord", ">= 4.0"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.5"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec", "~> 3.0.0"
26
+ spec.add_development_dependency "sqlite3", "~> 1.3.0"
27
+ end
@@ -0,0 +1,2 @@
1
+ adapter: sqlite3
2
+ database: tmp/default.sqlite
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+
3
+ describe Octoshark::ConnectionSwitcher do
4
+ let(:config) { {
5
+ db1: { adapter: "sqlite3", database: "tmp/db1.sqlite" },
6
+ db2: { adapter: "sqlite3", database: "tmp/db2.sqlite" }
7
+ } }
8
+
9
+ def current_database_name(connection)
10
+ connection.execute('PRAGMA database_list').first['file'].split('/').last
11
+ end
12
+
13
+ describe "#initialize" do
14
+ it "can initialize connection switcher with default connection" do
15
+ switcher = Octoshark::ConnectionSwitcher.new
16
+ conn = ActiveRecord::Base.connection
17
+
18
+ expect(switcher.connection_pools.length).to eq(1)
19
+ expect(switcher.connection_pools[:default]).to be_an_instance_of(ActiveRecord::ConnectionAdapters::ConnectionPool)
20
+ end
21
+
22
+ it "can initialize connection switcher with custom connections" do
23
+ switcher = Octoshark::ConnectionSwitcher.new(config)
24
+
25
+ expect(switcher.connection_pools.length).to eq(3)
26
+ expect(switcher.connection_pools[:default]).to be_an_instance_of(ActiveRecord::ConnectionAdapters::ConnectionPool)
27
+ expect(switcher.connection_pools[:db1]).to be_an_instance_of(ActiveRecord::ConnectionAdapters::ConnectionPool)
28
+ end
29
+ end
30
+
31
+ describe "#current_connection" do
32
+ it "returns last used connection as current one" do
33
+ switcher = Octoshark::ConnectionSwitcher.new(config)
34
+ connection = switcher.connection(:db1)
35
+
36
+ expect(switcher.current_connection).to eq(connection)
37
+ end
38
+
39
+ it "raises error when no current connection" do
40
+ switcher = Octoshark::ConnectionSwitcher.new
41
+
42
+ expect { switcher.current_connection }.to raise_error(Octoshark::NoCurrentConnectionError)
43
+ end
44
+
45
+ end
46
+
47
+ describe '#find_connection_pool' do
48
+ it "can find connection pool by name" do
49
+ switcher = Octoshark::ConnectionSwitcher.new(config)
50
+ expect(switcher.find_connection_pool(:db1)).to be_an_instance_of(ActiveRecord::ConnectionAdapters::ConnectionPool)
51
+ end
52
+
53
+ it "raises Octoshark::NoConnectionError when no pool with that name" do
54
+ switcher = Octoshark::ConnectionSwitcher.new({})
55
+ expect { switcher.find_connection_pool(:invalid) }.to raise_error(Octoshark::NoConnectionError)
56
+ end
57
+ end
58
+
59
+ describe "#connection" do
60
+ it "returns useful connection from the pool" do
61
+ switcher = Octoshark::ConnectionSwitcher.new(config)
62
+ connection = switcher.connection(:db1)
63
+ expect(current_database_name(connection)).to eq("db1.sqlite")
64
+ end
65
+
66
+ it "raises Octoshark::NoConnectionError" do
67
+ switcher = Octoshark::ConnectionSwitcher.new({})
68
+
69
+ expect { switcher.connection(:invalid) }.to raise_error(Octoshark::NoConnectionError)
70
+ end
71
+ end
72
+
73
+ describe '#with_connection' do
74
+ it "can select default connection" do
75
+ switcher = Octoshark::ConnectionSwitcher.new({})
76
+
77
+ switcher.with_connection(:default) do |connection|
78
+ expect(current_database_name(switcher.current_connection)).to eq("default.sqlite")
79
+ end
80
+ end
81
+
82
+ it "can use multiple connections" do
83
+ switcher = Octoshark::ConnectionSwitcher.new(config)
84
+
85
+ switcher.with_connection(:default) do |connection|
86
+ expect(current_database_name(switcher.current_connection)).to eq("default.sqlite")
87
+ end
88
+
89
+ switcher.with_connection(:db1) do |connection|
90
+ expect(current_database_name(switcher.current_connection)).to eq("db1.sqlite")
91
+ end
92
+
93
+ switcher.with_connection(:db2) do |connection|
94
+ expect(current_database_name(switcher.current_connection)).to eq("db2.sqlite")
95
+ end
96
+ end
97
+
98
+ it "can nest connection" do
99
+ switcher = Octoshark::ConnectionSwitcher.new(config)
100
+
101
+ switcher.with_connection(:db1) do |connection|
102
+ expect(current_database_name(switcher.current_connection)).to eq("db1.sqlite")
103
+
104
+ switcher.with_connection(:db2) do |connection|
105
+ expect(current_database_name(switcher.current_connection)).to eq("db2.sqlite")
106
+ end
107
+
108
+ expect(current_database_name(switcher.current_connection)).to eq("db1.sqlite")
109
+ end
110
+ end
111
+
112
+ it "raises Octoshark::NoConnectionError" do
113
+ switcher = Octoshark::ConnectionSwitcher.new({})
114
+
115
+ expect { switcher.with_connection(:invalid) }.to raise_error(Octoshark::NoConnectionError)
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Octoshark do
4
+
5
+ it "can setup Octoshark with default connection" do
6
+ Octoshark.setup({})
7
+ expect(Octoshark.switcher).to_not be_nil
8
+ end
9
+
10
+ it "raises NotConfiguredError exception when not configured" do
11
+ Octoshark.setup({})
12
+ Octoshark.reset!
13
+ expect { Octoshark.switcher }.to raise_error(Octoshark::NotConfiguredError)
14
+ end
15
+
16
+ [
17
+ :current_connection, :with_connection, :connection,
18
+ :connection_pools, :find_connection_pool
19
+ ].each do |method_name|
20
+ it "delegates #{method_name} to connection switcher" do
21
+ Octoshark.setup({})
22
+ expect(Octoshark.switcher).to receive(method_name)
23
+
24
+ Octoshark.send(method_name)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,8 @@
1
+ require 'octoshark'
2
+
3
+ RSpec.configure do |config|
4
+ config.before :each do
5
+ ActiveRecord::Base.establish_connection(YAML.load(File.read('spec/config/database.yml')))
6
+ Octoshark.reset!
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: octoshark
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Dalibor Nasevic
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 3.0.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.0.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.3.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.3.0
83
+ description: Octoshark is a library for switching between multiple ActiveRecord connections
84
+ email:
85
+ - dalibor.nasevic@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - lib/octoshark.rb
98
+ - lib/octoshark/connection_switcher.rb
99
+ - lib/octoshark/version.rb
100
+ - octoshark.gemspec
101
+ - spec/config/database.yml
102
+ - spec/octoshark/connection_switcher_spec.rb
103
+ - spec/octoshark_spec.rb
104
+ - spec/spec_helper.rb
105
+ homepage: ''
106
+ licenses:
107
+ - MIT
108
+ metadata: {}
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 2.2.0
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: Octoshark is an ActiveRecord connection switcher
129
+ test_files:
130
+ - spec/config/database.yml
131
+ - spec/octoshark/connection_switcher_spec.rb
132
+ - spec/octoshark_spec.rb
133
+ - spec/spec_helper.rb
134
+ has_rdoc: