Skip to content

Commit

Permalink
Index methods working and fully specd
Browse files Browse the repository at this point in the history
  • Loading branch information
anthonyalberto committed Nov 24, 2013
1 parent fcf579a commit f421539
Show file tree
Hide file tree
Showing 14 changed files with 390 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -16,3 +16,6 @@ tmp
.yardoc
_yardoc
doc/

.DS_Store
*.swp
2 changes: 2 additions & 0 deletions .rspec
@@ -0,0 +1,2 @@
--color
--format progress
2 changes: 2 additions & 0 deletions Gemfile
@@ -0,0 +1,2 @@
source :rubygems
gemspec
115 changes: 115 additions & 0 deletions Gemfile.lock
@@ -0,0 +1,115 @@
PATH
remote: .
specs:
mysql_online_migrations (0.0.1)
activerecord (~> 3.2.15)
mysql2

GEM
remote: http://rubygems.org/
specs:
actionmailer (3.2.15)
actionpack (= 3.2.15)
mail (~> 2.5.4)
actionpack (3.2.15)
activemodel (= 3.2.15)
activesupport (= 3.2.15)
builder (~> 3.0.0)
erubis (~> 2.7.0)
journey (~> 1.0.4)
rack (~> 1.4.5)
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.2.1)
activemodel (3.2.15)
activesupport (= 3.2.15)
builder (~> 3.0.0)
activerecord (3.2.15)
activemodel (= 3.2.15)
activesupport (= 3.2.15)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activeresource (3.2.15)
activemodel (= 3.2.15)
activesupport (= 3.2.15)
activesupport (3.2.15)
i18n (~> 0.6, >= 0.6.4)
multi_json (~> 1.0)
arel (3.0.3)
builder (3.0.4)
coderay (1.0.9)
diff-lcs (1.2.5)
erubis (2.7.0)
hike (1.2.3)
i18n (0.6.5)
journey (1.0.4)
json (1.8.1)
logger (1.2.8)
mail (2.5.4)
mime-types (~> 1.16)
treetop (~> 1.4.8)
method_source (0.8.2)
mime-types (1.25)
multi_json (1.8.2)
mysql2 (0.3.14)
polyglot (0.3.3)
pry (0.9.12.2)
coderay (~> 1.0.5)
method_source (~> 0.8)
slop (~> 3.4)
rack (1.4.5)
rack-cache (1.2)
rack (>= 0.4)
rack-ssl (1.3.3)
rack
rack-test (0.6.2)
rack (>= 1.0)
rails (3.2.15)
actionmailer (= 3.2.15)
actionpack (= 3.2.15)
activerecord (= 3.2.15)
activeresource (= 3.2.15)
activesupport (= 3.2.15)
bundler (~> 1.0)
railties (= 3.2.15)
railties (3.2.15)
actionpack (= 3.2.15)
activesupport (= 3.2.15)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (>= 0.14.6, < 2.0)
rake (10.1.0)
rdoc (3.12.2)
json (~> 1.4)
rspec (2.14.1)
rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0)
rspec-core (2.14.7)
rspec-expectations (2.14.4)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.14.4)
slop (3.4.6)
sprockets (2.2.2)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
thor (0.18.1)
tilt (1.4.1)
treetop (1.4.15)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.38)

PLATFORMS
ruby

DEPENDENCIES
activerecord (~> 3.2.15)
logger
mysql_online_migrations!
pry
rails (~> 3.2.15)
rspec
26 changes: 26 additions & 0 deletions lib/mysql_online_migrations.rb
@@ -0,0 +1,26 @@
require 'active_record'
require "mysql_online_migrations/columns"
require "mysql_online_migrations/indexes"
require "active_record/connection_adapters/mysql2_adapter"
require "active_record/connection_adapters/abstract_mysql_adapter"

module MysqlOnlineMigrations
include Indexes
include Columns

def self.included(base)
base.extend ClassMethods
end

def lock_statement(lock)
return "" if lock == true
return "" if defined?(Rails) && Rails.application.config.active_record.mysql_online_migrations == false
puts "ONLINE MIGRATION"
" LOCK=NONE"
end

module ClassMethods
end
end

ActiveRecord::ConnectionAdapters::Mysql2Adapter.send(:include, MysqlOnlineMigrations)
5 changes: 5 additions & 0 deletions lib/mysql_online_migrations/columns.rb
@@ -0,0 +1,5 @@
module MysqlOnlineMigrations
module Columns

end
end
22 changes: 22 additions & 0 deletions lib/mysql_online_migrations/indexes.rb
@@ -0,0 +1,22 @@
module MysqlOnlineMigrations
module Indexes
def add_index(table_name, column_name, options = {})
lock = options.delete(:lock)
index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns}) #{lock_statement(lock)}"
end

def remove_index(table_name, options_index_name, options = {})
lock = options.delete(:lock)
execute "DROP INDEX #{quote_column_name(index_name_for_remove(table_name, options_index_name))} ON #{quote_table_name(table_name)} #{lock_statement(lock)}"
end

def rename_index(table_name, old_name, new_name, options = {})
old_index_def = indexes(table_name).detect { |i| i.name == old_name }
return unless old_index_def
lock = options[:lock]
remove_index(table_name, { :name => old_name }, options)
add_index(table_name, old_index_def.columns, options.merge(name: new_name, unique: old_index_def.unique, lock: lock))
end
end
end
18 changes: 18 additions & 0 deletions mysql_online_migrations.gemspec
@@ -0,0 +1,18 @@
Gem::Specification.new do |s|
s.name = 'mysql_online_migrations'
s.version = '0.0.1'
s.summary = "Use MySQL 5.6+ capacities to perform online migrations"
s.description = "MySQL 5.6 adds a way to append `LOCK=NONE` to alter table statements to allow online migrations. Let's use it."
s.authors = ["Anthony Alberto"]
s.email = 'alberto.anthony@gmail.com'
s.files = ["lib/mysql_online_migrations.rb"]
s.homepage = 'https://github.com/anthonyalberto/mysql_online_migrations'

s.add_runtime_dependency "activerecord", "~> 3.2.15"
s.add_runtime_dependency "mysql2"
s.add_development_dependency "activerecord", "~> 3.2.15"
s.add_development_dependency "rails", "~> 3.2.15"
s.add_development_dependency "logger"
s.add_development_dependency "rspec"
s.add_development_dependency "pry"
end
Empty file.
64 changes: 64 additions & 0 deletions spec/lib/mysql_online_migrations/indexes_spec.rb
@@ -0,0 +1,64 @@
require "spec_helper"

describe MysqlOnlineMigrations::Indexes do
let(:comma_before_lock_none) { false }

context "#add_index" do
let(:queries) do
{
[:testing, :foo, {}] =>
"CREATE INDEX `index_testing_on_foo` ON `testing` (`foo`)",
[:testing, :foo, { length: 10 }] =>
"CREATE INDEX `index_testing_on_foo` ON `testing` (`foo`(10))",
[:testing, [:foo, :bar, :baz], {}] =>
"CREATE INDEX `index_testing_on_foo_and_bar_and_baz` ON `testing` (`foo`, `bar`, `baz`)",
[:testing, [:foo, :bar, :baz], { unique: true }] =>
"CREATE UNIQUE INDEX `index_testing_on_foo_and_bar_and_baz` ON `testing` (`foo`, `bar`, `baz`)",
[:testing, [:foo, :bar, :baz], { unique: true, name: "best_index_of_the_world" }] =>
"CREATE UNIQUE INDEX `best_index_of_the_world` ON `testing` (`foo`, `bar`, `baz`)",
}
end

let(:method_name) { :add_index }

it_behaves_like "a method that adds LOCK=NONE when needed"
end

context "#remove_index" do
let(:queries) do
{
[:testing, :baz, {}] =>
"DROP INDEX `index_testing_on_baz` ON `testing`",
[:testing, [:bar, :baz], {}] =>
"DROP INDEX `index_testing_on_bar_and_baz` ON `testing`",
[:testing, { column: [:bar, :baz] }, {}] =>
"DROP INDEX `index_testing_on_bar_and_baz` ON `testing`",
[:testing, { name: "best_index_of_the_world2" }, {}] =>
"DROP INDEX `best_index_of_the_world2` ON `testing`"
}
end
let(:method_name) { :remove_index }

it_behaves_like "a method that adds LOCK=NONE when needed"
end

context "#rename_index" do
let(:queries) do
{
[:testing, "best_index_of_the_world2", "renamed_best_index_of_the_world2", {}] =>
[
"DROP INDEX `best_index_of_the_world2` ON `testing`",
"CREATE INDEX `renamed_best_index_of_the_world2` ON `testing` (`extra`)",
],
[:testing, "best_index_of_the_world3", "renamed_best_index_of_the_world3", {}] =>
[
"DROP INDEX `best_index_of_the_world3` ON `testing`",
"CREATE UNIQUE INDEX `renamed_best_index_of_the_world3` ON `testing` (`baz`, `extra`)",
]
}
end
let(:method_name) { :rename_index }

it_behaves_like "a method that adds LOCK=NONE when needed"
end
end
43 changes: 43 additions & 0 deletions spec/spec_helper.rb
@@ -0,0 +1,43 @@
# This file was generated by the `rspec --init` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# Require this file using `require "spec_helper"` to ensure that it is only
# loaded once.
#
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
# require 'active_record'
require 'rubygems'
require 'bundler/setup'
require 'mysql_online_migrations'
require 'logger'
#require 'rails'
require 'pry'
require 'support/helpers'
require 'support/shared_examples/mysql_online_migrations'

RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true
config.run_all_when_everything_filtered = true
config.filter_run :focus

config.include Helpers

# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = 'random'

config.before(:all) do
setup
require 'support/rails'
end

config.after(:all) do
teardown
end

config.before(:each) do
stub_execute
set_ar_setting(true)
end
end
59 changes: 59 additions & 0 deletions spec/support/helpers.rb
@@ -0,0 +1,59 @@
module Helpers
def execute(statement)
end

def stub_execute
original_execute = @adapter.method(:execute)
@adapter.stub(:execute) do |statement|
if statement =~ /^(alter|create|drop) /i
execute(statement.squeeze(' ').strip)
else
original_execute.call(statement)
end
end
end

def add_lock_none(str, with_comma)
"#{str}#{with_comma ? ', ' : ''} LOCK=NONE"
end

def setup
ActiveRecord::Base.establish_connection(
adapter: :mysql2,
reconnect: false,
database: "mysql_online_migrations",
username: "root",
host: "localhost",
encoding: "utf8",
socket: "/tmp/mysql.sock"
)

ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Base.logger.level = Logger::INFO

@adapter = ActiveRecord::Base.connection

@table_name = :testing
@adapter.drop_table @table_name rescue nil
@adapter.create_table @table_name do |t|
t.column :foo, :string, :limit => 100
t.column :bar, :string, :limit => 100
t.column :baz, :string, :limit => 100
t.column :extra, :string, :limit => 100
end

@adapter.add_index :testing, :baz
@adapter.add_index :testing, [:bar, :baz]
@adapter.add_index :testing, :extra, name: "best_index_of_the_world2"
@adapter.add_index :testing, [:baz, :extra], name: "best_index_of_the_world3", unique: true
end

def set_ar_setting(value)
Rails.stub_chain(:application, :config, :active_record, :mysql_online_migrations).and_return(value)
end

def teardown
@adapter.drop_table :testing rescue nil
ActiveRecord::Base.primary_key_prefix_type = nil
end
end
2 changes: 2 additions & 0 deletions spec/support/rails.rb
@@ -0,0 +1,2 @@
module Rails
end

0 comments on commit f421539

Please sign in to comment.