pt-osc 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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