percona-migrations 0.0.2

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,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