pt-osc 0.2.2 → 0.2.3

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.
@@ -1,3 +1,7 @@
1
+ ## 0.2.3
2
+
3
+ - a warning will be issued if an `ALTER` command is called outside of a PtOscMigration
4
+
1
5
  ## 0.2.2
2
6
 
3
7
  - fix bugs with string quoting, use shellwords instead
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
- [![Travis CI](https://travis-ci.org/steverice/pt-osc.svg)](https://travis-ci.org/steverice/pt-osc)
2
- [![Code Climate](https://codeclimate.com/github/steverice/pt-osc.png)](https://codeclimate.com/github/steverice/pt-osc)
3
- [![Code Coverage](https://codeclimate.com/github/steverice/pt-osc/coverage.png)](https://codeclimate.com/github/steverice/pt-osc)
4
- [![Gem Version](https://badge.fury.io/rb/pt-osc.svg)](http://badge.fury.io/rb/pt-osc)
1
+ [![Travis CI](https://img.shields.io/travis/steverice/pt-osc.svg)](https://travis-ci.org/steverice/pt-osc)
2
+ [![Code Climate](https://img.shields.io/codeclimate/github/steverice/pt-osc.svg)](https://codeclimate.com/github/steverice/pt-osc)
3
+ [![Code Coverage](https://img.shields.io/codeclimate/coverage/github/steverice/pt-osc.svg)](https://codeclimate.com/github/steverice/pt-osc)
4
+ [![Gem Version](https://img.shields.io/gem/v/pt-osc.svg)](http://badge.fury.io/rb/pt-osc)
5
+ [![Dependencies](https://img.shields.io/gemnasium/steverice/pt-osc.svg)](https://gemnasium.com/steverice/pt-osc)
5
6
 
6
7
  ## `pt-online-schema-change` migrations
7
8
 
@@ -123,10 +123,24 @@ module ActiveRecord
123
123
 
124
124
  protected
125
125
  def add_command(table_name, command)
126
+ warn (<<-WARN
127
+ You are trying to ALTER table "#{table_name}" with the mysql_pt_osc adapter without using an PtOscMigration.
128
+ Be aware that ALTER commands will only be executed via pt-online-schema-change inside of an ActiveRecord::PtOscMigration.
129
+ It is likely that although your migration will complete, no schema alterations have been made.
130
+ WARN
131
+ ) if caller.any? { |c| c.include? 'active_record/migration.rb' } && caller.none? { |c| c.include? 'active_record/pt_osc_migration.rb' }
126
132
  get_commands[table_name] ||= []
127
133
  get_commands[table_name] << command
128
134
  end
129
135
 
136
+ # Provides the opportunity to handle warnings in a custom way
137
+ # @param [String] message
138
+ def warn(message)
139
+ # Rake tasks loaded through Railties had warnings silenced before Rails 4.1
140
+ # @see https://github.com/rails/rails/pull/11601
141
+ defined?(Rails) && Rails.version < '4.1' ? enable_warnings { super } : super
142
+ end
143
+
130
144
  def get_commands(table_name = nil)
131
145
  @osc_commands ||= {}
132
146
  table_name.nil? ? @osc_commands : @osc_commands[table_name]
@@ -88,6 +88,11 @@ module ActiveRecord
88
88
  end
89
89
  end
90
90
 
91
+ def method_missing(method, *arguments, &block) # :nodoc:
92
+ # Putting this here ensures that pt_osc_migration shows up in the caller trace
93
+ super
94
+ end
95
+
91
96
  protected
92
97
  def execute_pt_osc
93
98
  return unless @connection.is_a? ActiveRecord::ConnectionAdapters::MysqlPtOscAdapter
@@ -1,5 +1,5 @@
1
1
  module Pt
2
2
  module Osc
3
- VERSION = '0.2.2'
3
+ VERSION = '0.2.3'
4
4
  end
5
5
  end
@@ -3,6 +3,8 @@ require 'yaml'
3
3
 
4
4
  class MysqlPtOscAdapterTest < Test::Unit::TestCase
5
5
  class TestConnection < ActiveRecord::Base; end
6
+ class TestArMigration < ActiveRecord::Migration; end
7
+ class TestPtOscMigration < ActiveRecord::PtOscMigration; end
6
8
 
7
9
  def test_connection
8
10
  # Test that we can open a connection with the pt_osc adapter
@@ -18,4 +20,78 @@ class MysqlPtOscAdapterTest < Test::Unit::TestCase
18
20
  # Test that the adapter can be loaded when using the activerecord-import gem
19
21
  assert_nothing_raised { require 'activerecord-import' }
20
22
  end
23
+
24
+ context 'connected using stubbed pt-osc adapter' do
25
+ setup do
26
+ ActiveRecord::Base.establish_connection(test_spec)
27
+ ActiveRecord::ConnectionAdapters::MysqlPtOscAdapter.stubs(:execute)
28
+ end
29
+
30
+ teardown do
31
+ ActiveRecord::ConnectionAdapters::MysqlPtOscAdapter.unstub(:execute)
32
+ end
33
+
34
+ context 'an ActiveRecord::Migration' do
35
+ setup do
36
+ @migration = TestArMigration.new
37
+ @migration.instance_variable_set(:@connection, ActiveRecord::Base.connection)
38
+ @migration.stubs(:write)
39
+ @migration.stubs(:announce)
40
+ end
41
+
42
+ teardown do
43
+ @migration.unstub(:write, :announce)
44
+ end
45
+
46
+ should 'raise a warning when adding columns' do
47
+ add_column_fixtures.each do |fixture|
48
+ fixture_command = sprintf(fixture[:command], table: 'a', column: 'b', default: 0, nullable: true)
49
+ @migration.class.class_eval "def change; #{fixture_command}; end"
50
+ ActiveRecord::ConnectionAdapters::MysqlPtOscAdapter.any_instance.expects(:warn).once
51
+ @migration.migrate(:up)
52
+ @migration.class.send(:remove_method, :change)
53
+ end
54
+ end
55
+
56
+ should 'explicitly enable warnings for Rails versions before 4.1' do
57
+ fixture = add_column_fixtures.first
58
+ fixture_command = sprintf(fixture[:command], table: 'a', column: 'b', default: 0, nullable: true)
59
+ @migration.class.class_eval "def change; #{fixture_command}; end"
60
+ %w(3.1 3.1.8 3.2 3.2.20 4.0 4.0.12).each do |rails_version|
61
+ Rails.stubs(:version).returns(rails_version)
62
+ Object.any_instance.expects(:enable_warnings).once.yields
63
+ silence_warnings { @migration.migrate(:up) }
64
+ end
65
+ %w(4.1 4.1.9 4.2 4.2.0.beta4 4.2.1 5 5.0).each do |rails_version|
66
+ Rails.stubs(:version).returns(rails_version)
67
+ Object.any_instance.expects(:enable_warnings).never
68
+ silence_warnings { @migration.migrate(:up) }
69
+ end
70
+ @migration.class.send(:remove_method, :change)
71
+ end
72
+ end
73
+
74
+ context 'an ActiveRecord::PtOscMigration' do
75
+ setup do
76
+ @migration = TestPtOscMigration.new
77
+ @migration.instance_variable_set(:@connection, ActiveRecord::Base.connection)
78
+ @migration.stubs(:write)
79
+ @migration.stubs(:announce)
80
+ end
81
+
82
+ teardown do
83
+ @migration.unstub(:write, :announce)
84
+ end
85
+
86
+ should 'not raise a warning when adding columns' do
87
+ add_column_fixtures.each do |fixture|
88
+ fixture_command = sprintf(fixture[:command], table: 'a', column: 'b', default: 0, nullable: true)
89
+ @migration.class.class_eval "def change; #{fixture_command}; end"
90
+ ActiveRecord::ConnectionAdapters::MysqlPtOscAdapter.any_instance.expects(:warn).never
91
+ @migration.migrate(:up)
92
+ @migration.class.send(:remove_method, :change)
93
+ end
94
+ end
95
+ end
96
+ end
21
97
  end
@@ -22,7 +22,7 @@ class PtOscMigrationFunctionalTest < ActiveRecord::TestCase
22
22
  context 'on an existing table with an existing column' do
23
23
  setup do
24
24
  @table_name = Faker::Lorem.word
25
- @column_name = Faker::Lorem.word
25
+ @column_name = 'foobar'
26
26
  @index_name = Faker::Lorem.words.join('_')
27
27
 
28
28
  ActiveRecord::Base.connection.execute "DROP TABLE IF EXISTS `#{@table_name}`;"
@@ -40,8 +40,8 @@ class PtOscMigrationFunctionalTest < ActiveRecord::TestCase
40
40
 
41
41
  context 'a migration with only ALTER statements' do
42
42
  setup do
43
- @renamed_column_name = Faker::Lorem.word
44
- @new_column_name = Faker::Lorem.word
43
+ @renamed_column_name = 'foobar'
44
+ @new_column_name = 'bazqux'
45
45
  @new_table_name = Faker::Lorem.word
46
46
  @index_name_2 = "#{@index_name}_2"
47
47
  @index_name_3 = "#{@index_name}_3"
@@ -46,32 +46,14 @@ class PtOscMigrationIntegrationTest < ActiveRecord::TestCase
46
46
  ]
47
47
  end
48
48
 
49
- # Rails' "magic" time, for Time columns
50
- # https://github.com/rails/rails/blob/fcf9b712b1dbbcb8f48644e6f20676ad9480ba66/activerecord/lib/active_record/type/time.rb#L16
51
- base_date = Time.utc(2000, 1, 1, 0, 0, 0)
52
-
53
- datetime_value = Time.at(Time.now.to_i).utc # Date types we're testing don't have sub-second precision
54
- date_value = Time.utc(datetime_value.year, datetime_value.month, datetime_value.day)
55
- rails_time_value = Time.utc(base_date.year, base_date.month, base_date.day, datetime_value.hour, datetime_value.min, datetime_value.sec)
56
-
57
- [
58
- { type: :integer, default: 42 },
59
- { type: :string, default: ["'foobar'", ':bazqux'], expected_default: ['foobar', 'bazqux'] },
60
- { type: :text, default: nil }, # TEXT columns cannot have a default http://dev.mysql.com/doc/refman/5.7/en/blob.html#idm140380410069472
61
- { type: :float, default: 3.14159 },
62
- { type: :datetime, default: "'#{datetime_value.strftime('%F %T')}'", expected_default: datetime_value },
63
- { type: :time, default: "'#{datetime_value.strftime('%T')}'", expected_default: rails_time_value },
64
- { type: :date, default: "'#{datetime_value.strftime('%F')}'", expected_default: date_value },
65
- { type: :binary, default: nil }, # BLOB columns cannot have a default http://dev.mysql.com/doc/refman/5.7/en/blob.html#idm140380410069472
66
- { type: :boolean, default: [false, true] },
67
- ].each do |test|
49
+ add_column_fixtures.each do |test|
68
50
 
69
51
  defaults = [test[:default]].flatten(1).compact # remove nils for columns that can't have a default
70
52
  expected_defaults = test[:expected_default] ? [test[:expected_default]].flatten(1) : defaults
71
53
 
72
54
  should "add a nullable #{test[:type]} column with default null" do
73
55
  column_name = "#{@new_column_name}_#{test[:type]}"
74
- @arguments[0] = "add_column :#{@table_name}, :#{column_name}, :#{test[:type]}, default: nil, null: true"
56
+ @arguments[0] = sprintf(test[:command], table: @table_name, column: column_name, default: nil, nullable: true)
75
57
  @arguments[-2] = column_name
76
58
  @arguments[-1] = { type: test[:type], null: true, default: nil }
77
59
  migrate_and_test_field *@arguments
@@ -80,7 +62,7 @@ class PtOscMigrationIntegrationTest < ActiveRecord::TestCase
80
62
  defaults.each_with_index do |default, index|
81
63
  should "add a nullable #{test[:type]} column with default value #{default}" do
82
64
  column_name = "#{@new_column_name}_#{test[:type]}"
83
- @arguments[0] = "add_column :#{@table_name}, :#{column_name}, :#{test[:type]}, default: #{default}, null: true"
65
+ @arguments[0] = sprintf(test[:command], table: @table_name, column: column_name, default: default, nullable: true)
84
66
  @arguments[-2] = column_name
85
67
  @arguments[-1] = { type: test[:type], null: true, default: expected_defaults[index] }
86
68
  migrate_and_test_field *@arguments
@@ -88,7 +70,7 @@ class PtOscMigrationIntegrationTest < ActiveRecord::TestCase
88
70
 
89
71
  should "add a not-nullable #{test[:type]} column with default value #{default}" do
90
72
  column_name = "#{@new_column_name}_#{test[:type]}"
91
- @arguments[0] = "add_column :#{@table_name}, :#{column_name}, :#{test[:type]}, default: #{default}, null: false"
73
+ @arguments[0] = sprintf(test[:command], table: @table_name, column: column_name, default: default, nullable: false)
92
74
  @arguments[-2] = column_name
93
75
  @arguments[-1] = { type: test[:type], null: false, default: expected_defaults[index] }
94
76
  migrate_and_test_field *@arguments
@@ -41,6 +41,33 @@ def migrate_and_test_field(command, migration, table_name, field_name, assertion
41
41
  migration.class.send(:remove_method, :change)
42
42
  end
43
43
 
44
+ # @return [Array<Hash>] Fixtures containing :type, :default, :expected_default, and :command for use with sprintf
45
+ def add_column_fixtures
46
+ # Rails' "magic" time, for Time columns
47
+ # https://github.com/rails/rails/blob/fcf9b712b1dbbcb8f48644e6f20676ad9480ba66/activerecord/lib/active_record/type/time.rb#L16
48
+ base_date = Time.utc(2000, 1, 1, 0, 0, 0)
49
+
50
+ datetime_value = Time.at(Time.now.to_i).utc # Date types we're testing don't have sub-second precision
51
+ date_value = Time.utc(datetime_value.year, datetime_value.month, datetime_value.day)
52
+ rails_time_value = Time.utc(base_date.year, base_date.month, base_date.day, datetime_value.hour, datetime_value.min, datetime_value.sec)
53
+
54
+ fixtures = [
55
+ { type: :integer, default: 42 },
56
+ { type: :string, default: ['foobar', :bazqux], expected_default: ['foobar', 'bazqux'] },
57
+ { type: :text, default: nil }, # TEXT columns cannot have a default http://dev.mysql.com/doc/refman/5.7/en/blob.html#idm140380410069472
58
+ { type: :float, default: 3.14159 },
59
+ { type: :datetime, default: datetime_value.strftime('%F %T'), expected_default: datetime_value },
60
+ { type: :time, default: datetime_value.strftime('%T'), expected_default: rails_time_value },
61
+ { type: :date, default: datetime_value.strftime('%F'), expected_default: date_value },
62
+ { type: :binary, default: nil }, # BLOB columns cannot have a default http://dev.mysql.com/doc/refman/5.7/en/blob.html#idm140380410069472
63
+ { type: :boolean, default: [false, true] },
64
+ ]
65
+ fixtures.map do |fixture|
66
+ fixture[:command] = "add_column :%{table}, :%{column}, :#{fixture[:type]}, default: %<default>p, null: %{nullable}"
67
+ fixture
68
+ end
69
+ end
70
+
44
71
  # SQLCounter is part of ActiveRecord but is not distributed with the gem (used for internal tests only)
45
72
  # see https://github.com/rails/rails/blob/3-2-stable/activerecord/test/cases/helper.rb#L59
46
73
  module ActiveRecord
@@ -7,6 +7,13 @@ class MysqlPtOscAdapterTest < Test::Unit::TestCase
7
7
  setup do
8
8
  TestConnection.establish_connection(test_spec)
9
9
  @adapter = TestConnection.connection
10
+ # Silence warnings about not using an ActiveRecord::PtOscMigration
11
+ # @see Kernel#suppress_warnings
12
+ @original_verbosity, $VERBOSE = $VERBOSE, nil
13
+ end
14
+
15
+ teardown do
16
+ $VERBOSE = @original_verbosity
10
17
  end
11
18
 
12
19
  context '#rename_table' do
@@ -32,7 +39,7 @@ class MysqlPtOscAdapterTest < Test::Unit::TestCase
32
39
 
33
40
  should 'add an ADD command to the commands hash' do
34
41
  table_name = Faker::Lorem.word
35
- column_name = Faker::Lorem.word
42
+ column_name = 'foobar'
36
43
  @adapter.add_column(table_name, column_name, :string, default: 0, null: false)
37
44
  assert_equal "ADD `#{column_name}` varchar(255) DEFAULT 0 NOT NULL", @adapter.send(:get_commands, table_name).first
38
45
  end
@@ -48,7 +55,7 @@ class MysqlPtOscAdapterTest < Test::Unit::TestCase
48
55
  context 'with an existing table and column' do
49
56
  setup do
50
57
  @table_name = Faker::Lorem.word
51
- @column_name = Faker::Lorem.word
58
+ @column_name = 'foobar'
52
59
  @adapter.create_table @table_name, force: true do |t|
53
60
  t.string @column_name
54
61
  end
@@ -71,14 +78,14 @@ class MysqlPtOscAdapterTest < Test::Unit::TestCase
71
78
  context 'with an existing table and column' do
72
79
  setup do
73
80
  @table_name = Faker::Lorem.word
74
- @column_name = Faker::Lorem.word
81
+ @column_name = 'foobar'
75
82
  @adapter.create_table @table_name, force: true do |t|
76
83
  t.string @column_name, default: nil
77
84
  end
78
85
  end
79
86
 
80
87
  should 'add a CHANGE command to the commands hash' do
81
- new_column_name = Faker::Lorem.word
88
+ new_column_name = 'foobar'
82
89
  @adapter.rename_column(@table_name, @column_name, new_column_name)
83
90
  assert_equal "CHANGE `#{@column_name}` `#{new_column_name}` varchar(255) DEFAULT NULL", @adapter.send(:get_commands, @table_name).first
84
91
  end
@@ -94,14 +101,14 @@ class MysqlPtOscAdapterTest < Test::Unit::TestCase
94
101
 
95
102
  should 'add a DROP COLUMN command to the commands hash' do
96
103
  table_name = Faker::Lorem.word
97
- column_name = Faker::Lorem.word
104
+ column_name = 'foobar'
98
105
  @adapter.remove_column(table_name, column_name)
99
106
  assert_equal "DROP COLUMN `#{column_name}`", @adapter.send(:get_commands, table_name).first
100
107
  end
101
108
 
102
109
  should 'add multiple DROP COLUMN commands to the commands hash' do
103
110
  table_name = Faker::Lorem.word
104
- column_names = 3.times.map { Faker::Lorem.word }
111
+ column_names = %w(foo bar baz)
105
112
  @adapter.remove_column(table_name, *column_names)
106
113
  commands = @adapter.send(:get_commands, table_name)
107
114
  column_names.each_with_index do |column_name, index|
@@ -120,7 +127,7 @@ class MysqlPtOscAdapterTest < Test::Unit::TestCase
120
127
  context 'with an existing table and columns' do
121
128
  setup do
122
129
  @table_name = Faker::Lorem.word
123
- @column_names = Faker::Lorem.words
130
+ @column_names = %w(foo bar baz)
124
131
  @adapter.create_table @table_name, force: true do |t|
125
132
  @column_names.each do |column_name|
126
133
  t.string(column_name, default: nil)
@@ -157,7 +164,7 @@ class MysqlPtOscAdapterTest < Test::Unit::TestCase
157
164
 
158
165
  should 'add a DROP COLUMN command to the commands hash' do
159
166
  table_name = Faker::Lorem.word
160
- index_name = Faker::Lorem.word
167
+ index_name = Faker::Lorem.words.join('_')
161
168
  @adapter.remove_index!(table_name, index_name)
162
169
  assert_equal "DROP INDEX `#{index_name}`", @adapter.send(:get_commands, table_name).first
163
170
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pt-osc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-02-14 00:00:00.000000000 Z
12
+ date: 2015-03-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -248,7 +248,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
248
248
  version: '0'
249
249
  segments:
250
250
  - 0
251
- hash: 999232518907264640
251
+ hash: 3650568307132967926
252
252
  required_rubygems_version: !ruby/object:Gem::Requirement
253
253
  none: false
254
254
  requirements:
@@ -257,7 +257,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
257
257
  version: '0'
258
258
  segments:
259
259
  - 0
260
- hash: 999232518907264640
260
+ hash: 3650568307132967926
261
261
  requirements: []
262
262
  rubyforge_project:
263
263
  rubygems_version: 1.8.23