alterity 0.9.0 → 1.0.0

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