switcheroo 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.
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ *.swp
4
+ .DS_Store
5
+ .bundle
6
+ .config
7
+ .vagrant
8
+ .yardoc
9
+ Gemfile.lock
10
+ InstalledFiles
11
+ _yardoc
12
+ coverage
13
+ doc/
14
+ lib/bundler/man
15
+ pkg
16
+ rdoc
17
+ spec/reports
18
+ test/tmp
19
+ test/version_tmp
20
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in zerobuf.gemspec
4
+ gemspec
@@ -0,0 +1,23 @@
1
+ Copyright 2012 MoneyDesktop Inc.
2
+ Copyright Switcheroo contributors https://github.com/moneydesktop/switcheroo/contributors
3
+
4
+ MIT License
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,37 @@
1
+ # Switcheroo
2
+
3
+ ActiveRecord migration library to speed up schema changes for large PostgreSQL tables
4
+
5
+ ## Usage
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'switcheroo'
10
+
11
+ And then in an ActiveRecord::Migration
12
+
13
+ class AddColumnsToTransactions < ActiveRecord::Migration
14
+ def up
15
+ switcheroo :transactions do |t|
16
+ t.datetime :deleted_date
17
+ t.rename :user_description, :description
18
+ ...
19
+ end
20
+ end
21
+
22
+ def down
23
+ switcheroo :transactions do |t|
24
+ t.remove :deleted_date
25
+ t.rename :description, :user_description
26
+ ...
27
+ end
28
+ end
29
+ end
30
+
31
+ ## Contributing
32
+
33
+ 1. Fork it
34
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
35
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
36
+ 4. Push to the branch (`git push origin my-new-feature`)
37
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,9 @@
1
+ require 'switcheroo/schema_statements'
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ class AbstractAdapter
6
+ include ::Switcheroo::SchemaStatements
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,124 @@
1
+ module Switcheroo
2
+ module SchemaStatements
3
+ def switcheroo(table_name, options = {})
4
+ switch_options = {
5
+ :table_name => table_name,
6
+ :clone_table_name => "clone_#{table_name}",
7
+ :columns => columns(table_name),
8
+ :indexes => indexes(table_name),
9
+ :pk_and_sequence => pk_and_sequence_for(table_name),
10
+ :recorder => ActiveRecord::Migration::CommandRecorder.new(self)
11
+ }
12
+ switch_options[:column_mappings] = switch_options[:columns].inject({}) do |hsh,column|
13
+ hsh[column.name] = column.name
14
+ hsh
15
+ end
16
+
17
+ yield ActiveRecord::ConnectionAdapters::Table.new(switch_options[:clone_table_name], switch_options[:recorder])
18
+
19
+ prepare_clone_table(switch_options)
20
+ clone_data(switch_options)
21
+ switch_tables(switch_options)
22
+ build_new_indexes(switch_options)
23
+ vacuum_table(switch_options)
24
+ end
25
+
26
+ private
27
+
28
+ def prepare_clone_table(opts)
29
+ ActiveRecord::Migration.say_with_time("Preparing the new table...") do
30
+ drop_table opts[:clone_table_name] if table_exists?(opts[:clone_table_name])
31
+
32
+ execute "CREATE TABLE #{opts[:clone_table_name]} " \
33
+ "(LIKE #{opts[:table_name]} " \
34
+ "INCLUDING DEFAULTS INCLUDING CONSTRAINTS)"
35
+ end
36
+ end
37
+
38
+ def clone_data(opts)
39
+ ActiveRecord::Migration.say_with_time("Inserting records into the new table...") do
40
+ process_commands(opts)
41
+
42
+ columns_list = []
43
+ values_list = []
44
+
45
+ opts[:column_mappings].each do |column,value|
46
+ columns_list << column
47
+ values_list << value
48
+ end
49
+
50
+ bulk_insert = "INSERT INTO #{opts[:clone_table_name]} ("
51
+ bulk_insert << columns_list.join(',')
52
+ bulk_insert << ") SELECT "
53
+ bulk_insert << values_list.join(',')
54
+ bulk_insert << " FROM #{opts[:table_name]}"
55
+
56
+ execute bulk_insert
57
+ end
58
+ end
59
+
60
+ def process_commands(opts)
61
+ opts[:recorder].commands.each do |cmd, cmd_opts|
62
+ send(cmd, *cmd_opts)
63
+
64
+ table, arguments = cmd_opts.shift, cmd_opts
65
+
66
+ case cmd
67
+ when :add_column
68
+ col_name, col_type, col_opts = *arguments
69
+
70
+ if source_column = col_opts[:source]
71
+ opts[:column_mappings][col_name.to_s] = source_column.to_s
72
+ end
73
+
74
+ when :remove_column
75
+ arguments.flatten.each do |col|
76
+ opts[:column_mappings].delete col.to_s
77
+
78
+ opts[:indexes].delete_if do |idx|
79
+ idx.columns.map(&:to_s).include?(col.to_s)
80
+ end
81
+ end
82
+
83
+ when :rename_column
84
+ source_col, dest_col = *arguments
85
+
86
+ opts[:column_mappings].delete source_col.to_s
87
+ opts[:column_mappings][dest_col.to_s] = source_col.to_s
88
+ end
89
+ end
90
+ end
91
+
92
+ def switch_tables(opts)
93
+ ActiveRecord::Migration.say_with_time("The ol' switcheroo...") do
94
+ ActiveRecord::Migration.suppress_messages do
95
+ execute "ALTER SEQUENCE #{opts[:pk_and_sequence][1]} OWNED BY #{opts[:clone_table_name]}.#{opts[:pk_and_sequence][0]}"
96
+ execute "DROP TABLE IF EXISTS #{opts[:table_name]} CASCADE"
97
+ execute "ALTER TABLE #{opts[:clone_table_name]} RENAME TO #{opts[:table_name]}"
98
+ execute "ALTER TABLE #{opts[:table_name]} ADD CONSTRAINT #{opts[:table_name]}_pkey PRIMARY KEY(#{opts[:pk_and_sequence][0]})"
99
+ end
100
+ end
101
+ end
102
+
103
+ def build_new_indexes(opts)
104
+ opts[:indexes].each do |index|
105
+ ActiveRecord::Migration.say_with_time("Building #{index.name}...") do
106
+ add_index opts[:table_name], index.columns, { :name => index.name, :unique => index.unique }
107
+ end
108
+ end
109
+ end
110
+
111
+ def vacuum_table(opts)
112
+ ActiveRecord::Migration.say_with_time("Vacumm analyze...") do
113
+ # VACUUM cannot run inside a transaction block
114
+ commit_db_transaction
115
+ execute "VACUUM ANALYZE #{opts[:table_name]}"
116
+
117
+ # The migration will attempt to commit a transaction when it is done
118
+ begin_db_transaction
119
+ end
120
+
121
+ ActiveRecord::Migration.say("Boom! Your table has been successfully rebuilt.")
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,3 @@
1
+ module Switcheroo
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'switcheroo/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = 'switcheroo'
8
+ gem.version = Switcheroo::VERSION
9
+ gem.authors = ['JohnnyT']
10
+ gem.email = ['johnnyt@moneydesktop.com']
11
+ gem.description = %q{ActiveRecord migration library to speed up schema changes for large PostgreSQL tables}
12
+ gem.summary = gem.description
13
+ gem.homepage = 'https://github.com/moneydesktop/switcheroo'
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ['lib']
19
+
20
+ gem.add_development_dependency 'rake'
21
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: switcheroo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - JohnnyT
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: ActiveRecord migration library to speed up schema changes for large PostgreSQL
31
+ tables
32
+ email:
33
+ - johnnyt@moneydesktop.com
34
+ executables: []
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - .gitignore
39
+ - Gemfile
40
+ - LICENSE.txt
41
+ - README.md
42
+ - Rakefile
43
+ - lib/switcheroo.rb
44
+ - lib/switcheroo/schema_statements.rb
45
+ - lib/switcheroo/version.rb
46
+ - switcheroo.gemspec
47
+ homepage: https://github.com/moneydesktop/switcheroo
48
+ licenses: []
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ segments:
60
+ - 0
61
+ hash: 891026658637282503
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ segments:
69
+ - 0
70
+ hash: 891026658637282503
71
+ requirements: []
72
+ rubyforge_project:
73
+ rubygems_version: 1.8.24
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: ActiveRecord migration library to speed up schema changes for large PostgreSQL
77
+ tables
78
+ test_files: []