pt-osc 0.0.1

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.
Files changed (50) hide show
  1. data/.gitignore +22 -0
  2. data/Gemfile +4 -0
  3. data/README.md +35 -0
  4. data/Rakefile +20 -0
  5. data/lib/active_record/connection_adapters/pt_osc_adapter.rb +134 -0
  6. data/lib/active_record/pt_osc_migration.rb +150 -0
  7. data/lib/pt-osc.rb +3 -0
  8. data/lib/pt-osc/version.rb +5 -0
  9. data/pt-osc.gemspec +32 -0
  10. data/test/config/database.yml +6 -0
  11. data/test/dummy/.gitignore +1 -0
  12. data/test/dummy/README.rdoc +261 -0
  13. data/test/dummy/Rakefile +7 -0
  14. data/test/dummy/app/assets/javascripts/application.js +15 -0
  15. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  16. data/test/dummy/app/controllers/application_controller.rb +3 -0
  17. data/test/dummy/app/helpers/application_helper.rb +2 -0
  18. data/test/dummy/app/mailers/.gitkeep +0 -0
  19. data/test/dummy/app/models/.gitkeep +0 -0
  20. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  21. data/test/dummy/config.ru +4 -0
  22. data/test/dummy/config/application.rb +59 -0
  23. data/test/dummy/config/boot.rb +10 -0
  24. data/test/dummy/config/database.yml +16 -0
  25. data/test/dummy/config/environment.rb +5 -0
  26. data/test/dummy/config/environments/development.rb +37 -0
  27. data/test/dummy/config/environments/production.rb +67 -0
  28. data/test/dummy/config/environments/test.rb +37 -0
  29. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  30. data/test/dummy/config/initializers/inflections.rb +15 -0
  31. data/test/dummy/config/initializers/mime_types.rb +5 -0
  32. data/test/dummy/config/initializers/secret_token.rb +7 -0
  33. data/test/dummy/config/initializers/session_store.rb +8 -0
  34. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  35. data/test/dummy/config/locales/en.yml +5 -0
  36. data/test/dummy/config/routes.rb +58 -0
  37. data/test/dummy/db/development.sqlite3 +0 -0
  38. data/test/dummy/db/test.sqlite3 +0 -0
  39. data/test/dummy/lib/assets/.gitkeep +0 -0
  40. data/test/dummy/public/404.html +26 -0
  41. data/test/dummy/public/422.html +26 -0
  42. data/test/dummy/public/500.html +25 -0
  43. data/test/dummy/public/favicon.ico +0 -0
  44. data/test/dummy/script/rails +6 -0
  45. data/test/functional/pt_osc_adapter_test.rb +16 -0
  46. data/test/functional/pt_osc_migration_functional_test.rb +99 -0
  47. data/test/test_helper.rb +49 -0
  48. data/test/unit/pt_osc_adapter_test.rb +209 -0
  49. data/test/unit/pt_osc_migration_unit_test.rb +39 -0
  50. metadata +281 -0
File without changes
File without changes
File without changes
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/404.html -->
21
+ <div class="dialog">
22
+ <h1>The page you were looking for doesn't exist.</h1>
23
+ <p>You may have mistyped the address or the page may have moved.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/422.html -->
21
+ <div class="dialog">
22
+ <h1>The change you wanted was rejected.</h1>
23
+ <p>Maybe you tried to change something you didn't have access to.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,25 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/500.html -->
21
+ <div class="dialog">
22
+ <h1>We're sorry, but something went wrong.</h1>
23
+ </div>
24
+ </body>
25
+ </html>
File without changes
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,16 @@
1
+ require 'test_helper'
2
+ require 'yaml'
3
+
4
+ class PtOscAdapterTest < Test::Unit::TestCase
5
+ class TestConnection < ActiveRecord::Base; end
6
+
7
+ def test_connection
8
+ # Test that we can open a connection with the pt_osc adapter
9
+ spec = test_spec
10
+ spec.delete('database') # We don't care whether the database exists
11
+
12
+ assert_nothing_raised { TestConnection.establish_connection(spec) }
13
+ assert_equal true, TestConnection.connection.in_use
14
+ assert_kind_of ActiveRecord::ConnectionAdapters::PtOscAdapter, TestConnection.connection
15
+ end
16
+ end
@@ -0,0 +1,99 @@
1
+ require 'test_helper'
2
+
3
+ class PtOscMigrationFunctionalTest < ActiveRecord::TestCase
4
+ class TestMigration < ActiveRecord::PtOscMigration; end
5
+
6
+ context 'a migration connected to a pt-osc database' do
7
+ setup do
8
+ ActiveRecord::Base.establish_connection(test_spec)
9
+ @migration = TestMigration.new
10
+ @migration.instance_variable_set(:@connection, ActiveRecord::Base.connection)
11
+ end
12
+
13
+ context 'on an existing table with an existing column' do
14
+ setup do
15
+ @table_name = Faker::Lorem.word
16
+ @column_name = Faker::Lorem.word
17
+ @index_name = Faker::Lorem.words.join('_')
18
+ @index_name_2 = "#{@index_name}_2"
19
+
20
+ ActiveRecord::Base.connection.execute "DROP TABLE IF EXISTS `#{@table_name}`;"
21
+ ActiveRecord::Base.connection.execute <<-SQL
22
+ CREATE TABLE `#{@table_name}` (
23
+ `#{@column_name}` varchar(255) DEFAULT NULL,
24
+ KEY `#{@index_name}` (`#{@column_name}`)
25
+ );
26
+ SQL
27
+ end
28
+
29
+ teardown do
30
+ ActiveRecord::Base.connection.execute "DROP TABLE IF EXISTS `#{@table_name}`;"
31
+ end
32
+
33
+ context 'a migration with only ALTER statements' do
34
+ setup do
35
+ TestMigration.class_eval <<-EVAL
36
+ def change
37
+ rename_table :#{@table_name}, :#{Faker::Lorem.word}
38
+ add_column :#{@table_name}, :#{Faker::Lorem.word}, :integer
39
+ change_column :#{@table_name}, :#{@column_name}, :varchar, default: 'newthing'
40
+ rename_column :#{@table_name}, :#{@column_name}, :#{Faker::Lorem.word}
41
+ remove_column :#{@table_name}, :#{@column_name}
42
+ add_index :#{@table_name}, :#{@column_name}, name: :#{@index_name_2}
43
+ remove_index :#{@table_name}, name: :#{@index_name}
44
+ end
45
+ EVAL
46
+ end
47
+
48
+ teardown do
49
+ TestMigration.send(:remove_method, :change)
50
+ end
51
+
52
+ context 'ignoring schema lookups' do
53
+ setup do
54
+ # Kind of a hacky way to do this
55
+ ignored_sql = ActiveRecord::SQLCounter.ignored_sql + [
56
+ /^SHOW FULL FIELDS FROM/,
57
+ /^SHOW COLUMNS FROM/,
58
+ /^SHOW KEYS FROM/,
59
+ ]
60
+ ActiveRecord::SQLCounter.any_instance.stubs(:ignore).returns(ignored_sql)
61
+ end
62
+
63
+ teardown do
64
+ ActiveRecord::SQLCounter.any_instance.unstub(:ignore)
65
+ end
66
+
67
+ context 'with suppressed output' do
68
+ setup do
69
+ @migration.stubs(:write)
70
+ @migration.stubs(:announce)
71
+ end
72
+
73
+ teardown do
74
+ @migration.unstub(:write, :announce)
75
+ end
76
+
77
+ should 'not execute any queries immediately' do
78
+ assert_no_queries { @migration.change }
79
+ end
80
+
81
+ context 'with a working pt-online-schema-change' do
82
+ setup do
83
+ Kernel.expects(:system).with(regexp_matches(/^pt-online-schema-change/)).twice.returns(true)
84
+ end
85
+
86
+ teardown do
87
+ Kernel.unstub(:system)
88
+ end
89
+
90
+ should 'not directly execute any queries when migrating' do
91
+ assert_no_queries { @migration.migrate(:up) }
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,49 @@
1
+ # Configure Rails Environment
2
+ ENV['RAILS_ENV'] = 'test'
3
+
4
+ require File.expand_path('../dummy/config/environment.rb', __FILE__)
5
+ require 'test/unit'
6
+ require 'shoulda'
7
+ require 'faker'
8
+ require 'mocha'
9
+
10
+ Rails.backtrace_cleaner.remove_silencers!
11
+
12
+ def test_spec
13
+ test_spec = YAML.load_file(Rails.root.join(*%w(.. config database.yml)))['test']
14
+ test_spec['adapter'] = 'pt_osc'
15
+ test_spec
16
+ end
17
+
18
+ # SQLCounter is part of ActiveRecord but is not distributed with the gem (used for internal tests only)
19
+ # see https://github.com/rails/rails/blob/3-2-stable/activerecord/test/cases/helper.rb#L59
20
+ module ActiveRecord
21
+ class SQLCounter
22
+ cattr_accessor :ignored_sql
23
+ self.ignored_sql = [/^PRAGMA (?!(table_info))/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
24
+
25
+ # FIXME: this needs to be refactored so specific database can add their own
26
+ # ignored SQL. This ignored SQL is for Oracle.
27
+ ignored_sql.concat [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im]
28
+
29
+ cattr_accessor :log
30
+ self.log = []
31
+
32
+ attr_reader :ignore
33
+
34
+ def initialize(ignore = self.class.ignored_sql)
35
+ @ignore = ignore
36
+ end
37
+
38
+ def call(name, start, finish, message_id, values)
39
+ sql = values[:sql]
40
+
41
+ # FIXME: this seems bad. we should probably have a better way to indicate
42
+ # the query was cached
43
+ return if 'CACHE' == values[:name] || ignore.any? { |x| x =~ sql }
44
+ self.class.log << sql
45
+ end
46
+ end
47
+
48
+ ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new)
49
+ end
@@ -0,0 +1,209 @@
1
+ require 'test_helper'
2
+
3
+ class PtOscAdapterTest < Test::Unit::TestCase
4
+ class TestConnection < ActiveRecord::Base; end
5
+
6
+ context 'a pt-osc adapter' do
7
+ setup do
8
+ TestConnection.establish_connection(test_spec)
9
+ @adapter = TestConnection.connection
10
+ end
11
+
12
+ context '#rename_table' do
13
+ context 'with no existing commands' do
14
+ setup do
15
+ @adapter.instance_variable_set(:@osc_commands, nil)
16
+ end
17
+
18
+ should 'add a RENAME TO command to the commands hash' do
19
+ table_name = Faker::Lorem.word
20
+ new_table_name = Faker::Lorem.word
21
+ @adapter.rename_table(table_name, new_table_name)
22
+ assert_equal "RENAME TO `#{new_table_name}`", @adapter.send(:get_commands, table_name).first
23
+ end
24
+ end
25
+ end
26
+
27
+ context '#add_column' do
28
+ context 'with no existing commands' do
29
+ setup do
30
+ @adapter.instance_variable_set(:@osc_commands, nil)
31
+ end
32
+
33
+ should 'add an ADD command to the commands hash' do
34
+ table_name = Faker::Lorem.word
35
+ column_name = Faker::Lorem.word
36
+ @adapter.add_column(table_name, column_name, :string, default: 0, null: false)
37
+ assert_equal "ADD `#{column_name}` varchar(255) DEFAULT 0 NOT NULL", @adapter.send(:get_commands, table_name).first
38
+ end
39
+ end
40
+ end
41
+
42
+ context '#change_column' do
43
+ context 'with no existing commands' do
44
+ setup do
45
+ @adapter.instance_variable_set(:@osc_commands, nil)
46
+ end
47
+
48
+ context 'with an existing table and column' do
49
+ setup do
50
+ @table_name = Faker::Lorem.word
51
+ @column_name = Faker::Lorem.word
52
+ @adapter.create_table @table_name, force: true do |t|
53
+ t.string @column_name
54
+ end
55
+ end
56
+
57
+ should 'add a CHANGE command to the commands hash' do
58
+ @adapter.change_column(@table_name, @column_name, :string, default: 0, null: false)
59
+ assert_equal "CHANGE `#{@column_name}` `#{@column_name}` varchar(255) DEFAULT 0 NOT NULL", @adapter.send(:get_commands, @table_name).first
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ context '#rename_column' do
66
+ context 'with no existing commands' do
67
+ setup do
68
+ @adapter.instance_variable_set(:@osc_commands, nil)
69
+ end
70
+
71
+ context 'with an existing table and column' do
72
+ setup do
73
+ @table_name = Faker::Lorem.word
74
+ @column_name = Faker::Lorem.word
75
+ @adapter.create_table @table_name, force: true do |t|
76
+ t.string @column_name, default: nil
77
+ end
78
+ end
79
+
80
+ should 'add a CHANGE command to the commands hash' do
81
+ new_column_name = Faker::Lorem.word
82
+ @adapter.rename_column(@table_name, @column_name, new_column_name)
83
+ assert_equal "CHANGE `#{@column_name}` `#{new_column_name}` varchar(255) DEFAULT NULL", @adapter.send(:get_commands, @table_name).first
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ context '#remove_column' do
90
+ context 'with no existing commands' do
91
+ setup do
92
+ @adapter.instance_variable_set(:@osc_commands, nil)
93
+ end
94
+
95
+ should 'add a DROP COLUMN command to the commands hash' do
96
+ table_name = Faker::Lorem.word
97
+ column_name = Faker::Lorem.word
98
+ @adapter.remove_column(table_name, column_name)
99
+ assert_equal "DROP COLUMN `#{column_name}`", @adapter.send(:get_commands, table_name).first
100
+ end
101
+
102
+ should 'add multiple DROP COLUMN commands to the commands hash' do
103
+ table_name = Faker::Lorem.word
104
+ column_names = 3.times.map { Faker::Lorem.word }
105
+ @adapter.remove_column(table_name, *column_names)
106
+ commands = @adapter.send(:get_commands, table_name)
107
+ column_names.each_with_index do |column_name, index|
108
+ assert_equal "DROP COLUMN `#{column_name}`", commands[index]
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ context '#add_index' do
115
+ context 'with no existing commands' do
116
+ setup do
117
+ @adapter.instance_variable_set(:@osc_commands, nil)
118
+ end
119
+
120
+ context 'with an existing table and columns' do
121
+ setup do
122
+ @table_name = Faker::Lorem.word
123
+ @column_names = Faker::Lorem.words
124
+ @adapter.create_table @table_name, force: true do |t|
125
+ @column_names.each do |column_name|
126
+ t.string(column_name, default: nil)
127
+ end
128
+ end
129
+ end
130
+
131
+ should 'add an ADD INDEX command for one column to the commands hash' do
132
+ index_name = Faker::Lorem.words.join('_')
133
+ @adapter.add_index(@table_name, @column_names.first, name: index_name)
134
+ assert_equal "ADD INDEX `#{index_name}` (`#{@column_names.first}`)", @adapter.send(:get_commands, @table_name).first
135
+ end
136
+
137
+ should 'add an ADD UNIQUE INDEX command for one column to the commands hash' do
138
+ index_name = Faker::Lorem.words.join('_')
139
+ @adapter.add_index(@table_name, @column_names.first, unique: true, name: index_name)
140
+ assert_equal "ADD UNIQUE INDEX `#{index_name}` (`#{@column_names.first}`)", @adapter.send(:get_commands, @table_name).first
141
+ end
142
+
143
+ should 'add an ADD INDEX command for multiple columns to the commands hash' do
144
+ index_name = Faker::Lorem.words.join('_')
145
+ @adapter.add_index(@table_name, @column_names, name: index_name)
146
+ assert_equal "ADD INDEX `#{index_name}` (`#{@column_names.join('`, `')}`)", @adapter.send(:get_commands, @table_name).first
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ context '#remove_index!' do
153
+ context 'with no existing commands' do
154
+ setup do
155
+ @adapter.instance_variable_set(:@osc_commands, nil)
156
+ end
157
+
158
+ should 'add a DROP COLUMN command to the commands hash' do
159
+ table_name = Faker::Lorem.word
160
+ index_name = Faker::Lorem.word
161
+ @adapter.remove_index!(table_name, index_name)
162
+ assert_equal "DROP INDEX `#{index_name}`", @adapter.send(:get_commands, table_name).first
163
+ end
164
+ end
165
+ end
166
+
167
+ context '#add_command' do
168
+ context 'with no existing commands' do
169
+ setup do
170
+ @adapter.instance_variable_set(:@osc_commands, nil)
171
+ end
172
+
173
+ should 'add a command without initializing the array' do
174
+ table_name = Faker::Lorem.word
175
+ @adapter.send(:add_command, table_name, 'foo')
176
+ assert_kind_of Array, @adapter.send(:get_commands, table_name)
177
+ assert_equal 1, @adapter.send(:get_commands, table_name).size
178
+ assert_equal 'foo', @adapter.send(:get_commands, table_name).first
179
+ end
180
+ end
181
+ end
182
+
183
+ context '#get_commands' do
184
+ context 'with no existing commands' do
185
+ setup do
186
+ @adapter.instance_variable_set(:@osc_commands, nil)
187
+ end
188
+
189
+ should "return nil for a table that doesn't exist" do
190
+ table_name = Faker::Lorem.word
191
+ assert_nil @adapter.send(:get_commands, table_name)
192
+ end
193
+ end
194
+ end
195
+
196
+ context '#get_commands_string' do
197
+ context 'with no existing commands' do
198
+ setup do
199
+ @adapter.instance_variable_set(:@osc_commands, nil)
200
+ end
201
+
202
+ should "return an empty string for a table that doesn't exist" do
203
+ table_name = Faker::Lorem.word
204
+ assert_equal '', @adapter.send(:get_commands_string, table_name)
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end