flipp 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ # flipp
2
+
3
+ To alleviate issues around highly divergent development databases on feature/refactor branches, flipp helps switch databases upon new branch checkouts.
4
+
5
+ # Database Support
6
+
7
+ Current support is for MySQL only.
8
+
9
+ # Installation & Usage
10
+
11
+ 1. Add `gem 'flipp'` to your Gemfile or do `gem install flipp`.
12
+ 2. Run `rake flipp:install` from your project's root directory to install the git hook
13
+ 3. Normal git workflow, ie. switch branches normally
14
+
15
+ # License
16
+
17
+ flipp is released under the MIT license
@@ -0,0 +1,2 @@
1
+ $:.unshift File.expand_path('..', __FILE__)
2
+ require 'tasks/flipp'
@@ -0,0 +1,24 @@
1
+ lib = File.expand_path('../lib/', __FILE__)
2
+ $:.unshift lib unless $:.include?(lib)
3
+
4
+ require 'bundler'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "flipp"
8
+ s.version = '0.0.1'
9
+ s.platform = Gem::Platform::RUBY
10
+ s.authors = ["Mike Pack"]
11
+ s.email = ["mikepackdev@gmail.com"]
12
+ s.homepage = "http://www.mikepackdev.com"
13
+ s.summary = %q{Switch development databases upon branch checkouts.}
14
+ s.description = %q{To alleviate issues around highly divergent development databases on feature/refactor branches, flipp helps switch databases upon new branch checkouts.}
15
+
16
+ s.required_rubygems_version = ">= 1.3.6"
17
+
18
+ s.add_runtime_dependency 'active_record'
19
+ s.add_runtime_dependency 'database_exporter'
20
+
21
+ s.files = %w( README.md Rakefile flipp.gemspec tasks/flipp.rb ) + Dir["lib/**/*.rb"]
22
+ s.test_files = Dir["spec/**/*.rb"]
23
+ s.require_paths = ["lib"]
24
+ end
@@ -0,0 +1,2 @@
1
+ require 'flipp/flipp'
2
+ require 'flipp/railties/reestablish_connection' if defined?(Rails)
@@ -0,0 +1,56 @@
1
+ require 'rubygems'
2
+ require 'active_record'
3
+ require 'database_exporter'
4
+ require File.join(File.dirname(__FILE__), '../helpers/git_helper')
5
+
6
+ # The Flipp class orchestrates the actual switching of databaseses
7
+ class Flipp
8
+ def initialize(branch = GitHelper.current_branch)
9
+ @branch = branch
10
+ end
11
+
12
+ def switch_databases
13
+ current_config = ActiveRecord::Base.connection_pool.spec.config
14
+ new_config = current_config.merge!({:database => new_database_name(current_config)})
15
+
16
+ puts "Switching databases (#{new_config[:database]})..."
17
+
18
+ # Try connecting to the new database
19
+ begin
20
+ ActiveRecord::Base.establish_connection new_config
21
+ rescue
22
+ create_new_db_or_fallback current_config, new_config
23
+ end
24
+ end
25
+
26
+ def create_new_db_or_fallback(current_config, new_config)
27
+ # Try creating then connecting to the new database
28
+ begin
29
+ create_and_copy_data! current_config, new_config
30
+ rescue => exception
31
+ # Fallback and use the original config
32
+ puts "Could not use the new database, falling back to your normal development database..."
33
+ ActiveRecord::Base.establish_connection current_config
34
+ end
35
+ end
36
+
37
+ def new_database_name(config)
38
+ # The new name for the database
39
+ # eg. if the original dev database is dev_db and the branch was "new_feature",
40
+ # the new database would be db_db_new_feature
41
+ config[:database] + '_' + @branch
42
+ end
43
+
44
+ def create_and_copy_data!(current_config, new_config)
45
+ # Copy the current development database's data to the new database
46
+ puts "Copying data to the new database (this could take a minute but will only run once)..."
47
+ copy_data current_config, new_config
48
+
49
+ # Try out the new database
50
+ ActiveRecord::Base.establish_connection new_config
51
+ end
52
+
53
+ def copy_data(current_config, new_config)
54
+ DatabaseExporter.new(current_config).copy(new_config)
55
+ end
56
+ end
@@ -0,0 +1,11 @@
1
+ require 'flipp'
2
+
3
+ class ReestablishConnection < Rails::Railtie
4
+ initializer "restablish_connection.configure_rails_initialization" do
5
+ if Rails.env.development? and GitHelper.hook_installed?
6
+ # Use the connection determined by flipp
7
+ flipp = Flipp.new
8
+ flipp.switch_databases
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,33 @@
1
+ require 'fileutils'
2
+
3
+ module GitHelper
4
+ def self.hook_installed?
5
+ File.exists? '.git/hooks/post-checkout'
6
+ end
7
+
8
+ def self.install_hook
9
+ # Check for the .git directory
10
+ if File.directory? '.git'
11
+ unless File.directory? '.git/hooks'
12
+ # Create hooks directory
13
+ end
14
+
15
+ # Copy the file to the git hooks
16
+ puts 'Moving git hook into place...'
17
+ FileUtils.cp 'lib/git_hooks/post-checkout', File.expand_path('.git/hooks/post-checkout')
18
+ # And give it execute permissions
19
+ FileUtils.chmod 0755, '.git/hooks/post-checkout'
20
+ else
21
+ puts "Can't find your .git directory. Are you at the root of your project?"
22
+ end
23
+ end
24
+
25
+ def self.uninstall_hook
26
+ puts 'Removing git hook...'
27
+ FileUtils.rm File.expand_path('.git/hooks/post-checkout')
28
+ end
29
+
30
+ def self.current_branch
31
+ `git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* /\1/'`.chomp[1..-1] # Remove control character
32
+ end
33
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe Flipp do
4
+ let(:flipp) { Flipp.new('new_feature_branch') }
5
+ let(:old_db) { {:adapter => 'mysql2', :host => 'localhost', :username => 'root', :database => 'old_db'} }
6
+ let(:new_db) { {:adapter => 'mysql2', :host => 'localhost', :username => 'root', :database => 'new_db'} }
7
+
8
+ describe '#switch_databases' do
9
+ before do
10
+ # Stub the current connection
11
+ ActiveRecord::Base.stub_chain(:connection_pool, :spec, :config).and_return({:adapter => 'mysql2', :host => 'localhost', :username => 'root', :database => 'old_db'})
12
+ end
13
+
14
+ it 'tries to connect to the new database' do
15
+ ActiveRecord::Base.should_receive(:establish_connection).with(an_instance_of(Hash))
16
+ flipp.switch_databases
17
+ end
18
+
19
+ it 'tries to create and copy data to new database, then connect' do
20
+ ActiveRecord::Base.stub(:establish_connection) { raise }
21
+ flipp.should_receive(:create_new_db_or_fallback).with(an_instance_of(Hash), an_instance_of(Hash))
22
+ flipp.switch_databases
23
+ end
24
+ end
25
+
26
+ describe '#create_new_db_or_fallback' do
27
+ it 'falls back on the original connection if the new database can not be formed' do
28
+ flipp.stub(:create_and_copy_data!) { raise }
29
+ flipp.should_receive(:create_and_copy_data!).with(old_db, new_db)
30
+ ActiveRecord::Base.should_receive(:establish_connection).with(old_db)
31
+ flipp.create_new_db_or_fallback(old_db, new_db)
32
+ end
33
+ end
34
+
35
+ describe '#new_database_name' do
36
+ it 'constructs a database name consisting of the current dev db and the branch name' do
37
+ flipp.new_database_name({:database => 'dev_db'}).should == 'dev_db_new_feature_branch'
38
+ end
39
+ end
40
+
41
+ describe '#create_and_copy_data!' do
42
+ it 'copies the database' do
43
+ flipp.should_receive(:copy_data).with(old_db, new_db)
44
+ flipp.create_and_copy_data!(old_db, new_db)
45
+ end
46
+
47
+ it 'tries to connect to the new database' do
48
+ ActiveRecord::Base.should_receive(:establish_connection).with(new_db)
49
+ flipp.create_and_copy_data!(old_db, new_db)
50
+ end
51
+ end
52
+
53
+ describe '#copy_data' do
54
+ it 'invokes the database adapter and makes a copy' do
55
+ # Mock DatabaseExporter so we can access the #copy method
56
+ mock = double(DatabaseExporter)
57
+ DatabaseExporter.stub(:new).and_return(mock)
58
+
59
+ # Expect it to instantiate and call the #copy method
60
+ DatabaseExporter.should_receive(:new).with(old_db)
61
+ mock.should_receive(:copy).with(new_db)
62
+
63
+ flipp.copy_data(old_db, new_db)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe GitHelper do
4
+ describe '#hook_installed?' do
5
+ it 'returns true when the hook exists' do
6
+ FileUtils.touch '.git/hooks/post-checkout'
7
+ GitHelper.hook_installed?.should be_true
8
+ end
9
+
10
+ it 'returns false when the hook does not exists' do
11
+ FileUtils.rm '.git/hooks/post-checkout'
12
+ GitHelper.hook_installed?.should be_false
13
+ end
14
+ end
15
+
16
+ describe '#install_hook' do
17
+ it 'moves the git hook into place' do
18
+ GitHelper.install_hook
19
+ FileUtils.compare_file('.git/hooks/post-checkout', File.expand_path('../../../../lib/git_hooks/post-checkout', __FILE__))
20
+ end
21
+ end
22
+
23
+ describe '#uninstall_hook' do
24
+ before { GitHelper.install_hook }
25
+
26
+ it 'remove the git hook' do
27
+ GitHelper.uninstall_hook
28
+ GitHelper.hook_installed?.should be_false
29
+ end
30
+ end
31
+
32
+ describe '#current_branch' do
33
+ it 'returns the name of the current git branch' do
34
+ `git checkout -b new_and_fun_branch`
35
+ GitHelper.current_branch.should == 'new_and_fun_branch'
36
+
37
+ # Cleanup
38
+ `git checkout master && git branch -d new_and_fun_branch`
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ $:.unshift File.expand_path('../..', __FILE__)
2
+ require 'lib/helpers/git_helper'
3
+ require 'lib/flipp/flipp'
@@ -0,0 +1,15 @@
1
+ require File.join(File.dirname(__FILE__), '../lib/helpers/git_helper')
2
+
3
+ namespace :flipp do
4
+ desc 'Install flipp'
5
+ task :install do
6
+ # Move git hook into place
7
+ GitHelper.install_hook
8
+ end
9
+
10
+ desc 'Uninstall flipp'
11
+ task :uninstall do
12
+ # Remove git hook
13
+ GitHelper.uninstall_hook
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flipp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mike Pack
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-19 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: active_record
16
+ requirement: &70345674000980 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70345674000980
25
+ - !ruby/object:Gem::Dependency
26
+ name: database_exporter
27
+ requirement: &70345673999500 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70345673999500
36
+ description: To alleviate issues around highly divergent development databases on
37
+ feature/refactor branches, flipp helps switch databases upon new branch checkouts.
38
+ email:
39
+ - mikepackdev@gmail.com
40
+ executables: []
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - README.md
45
+ - Rakefile
46
+ - flipp.gemspec
47
+ - tasks/flipp.rb
48
+ - lib/flipp/flipp.rb
49
+ - lib/flipp/railties/reestablish_connection.rb
50
+ - lib/flipp.rb
51
+ - lib/helpers/git_helper.rb
52
+ - spec/lib/flipp/flipp_spec.rb
53
+ - spec/lib/helpers/git_helper_spec.rb
54
+ - spec/spec_helper.rb
55
+ homepage: http://www.mikepackdev.com
56
+ licenses: []
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: 1.3.6
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 1.8.10
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Switch development databases upon branch checkouts.
79
+ test_files:
80
+ - spec/lib/flipp/flipp_spec.rb
81
+ - spec/lib/helpers/git_helper_spec.rb
82
+ - spec/spec_helper.rb