alterity 0.9.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 61d1573038462b1d92925a95f8b67d8c0d53ffc250c429a742bb9a3e19fe715a
4
- data.tar.gz: e6cf943aae6b2ead8e6c04d335a63c1c1438c296381e066c513d0aefb99e6d13
3
+ metadata.gz: b468009cfd8631b397ea59fcb1750aada7238d5b8910f6cc492cf4778bdc2fa6
4
+ data.tar.gz: 02e17258c42233d8198ad62cf15841d6c3fe78b4b2730f64100e1771693686a1
5
5
  SHA512:
6
- metadata.gz: ea2e19e99394e1cacd831855e509363c4d164dabfe564ef7ac47945c61c9fc94071d965a3bc16efdd80530108fecd50ee582900d962f3fd63571ae1e6d71203f
7
- data.tar.gz: bd8f34566ced8252a587804d966c02e850d41a4d0faaf88bd0279886ea58286c4d70a7ca15b7ceafc17934bdce34f69b459b7546a1603acaee4b91ab26f89933
6
+ metadata.gz: 8929653022fbcec5f31c5583c619d252547928319276b020021404ad38cdbf2a2fae1861f9748009fcc97f99c0bc7a07f698397d6c8d20d41bbd96251b7a3693
7
+ data.tar.gz: ec349e1dfee9361ef937e55fe8911da437fad1c7dfc74446ff7f7ce2608943e0755f238297e5073f8786b2a6a0d9dc71c6785a1b91f1d7b04efe4b4a548a1b46
data/lib/alterity.rb CHANGED
@@ -8,14 +8,14 @@ require "alterity/railtie"
8
8
  class Alterity
9
9
  class << self
10
10
  def process_sql_query(sql, &block)
11
- case sql.strip
12
- when /^alter table (?<table>.+?) (?<updates>.+)/i
11
+ case sql.tr("\n", " ").strip
12
+ when /^alter\s+table\s+(?<table>.+?)\s+(?<updates>.+)/i
13
13
  execute_alter($~[:table], $~[:updates])
14
- when /^create index (?<index>.+?) on (?<table>.+?) (?<updates>.+)/i
14
+ when /^create\s+index\s+(?<index>.+?)\s+on\s+(?<table>.+?)\s+(?<updates>.+)/i
15
15
  execute_alter($~[:table], "ADD INDEX #{$~[:index]} #{$~[:updates]}")
16
- when /^create unique index (?<index>.+?) on (?<table>.+?) (?<updates>.+)/i
16
+ when /^create\s+unique\s+index\s+(?<index>.+?)\s+on\s+(?<table>.+?)\s+(?<updates>.+)/i
17
17
  execute_alter($~[:table], "ADD UNIQUE INDEX #{$~[:index]} #{$~[:updates]}")
18
- when /^drop index (?<index>.+?) on (?<table>.+)/i
18
+ when /^drop\s+index\s+(?<index>.+?)\s+on\s+(?<table>.+)/i
19
19
  execute_alter($~[:table], "DROP INDEX #{$~[:index]}")
20
20
  else
21
21
  block.call
@@ -38,9 +38,9 @@ class Alterity
38
38
  def execute_alter(table, updates)
39
39
  altered_table = table.delete("`")
40
40
  alter_argument = %("#{updates.gsub('"', '\\"').gsub('`', '\\\`')}")
41
- prepared_command = config.command.call(config, altered_table, alter_argument).gsub(/\n/, "\\\n")
41
+ prepared_command = config.command.call(altered_table, alter_argument).to_s.gsub(/\n/, "\\\n")
42
42
  puts "[Alterity] Will execute: #{prepared_command}"
43
- system(prepared_command)
43
+ system(prepared_command) || raise("[Alterity] Command failed")
44
44
  end
45
45
 
46
46
  def set_database_config
@@ -70,7 +70,7 @@ class Alterity
70
70
  return if config.replicas_dsns.empty?
71
71
 
72
72
  connection.execute <<~SQL
73
- INSERT INTO #{table} (dsn)
73
+ INSERT INTO #{table} (dsn) VALUES
74
74
  #{config.replicas_dsns.map { |dsn| "('#{dsn}')" }.join(',')}
75
75
  SQL
76
76
  end
@@ -13,46 +13,36 @@ class Alterity
13
13
  class << self
14
14
  def reset_state_and_configuration
15
15
  self.config = Configuration.new
16
- self.state = CurrentState.new
16
+ class << config
17
+ def replicas(database:, table:, dsns:)
18
+ return ArgumentError.new("database & table must be present") if database.blank? || table.blank?
19
+
20
+ self.replicas_dsns_database = database
21
+ self.replicas_dsns_table = table
22
+ self.replicas_dsns = dsns.uniq.map do |dsn|
23
+ parts = dsn.split(",")
24
+ # automatically add default port
25
+ parts << "P=3306" unless parts.any? { |part| part.start_with?("P=") }
26
+ # automatically remove master
27
+ next if parts.include?("h=#{host}") && parts.include?("P=#{port}")
28
+
29
+ parts.join(",")
30
+ end.compact
31
+ end
32
+ end
17
33
 
18
- config.command = lambda { |config, altered_table, alter_argument|
19
- <<~SHELL.squish
20
- pt-online-schema-change
21
- -h #{config.host}
22
- -P #{config.port}
23
- -u #{config.username}
24
- --password=#{config.password}
25
- --execute
26
- D=#{config.database},t=#{altered_table}
27
- --alter #{alter_argument}
28
- SHELL
29
- }
34
+ self.state = CurrentState.new
35
+ load "#{__dir__}/default_configuration.rb"
30
36
  end
31
37
 
32
38
  def configure
33
- yield self
39
+ yield config
34
40
  end
35
41
 
36
42
  def command=(new_command)
37
43
  config.command = new_command
38
44
  end
39
45
 
40
- def replicas_dsns_table(database:, table:, dsns:)
41
- return ArgumentError.new("database & table must be present") if database.blank? || table.blank?
42
-
43
- config.replicas_dsns_database = database
44
- config.replicas_dsns_table = table
45
- config.replicas_dsns = dsns.uniq.map do |dsn|
46
- parts = dsn.split(",")
47
- # automatically add default port
48
- parts << "P=3306" unless parts.any? { |part| part.start_with?("P=") }
49
- # automatically remove master
50
- next if parts.include?("h=#{config.host}") && parts.include?("P=#{config.port}")
51
-
52
- parts.join(",")
53
- end.compact
54
- end
55
-
56
46
  def disable
57
47
  state.disabled = true
58
48
  yield
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ Alterity.configure do |config|
4
+ config.command = lambda { |altered_table, alter_argument|
5
+ parts = ["pt-online-schema-change"]
6
+ parts << %(-h "#{config.host}") if config.host.present?
7
+ parts << %(-P "#{config.port}") if config.port.present?
8
+ parts << %(-u "#{config.username}") if config.username.present?
9
+ parts << %(--password "#{config.password.gsub('"', '\\"')}") if config.password.present?
10
+ parts << "--execute"
11
+ parts << "D=#{config.database},t=#{altered_table}"
12
+ parts << "--alter #{alter_argument}"
13
+ parts.join(" ")
14
+ }
15
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "mysql2"
4
+
3
5
  class Alterity
4
6
  module MysqlClientAdditions
5
7
  def query(sql, options = {})
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Alterity
4
- VERSION = "0.9.0"
4
+ VERSION = "1.0.0"
5
5
  end
@@ -9,8 +9,10 @@ RSpec.describe Alterity do
9
9
  ["CREATE INDEX `idx_users_on_col` ON `users` (col)", "`users`", "ADD INDEX `idx_users_on_col` (col)"],
10
10
  ["CREATE UNIQUE INDEX `idx_users_on_col` ON `users` (col)", "`users`", "ADD UNIQUE INDEX `idx_users_on_col` (col)"],
11
11
  ["DROP INDEX `idx_users_on_col` ON `users`", "`users`", "DROP INDEX `idx_users_on_col`"],
12
- ["alter table users drop col", "users", "drop col"]
12
+ ["alter table users drop col", "users", "drop col"],
13
+ [" ALTER TABLE\n users\n DROP col", "users", "DROP col"]
13
14
  ].each do |(query, expected_table, expected_updates)|
15
+ puts query.inspect
14
16
  expected_block = proc {}
15
17
  expect(expected_block).not_to receive(:call)
16
18
  expect(Alterity).to receive(:execute_alter).with(expected_table, expected_updates)
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ Alterity.configure do |config|
4
+ config.command = lambda { |altered_table, alter_argument|
5
+ string = config.to_h.slice(
6
+ *%i[host port username database replicas_dsns_database replicas_dsns_table replicas_dsns]
7
+ ).to_s
8
+ system("echo '#{string}' > /tmp/custom_command_result.txt")
9
+ system("echo '#{altered_table}' >> /tmp/custom_command_result.txt")
10
+ system("echo '#{alter_argument}' >> /tmp/custom_command_result.txt")
11
+ }
12
+
13
+ config.replicas(
14
+ database: "percona",
15
+ table: "replicas_dsns",
16
+ dsns: [
17
+ "h=host1",
18
+ "h=host2"
19
+ ]
20
+ )
21
+ end
@@ -1,7 +1,68 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
- set -euo pipefail
3
+ set -euox pipefail
4
+
5
+ unset BUNDLE_GEMFILE # because we're going to create a new rails app here and use bundler
4
6
 
5
- printenv
6
- sudo apt update
7
7
  sudo apt install percona-toolkit
8
+
9
+ # Fixes: `Cannot connect to MySQL: Cannot get MySQL var character_set_server: DBD::mysql::db selectrow_array failed: Table 'performance_schema.session_variables' doesn't exist [for Statement "SHOW VARIABLES LIKE 'character_set_server'"] at /usr/local/Cellar/percona-toolkit/3.3.0/libexec/bin/pt-online-schema-change line 2415.`
10
+ mysql -h $MYSQL_HOST -u $MYSQL_USERNAME -e 'set @@global.show_compatibility_56=ON'
11
+
12
+ gem install rails -v $RAILS_VERSION
13
+
14
+ rails new testapp \
15
+ --skip-action-mailer \
16
+ --skip-action-mailbox \
17
+ --skip-action-text \
18
+ --skip-active-job \
19
+ --skip-active-storage \
20
+ --skip-puma \
21
+ --skip-action-cable \
22
+ --skip-sprockets \
23
+ --skip-spring \
24
+ --skip-listen--skip-javascript \
25
+ --skip-turbolinks \
26
+ --skip-jbuilder--skip-test \
27
+ --skip-system-test \
28
+ --skip-bootsnap \
29
+ --skip-javascript \
30
+ --skip-webpack-install
31
+
32
+ cd testapp
33
+
34
+ # Sanity check:
35
+ # echo 'gem "mysql2"' >> Gemfile
36
+
37
+ echo 'gem "alterity", path: "../"' >> Gemfile
38
+
39
+ bundle
40
+
41
+ # Local machine test
42
+ # echo 'development:
43
+ # adapter: mysql2
44
+ # database: alterity_test' > config/database.yml
45
+ # bundle e rails db:drop db:create
46
+
47
+ echo 'development:
48
+ adapter: mysql2
49
+ database: <%= ENV.fetch("MYSQL_DATABASE") %>
50
+ host: <%= ENV.fetch("MYSQL_HOST") %>
51
+ username: <%= ENV.fetch("MYSQL_USERNAME") %>' > config/database.yml
52
+
53
+ bundle e rails g model shirt
54
+
55
+ bundle e rails g migration add_color_to_shirts color:string
56
+
57
+ # Test default configuration works as expected
58
+ bundle e rails db:migrate --trace
59
+ rails runner 'Shirt.columns.map(&:name).include?("color") || exit(1)'
60
+
61
+ # Now test custom command and replication setup
62
+ cp ../spec/bin/custom_config.rb config/initializers/alterity.rb
63
+
64
+ bundle e rails g migration add_color2_to_shirts color2:string
65
+
66
+ bundle e rails db:migrate --trace
67
+
68
+ ruby ../spec/bin/test_custom_config_result.rb
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ result = File.read("/tmp/custom_command_result.txt").downcase.strip
4
+
5
+ expected_result = %({:host=>"127.0.0.1", :port=>nil, :username=>"root", :database=>"alterity_test", :replicas_dsns_database=>"percona", :replicas_dsns_table=>"replicas_dsns", :replicas_dsns=>["h=host1,P=3306", "h=host2,P=3306"]}
6
+ shirts
7
+ "ADD \\`color2\\` VARCHAR(255)").downcase.strip
8
+
9
+ puts "Expected custom config result:"
10
+ puts expected_result
11
+ p expected_result.chars.map(&:hex)
12
+
13
+ puts "Custom config result:"
14
+ puts result
15
+ p result.chars.map(&:hex)
16
+
17
+ if result != expected_result
18
+ puts "=> mismatch"
19
+ exit(1)
20
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alterity
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Maximin
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '5'
33
+ version: '6.1'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '5'
40
+ version: '6.1'
41
41
  description: Execute your ActiveRecord migrations with Percona's pt-online-schema-change.
42
42
  email:
43
43
  - gems@chrismaximin.com
@@ -47,11 +47,14 @@ extra_rdoc_files: []
47
47
  files:
48
48
  - lib/alterity.rb
49
49
  - lib/alterity/configuration.rb
50
+ - lib/alterity/default_configuration.rb
50
51
  - lib/alterity/mysql_client_additions.rb
51
52
  - lib/alterity/railtie.rb
52
53
  - lib/alterity/version.rb
53
54
  - spec/alterity_spec.rb
55
+ - spec/bin/custom_config.rb
54
56
  - spec/bin/rails_app_migration_test.sh
57
+ - spec/bin/test_custom_config_result.rb
55
58
  - spec/spec_helper.rb
56
59
  homepage: https://github.com/gumroad/alterity
57
60
  licenses:
@@ -81,5 +84,7 @@ specification_version: 4
81
84
  summary: Execute your ActiveRecord migrations with Percona's pt-online-schema-change.
82
85
  test_files:
83
86
  - spec/alterity_spec.rb
87
+ - spec/bin/custom_config.rb
84
88
  - spec/bin/rails_app_migration_test.sh
89
+ - spec/bin/test_custom_config_result.rb
85
90
  - spec/spec_helper.rb