percona-migrations 0.0.2

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: 645a62ffc2dbbb400aab2434e5fa5662050a5976
4
+ data.tar.gz: 814f6e3fb1188855998cc3b71a48e7ef20afcd50
5
+ SHA512:
6
+ metadata.gz: e12d2fa7700ae6ba3ccd05445a00977ba9589cb91c59f29eef4c8a15718b8d0b1c0dc24260c18699a5fd6f18060cbd8a6a2f1daeac08914de8e8e54f44251e84
7
+ data.tar.gz: f1b48e3ca541afe4273718cf314938a49f0a7b960d03281312643d6b4d65c10f115ff5770180dff3c54b726a6b6d84bfc7c81075f8107972cdcc24e06971c721
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Sergey Varaksin
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,55 @@
1
+ # Percona Migrations
2
+
3
+ Allows to use `pt-online-schema-change` for table changes in MySQL.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'percona-migrations'
11
+ ```
12
+
13
+ Create `config/initializers/percona_migrations.rb` with:
14
+
15
+ ```ruby
16
+ PerconaMigrations.database_config = ActiveRecord::Base.configurations[Rails.env]
17
+ PerconaMigrations.allow_sql = !Rails.env.production?
18
+
19
+ ActiveRecord::Migration.send :include, PerconaMigrations::HelperMethods
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```ruby
25
+ class AddAddressToUsers < ActiveRecord::Migration
26
+ def up
27
+ commands = columns.map { |name, type| "ADD COLUMN #{name} #{type}" }
28
+
29
+ percona_alter_table :users, commands
30
+ end
31
+
32
+ def down
33
+ commands = columns.map { |name, _| "DROP COLUMN #{name}" }
34
+
35
+ percona_alter_table :users, commands
36
+ end
37
+
38
+ private
39
+
40
+ def columns
41
+ { street: 'STRING(255)' },
42
+ { city: 'STRING(255)' },
43
+ { state: 'STRING(2)' },
44
+ { zip: 'STRING(10)' }
45
+ end
46
+ end
47
+ ```
48
+
49
+ ## Contributing
50
+
51
+ 1. Fork it ( https://github.com/[my-github-username]/percona-migrations/fork )
52
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
53
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
54
+ 4. Push to the branch (`git push origin my-new-feature`)
55
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1 @@
1
+ require 'percona_migrations'
@@ -0,0 +1,33 @@
1
+ require 'percona_migrations/version'
2
+ require 'percona_migrations/runners'
3
+ require 'percona_migrations/helper_methods'
4
+
5
+ require 'active_record'
6
+ require 'logger'
7
+
8
+ module PerconaMigrations
9
+ extend self
10
+
11
+ @allow_sql = true
12
+
13
+ attr_writer :database_config, :allow_sql, :logger
14
+
15
+ def database_config
16
+ @database_config || raise('PerconaMigrations.database_config is not set.')
17
+ end
18
+
19
+ def allow_sql?
20
+ !!@allow_sql
21
+ end
22
+
23
+ def logger
24
+ unless defined? @logger
25
+ @logger = Logger.new($stdout)
26
+ @logger.formatter = proc do |severity, datetime, progname, msg|
27
+ "[percona-migrations] #{msg}\n"
28
+ end
29
+ end
30
+
31
+ @logger
32
+ end
33
+ end
@@ -0,0 +1,7 @@
1
+ module PerconaMigrations
2
+ module HelperMethods
3
+ def percona_alter_table(*args)
4
+ PerconaMigrations::Runners.run(*args)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,43 @@
1
+ require 'percona_migrations/runners/base'
2
+ require 'percona_migrations/runners/percona'
3
+ require 'percona_migrations/runners/sql'
4
+
5
+ module PerconaMigrations
6
+ module Runners
7
+ def self.run(*args)
8
+ runner_class = find_runner
9
+
10
+ unless runner_class
11
+ raise "No available migration runners found."
12
+ end
13
+
14
+ runner = runner_class.new(*args)
15
+ runner.run
16
+ end
17
+
18
+ def self.find_runner
19
+ if Runners::Percona.available?
20
+ Runners::Percona
21
+ else
22
+ log_percona_install_command
23
+ Runners::Sql if PerconaMigrations.allow_sql?
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def self.log_percona_install_command
30
+ logger = PerconaMigrations.logger
31
+ return unless logger
32
+
33
+ logger.warn ""
34
+ logger.warn "*" * 80
35
+ logger.warn ""
36
+ logger.warn "`#{Runners::Percona::COMMAND}` command not found, please install percona tools:"
37
+ logger.warn "$ brew install percona-toolkit"
38
+ logger.warn ""
39
+ logger.warn "*" * 80
40
+ logger.warn ""
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,26 @@
1
+ module PerconaMigrations
2
+ module Runners
3
+ class Base
4
+ def self.available?
5
+ true
6
+ end
7
+
8
+ def initialize(table_name, commands)
9
+ @table_name = table_name
10
+ @commands = commands
11
+ end
12
+
13
+ def run
14
+ raise NotImplementerError
15
+ end
16
+
17
+ def log(msg)
18
+ logger.info(msg) if logger
19
+ end
20
+
21
+ def logger
22
+ PerconaMigrations.logger
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,50 @@
1
+ module PerconaMigrations
2
+ module Runners
3
+ class Percona < Base
4
+ COMMAND = 'pt-online-schema-change'
5
+
6
+ def self.available?
7
+ !percona_command.empty?
8
+ end
9
+
10
+ def self.percona_command
11
+ @percona_command ||= %x(which #{COMMAND}).chop
12
+ end
13
+
14
+ def run
15
+ options = [
16
+ "--alter '#{@commands.join(', ')}'",
17
+ "-h #{database_config['host']}",
18
+ "-P #{database_config['port']}",
19
+ "-u #{database_config['username']}",
20
+ "D=#{database_config['database']},t=#{@table_name}"
21
+ ]
22
+
23
+ password = database_config['password']
24
+ if password && !password.empty?
25
+ options << "-p $PASSWORD"
26
+ end
27
+
28
+ run_command(options.join(' '), { 'PASSWORD' => password })
29
+ end
30
+
31
+ private
32
+
33
+ def database_config
34
+ PerconaMigrations.database_config
35
+ end
36
+
37
+ def run_command(options, env_vars = {})
38
+ %w(dry-run execute).each do |mode|
39
+ cmd = "#{self.class.percona_command} #{options} --#{mode}"
40
+
41
+ log "Running percona command: \"#{cmd}\""
42
+
43
+ unless system(env_vars, cmd)
44
+ raise "Percona command failed: #{$?}"
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,25 @@
1
+ module PerconaMigrations
2
+ module Runners
3
+ class Sql < Base
4
+ def run
5
+ @commands.each do |command|
6
+ run_command command
7
+ end
8
+ end
9
+
10
+ private
11
+
12
+ def run_command(command)
13
+ sql = "ALTER TABLE #{@table_name} #{command}"
14
+
15
+ log "Running SQL: \"#{sql}\""
16
+
17
+ conn.execute sql
18
+ end
19
+
20
+ def conn
21
+ @conn ||= ActiveRecord::Base.connection
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module PerconaMigrations
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'percona_migrations/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "percona-migrations"
8
+ spec.version = PerconaMigrations::VERSION
9
+ spec.authors = ["Sergey Varaksin"]
10
+ spec.email = ["varaksin86@gmail.com"]
11
+ spec.summary = %q{Allows to use percona in rails migrations.}
12
+ spec.homepage = "https://github.com/svarks/percona-migrations"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency 'activerecord', '>= 3.0'
21
+
22
+ spec.add_development_dependency 'rake'
23
+ spec.add_development_dependency 'rspec', '~> 3.2.0'
24
+ spec.add_development_dependency 'pry'
25
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe PerconaMigrations::HelperMethods do
4
+ subject do
5
+ klass = Class.new do
6
+ include PerconaMigrations::HelperMethods
7
+ end
8
+ klass.new
9
+ end
10
+
11
+ describe '#alter_table' do
12
+ it 'calls a method on PerconaMigrations' do
13
+ expect(PerconaMigrations::Runners).to receive(:run).with('users', ['add column name string(255)'])
14
+
15
+ subject.percona_alter_table('users', ['add column name string(255)'])
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe PerconaMigrations::Runners do
4
+ describe '::run' do
5
+ before do
6
+ expect(subject).to receive(:find_runner).and_return(runner_class)
7
+ end
8
+
9
+ context 'when runner is found' do
10
+ let(:runner_class) { PerconaMigrations::Runners::Percona }
11
+ let(:arguments) { [:arg1, :arg2, :arg3] }
12
+
13
+ it 'runs the runner' do
14
+ runner = double()
15
+ expect(runner_class).to receive(:new).with(*arguments).and_return(runner)
16
+ expect(runner).to receive(:run)
17
+
18
+ subject.run(*arguments)
19
+ end
20
+ end
21
+
22
+ context 'when runner is not found' do
23
+ let(:runner_class) { nil }
24
+
25
+ it 'throws an exception' do
26
+ expect { subject.run }.to raise_exception
27
+ end
28
+ end
29
+ end
30
+
31
+ describe '::find_runner' do
32
+ before do
33
+ expect(PerconaMigrations::Runners::Percona).to receive(:available?).and_return(percona_available)
34
+ end
35
+
36
+ context 'when percona is available' do
37
+ let(:percona_available) { true }
38
+
39
+ it 'returns percona runner if enabled' do
40
+ expect(subject.find_runner).to eq(PerconaMigrations::Runners::Percona)
41
+ end
42
+ end
43
+
44
+ context 'when percona is not available' do
45
+ let(:percona_available) { false }
46
+
47
+ context 'and sql is enabled' do
48
+ it 'returns sql runner' do
49
+ expect(subject.find_runner).to eq(PerconaMigrations::Runners::Sql)
50
+ end
51
+ end
52
+
53
+ context 'and sql is disabled' do
54
+ before do
55
+ allow(PerconaMigrations).to receive(:allow_sql?).and_return(false)
56
+ end
57
+ it 'returns nil' do
58
+ expect(subject.find_runner).to eq(nil)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe PerconaMigrations::Runners::Percona do
4
+ subject { described_class }
5
+
6
+ let(:percona_command) { '/bin/pt-online-schema-change' }
7
+
8
+ describe '::percona_command' do
9
+ it 'is looking for pt-online-schema-change in $PATH' do
10
+ expect(subject).to receive(:`).with('which pt-online-schema-change').and_return(percona_command + "\n")
11
+ expect(subject.percona_command).to eq(percona_command)
12
+ end
13
+ end
14
+
15
+ describe '::available?' do
16
+ it 'returns true when percona_command is present' do
17
+ expect(subject).to receive(:percona_command).and_return('/bin/percona')
18
+ expect(subject.available?).to eq(true)
19
+ end
20
+ end
21
+
22
+ describe '#run' do
23
+ let(:db_config) do
24
+ {
25
+ 'host' => 'localhost',
26
+ 'port' => '3306',
27
+ 'username' => 'root',
28
+ 'password' => 'test',
29
+ 'database' => 'test-db'
30
+ }
31
+ end
32
+ let(:runner) { described_class.new('users', ['add column name string(255)']) }
33
+ let(:arguments) do
34
+ [
35
+ "--alter 'add column name string(255)'",
36
+ "-h localhost",
37
+ "-P 3306",
38
+ "-u root",
39
+ "D=test-db,t=users",
40
+ "-p $PASSWORD"
41
+ ]
42
+ end
43
+
44
+ before do
45
+ allow(PerconaMigrations).to receive(:database_config).and_return(db_config)
46
+ allow(subject).to receive(:percona_command).and_return(percona_command)
47
+ end
48
+
49
+ it 'runs percona command 2 times (dry-run and execute)' do
50
+ command = "#{percona_command} #{arguments.join(' ')}"
51
+
52
+ expect(runner).to receive(:system).
53
+ with({ 'PASSWORD' => 'test' }, "#{command} --dry-run").
54
+ and_return(true)
55
+
56
+ expect(runner).to receive(:system).
57
+ with({ 'PASSWORD' => 'test' }, "#{command} --execute").
58
+ and_return(true)
59
+
60
+ runner.run
61
+ end
62
+
63
+ it 'throws an exception if dry-run fails' do
64
+ expect(runner).to receive(:system).
65
+ with({ 'PASSWORD' => 'test' }, "#{percona_command} #{arguments.join(' ')} --dry-run").
66
+ and_return(false)
67
+
68
+ expect { runner.run }.to raise_exception
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe PerconaMigrations::Runners::Sql do
4
+ let(:conn) { double() }
5
+ let(:runner) do
6
+ described_class.new('users', [
7
+ 'add column first_name string(255)',
8
+ 'add column last_name string(255)',
9
+ ])
10
+ end
11
+
12
+ before do
13
+ allow(ActiveRecord::Base).to receive(:connection).and_return(conn)
14
+ end
15
+
16
+ describe '#run' do
17
+ it 'runs sql commands throuth ActiveRecord' do
18
+ expect(conn).to receive(:execute).with('ALTER TABLE users add column first_name string(255)')
19
+ expect(conn).to receive(:execute).with('ALTER TABLE users add column last_name string(255)')
20
+
21
+ runner.run
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe PerconaMigrations do
4
+ describe '::allow_sql?' do
5
+ it 'is true by default' do
6
+ expect(subject.allow_sql?).to eq(true)
7
+ end
8
+
9
+ it 'can be set to false' do
10
+ subject.allow_sql = false
11
+
12
+ expect(subject.allow_sql?).to eq(false)
13
+
14
+ subject.allow_sql = true
15
+ end
16
+ end
17
+
18
+ describe '::database_config' do
19
+ it 'throws an exception if not set' do
20
+ expect { subject.database_config }.to raise_exception
21
+ end
22
+
23
+ it 'returns config' do
24
+ config = { 'username' => 'test' }
25
+ subject.database_config = config
26
+
27
+ expect(subject.database_config).to eq(config)
28
+
29
+ subject.database_config = nil
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.require(:default, :development)
5
+
6
+ $LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
7
+
8
+ require 'percona_migrations'
9
+
10
+ PerconaMigrations.logger = nil
11
+
12
+ RSpec.configure do |config|
13
+ config.expect_with :rspec do |expectations|
14
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
15
+ end
16
+
17
+ config.mock_with :rspec do |mocks|
18
+ mocks.verify_partial_doubles = true
19
+ end
20
+
21
+ config.filter_run :focus
22
+ config.run_all_when_everything_filtered = true
23
+ config.disable_monkey_patching!
24
+ config.order = :random
25
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: percona-migrations
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Sergey Varaksin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-12 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: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.2.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.2.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - varaksin86@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - lib/percona-migrations.rb
83
+ - lib/percona_migrations.rb
84
+ - lib/percona_migrations/helper_methods.rb
85
+ - lib/percona_migrations/runners.rb
86
+ - lib/percona_migrations/runners/base.rb
87
+ - lib/percona_migrations/runners/percona.rb
88
+ - lib/percona_migrations/runners/sql.rb
89
+ - lib/percona_migrations/version.rb
90
+ - percona-migrations.gemspec
91
+ - spec/lib/percona_migrations/helper_methods_spec.rb
92
+ - spec/lib/percona_migrations/runner_spec.rb
93
+ - spec/lib/percona_migrations/runners/percona_spec.rb
94
+ - spec/lib/percona_migrations/runners/sql_spec.rb
95
+ - spec/lib/percona_migrations_spec.rb
96
+ - spec/spec_helper.rb
97
+ homepage: https://github.com/svarks/percona-migrations
98
+ licenses:
99
+ - MIT
100
+ metadata: {}
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 2.2.2
118
+ signing_key:
119
+ specification_version: 4
120
+ summary: Allows to use percona in rails migrations.
121
+ test_files:
122
+ - spec/lib/percona_migrations/helper_methods_spec.rb
123
+ - spec/lib/percona_migrations/runner_spec.rb
124
+ - spec/lib/percona_migrations/runners/percona_spec.rb
125
+ - spec/lib/percona_migrations/runners/sql_spec.rb
126
+ - spec/lib/percona_migrations_spec.rb
127
+ - spec/spec_helper.rb