ardm-migrations 1.2.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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +35 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +53 -0
  5. data/LICENSE +20 -0
  6. data/README.rdoc +39 -0
  7. data/Rakefile +4 -0
  8. data/ardm-migrations.gemspec +27 -0
  9. data/db/migrations/1_create_people_table.rb +12 -0
  10. data/db/migrations/2_add_dob_to_people.rb +13 -0
  11. data/db/migrations/config.rb +4 -0
  12. data/examples/Rakefile +144 -0
  13. data/examples/sample_migration.rb +58 -0
  14. data/examples/sample_migration_spec.rb +50 -0
  15. data/lib/ardm-migrations.rb +1 -0
  16. data/lib/dm-migrations/adapters/dm-do-adapter.rb +295 -0
  17. data/lib/dm-migrations/adapters/dm-mysql-adapter.rb +299 -0
  18. data/lib/dm-migrations/adapters/dm-oracle-adapter.rb +332 -0
  19. data/lib/dm-migrations/adapters/dm-postgres-adapter.rb +159 -0
  20. data/lib/dm-migrations/adapters/dm-sqlite-adapter.rb +96 -0
  21. data/lib/dm-migrations/adapters/dm-sqlserver-adapter.rb +177 -0
  22. data/lib/dm-migrations/adapters/dm-yaml-adapter.rb +23 -0
  23. data/lib/dm-migrations/auto_migration.rb +239 -0
  24. data/lib/dm-migrations/exceptions/duplicate_migration.rb +6 -0
  25. data/lib/dm-migrations/migration.rb +300 -0
  26. data/lib/dm-migrations/migration_runner.rb +85 -0
  27. data/lib/dm-migrations/sql/column.rb +5 -0
  28. data/lib/dm-migrations/sql/mysql.rb +61 -0
  29. data/lib/dm-migrations/sql/postgres.rb +82 -0
  30. data/lib/dm-migrations/sql/sqlite.rb +51 -0
  31. data/lib/dm-migrations/sql/table.rb +15 -0
  32. data/lib/dm-migrations/sql/table_creator.rb +109 -0
  33. data/lib/dm-migrations/sql/table_modifier.rb +57 -0
  34. data/lib/dm-migrations/sql.rb +5 -0
  35. data/lib/dm-migrations/version.rb +5 -0
  36. data/lib/dm-migrations.rb +3 -0
  37. data/lib/spec/example/migration_example_group.rb +73 -0
  38. data/lib/spec/matchers/migration_matchers.rb +106 -0
  39. data/spec/integration/auto_migration_spec.rb +553 -0
  40. data/spec/integration/auto_upgrade_spec.rb +40 -0
  41. data/spec/integration/migration_runner_spec.rb +89 -0
  42. data/spec/integration/migration_spec.rb +157 -0
  43. data/spec/integration/sql_spec.rb +250 -0
  44. data/spec/isolated/require_after_setup_spec.rb +30 -0
  45. data/spec/isolated/require_before_setup_spec.rb +30 -0
  46. data/spec/isolated/require_spec.rb +25 -0
  47. data/spec/rcov.opts +6 -0
  48. data/spec/spec.opts +4 -0
  49. data/spec/spec_helper.rb +18 -0
  50. data/spec/unit/migration_spec.rb +453 -0
  51. data/spec/unit/sql/column_spec.rb +14 -0
  52. data/spec/unit/sql/postgres_spec.rb +97 -0
  53. data/spec/unit/sql/sqlite_extensions_spec.rb +108 -0
  54. data/spec/unit/sql/table_creator_spec.rb +94 -0
  55. data/spec/unit/sql/table_modifier_spec.rb +49 -0
  56. data/spec/unit/sql/table_spec.rb +28 -0
  57. data/spec/unit/sql_spec.rb +7 -0
  58. data/tasks/spec.rake +38 -0
  59. data/tasks/yard.rake +9 -0
  60. data/tasks/yardstick.rake +19 -0
  61. metadata +150 -0
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+
3
+ describe "A Migration" do
4
+
5
+ supported_by :postgres, :mysql, :sqlite, :oracle, :sqlserver do
6
+
7
+ describe DataMapper::Migration, 'interface' do
8
+
9
+ before(:all) do
10
+ @adapter = DataMapper::Spec.adapter
11
+ end
12
+
13
+ before do
14
+ @migration = DataMapper::Migration.new(1, :create_people_table, :verbose => false) { }
15
+ end
16
+
17
+ before do
18
+ $stderr, @original = StringIO.new, $stderr
19
+ end
20
+
21
+ after do
22
+ $stderr = @original
23
+ end
24
+
25
+ it "should have a postition attribute" do
26
+ @migration.should respond_to(:position)
27
+
28
+ @migration.position.should == 1
29
+ end
30
+
31
+ it "should have a name attribute" do
32
+ @migration.should respond_to(:name)
33
+
34
+ @migration.name.should == :create_people_table
35
+ end
36
+
37
+ it "should have a :repository option" do
38
+ m = DataMapper::Migration.new(2, :create_dogs_table, :repository => :alternate) {}
39
+
40
+ m.instance_variable_get(:@repository).should == :alternate
41
+ end
42
+
43
+ it "should use the default repository by default" do
44
+ @migration.instance_variable_get(:@repository).should == :default
45
+ end
46
+
47
+ it "should still support a :database option" do
48
+ m = DataMapper::Migration.new(2, :create_legacy_table, :database => :legacy) {}
49
+
50
+ m.instance_variable_get(:@repository).should == :legacy
51
+ end
52
+
53
+ it "warns when :database is used" do
54
+ m = DataMapper::Migration.new(2, :create_legacy_table, :database => :legacy) {}
55
+ $stderr.string.chomp.should == 'Using the :database option with migrations is deprecated, use :repository instead'
56
+ end
57
+
58
+ it "should have a verbose option" do
59
+ m = DataMapper::Migration.new(2, :create_dogs_table, :verbose => false) {}
60
+ m.instance_variable_get(:@verbose).should == false
61
+ end
62
+
63
+ it "should be verbose by default" do
64
+ m = DataMapper::Migration.new(2, :create_dogs_table) {}
65
+ m.instance_variable_get(:@verbose).should == true
66
+ end
67
+
68
+ it "should be sortable, first by position, then name" do
69
+ m1 = DataMapper::Migration.new(1, :create_people_table) {}
70
+ m2 = DataMapper::Migration.new(2, :create_dogs_table) {}
71
+ m3 = DataMapper::Migration.new(2, :create_cats_table) {}
72
+ m4 = DataMapper::Migration.new(4, :create_birds_table) {}
73
+
74
+ [m1, m2, m3, m4].sort.should == [m1, m3, m2, m4]
75
+ end
76
+
77
+ adapter = DataMapper::Spec.adapter_name
78
+
79
+ expected_module_lambda = {
80
+ :sqlite => lambda { SQL::Sqlite },
81
+ :mysql => lambda { SQL::Mysql },
82
+ :postgres => lambda { SQL::Postgres }
83
+ }[adapter.to_sym]
84
+
85
+ expected_module = expected_module_lambda ? expected_module_lambda.call : nil
86
+
87
+ if expected_module
88
+ it "should extend with #{expected_module} when adapter is #{adapter}" do
89
+ migration = DataMapper::Migration.new(1, :"#{adapter}_adapter_test") { }
90
+ (class << migration.adapter; self; end).included_modules.should include(expected_module)
91
+ end
92
+ end
93
+ end
94
+
95
+ describe DataMapper::Migration, 'defining actions' do
96
+ before do
97
+ @migration = DataMapper::Migration.new(1, :create_people_table, :verbose => false) { }
98
+ end
99
+
100
+ it "should have an #up method" do
101
+ @migration.should respond_to(:up)
102
+ end
103
+
104
+ it "should save the block passed into the #up method in @up_action" do
105
+ action = lambda {}
106
+ @migration.up(&action)
107
+
108
+ @migration.instance_variable_get(:@up_action).should == action
109
+ end
110
+
111
+ it "should have a #down method" do
112
+ @migration.should respond_to(:down)
113
+ end
114
+
115
+ it "should save the block passed into the #down method in @down_action" do
116
+ action = lambda {}
117
+ @migration.down(&action)
118
+
119
+ @migration.instance_variable_get(:@down_action).should == action
120
+ end
121
+
122
+ it "should make available an #execute method" do
123
+ @migration.should respond_to(:execute)
124
+ end
125
+
126
+ it "should run the sql passed into the #execute method"
127
+ # TODO: Find out how to stub the DataMapper::database.execute method
128
+ end
129
+
130
+ describe DataMapper::Migration, "output" do
131
+ before do
132
+ @migration = DataMapper::Migration.new(1, :create_people_table) { }
133
+ @migration.stub!(:write) # so that we don't actually write anything to the console!
134
+ end
135
+
136
+ it "should #say a string with an indent" do
137
+ @migration.should_receive(:write).with(" Foobar")
138
+ @migration.say("Foobar", 2)
139
+ end
140
+
141
+ it "should #say with a default indent of 4" do
142
+ @migration.should_receive(:write).with(" Foobar")
143
+ @migration.say("Foobar")
144
+ end
145
+
146
+ it "should #say_with_time the running time of a block" do
147
+ @migration.should_receive(:write).with(/Block/)
148
+ @migration.should_receive(:write).with(/-> [\d]+/)
149
+
150
+ @migration.say_with_time("Block"){ }
151
+ end
152
+
153
+ end
154
+
155
+ end
156
+
157
+ end
@@ -0,0 +1,250 @@
1
+ require 'spec_helper'
2
+
3
+ describe "SQL generation" do
4
+
5
+ supported_by :postgres, :mysql, :sqlite, :oracle, :sqlserver do
6
+
7
+ describe DataMapper::Migration, "#create_table helper" do
8
+ before :all do
9
+
10
+ @adapter = DataMapper::Spec.adapter
11
+ @repository = DataMapper.repository(@adapter.name)
12
+
13
+ case DataMapper::Spec.adapter_name.to_sym
14
+ when :sqlite then @adapter.extend(SQL::Sqlite)
15
+ when :mysql then @adapter.extend(SQL::Mysql)
16
+ when :postgres then @adapter.extend(SQL::Postgres)
17
+ end
18
+
19
+ end
20
+
21
+ before do
22
+ @creator = DataMapper::Migration::TableCreator.new(@adapter, :people) do
23
+ column :id, DataMapper::Property::Serial
24
+ column :name, 'VARCHAR(50)', :allow_nil => false
25
+ column :long_string, String, :size => 200
26
+ end
27
+ end
28
+
29
+ it "should have a #create_table helper" do
30
+ @migration = DataMapper::Migration.new(1, :create_people_table, :verbose => false) { }
31
+ @migration.should respond_to(:create_table)
32
+ end
33
+
34
+ it "should have a table_name" do
35
+ @creator.table_name.should == "people"
36
+ end
37
+
38
+ it "should have an adapter" do
39
+ @creator.instance_eval("@adapter").should == @adapter
40
+ end
41
+
42
+ it "should have an options hash" do
43
+ @creator.opts.should be_kind_of(Hash)
44
+ @creator.opts.should == {}
45
+ end
46
+
47
+ it "should have an array of columns" do
48
+ @creator.instance_eval("@columns").should be_kind_of(Array)
49
+ @creator.instance_eval("@columns").size.should == 3
50
+ @creator.instance_eval("@columns").first.should be_kind_of(DataMapper::Migration::TableCreator::Column)
51
+ end
52
+
53
+ it "should quote the table name for the adapter" do
54
+ @creator.quoted_table_name.should == (DataMapper::Spec.adapter_name.to_sym == :mysql ? '`people`' : '"people"')
55
+ end
56
+
57
+ it "should allow for custom options" do
58
+ columns = @creator.instance_eval("@columns")
59
+ col = columns.detect{|c| c.name == "long_string"}
60
+ col.instance_eval("@type").should include("200")
61
+ end
62
+
63
+ it "should generate a NOT NULL column when :allow_nil is false" do
64
+ @creator.instance_eval("@columns")[1].type.should match(/NOT NULL/)
65
+ end
66
+
67
+ case DataMapper::Spec.adapter_name.to_sym
68
+ when :mysql
69
+ it "should create an InnoDB database for MySQL" do
70
+ #can't get an exact == comparison here because character set and collation may differ per connection
71
+ @creator.to_sql.should match(/^CREATE TABLE `people` \(`id` SERIAL PRIMARY KEY, `name` VARCHAR\(50\) NOT NULL, `long_string` VARCHAR\(200\)\) ENGINE = InnoDB CHARACTER SET \w+ COLLATE \w+\z/)
72
+ end
73
+
74
+ it "should allow for custom table creation options for MySQL" do
75
+ opts = {
76
+ :storage_engine => 'MyISAM',
77
+ :character_set => 'big5',
78
+ :collation => 'big5_chinese_ci',
79
+ }
80
+
81
+ creator = DataMapper::Migration::TableCreator.new(@adapter, :people, opts) do
82
+ column :id, DataMapper::Property::Serial
83
+ end
84
+
85
+ creator.to_sql.should match(/^CREATE TABLE `people` \(`id` SERIAL PRIMARY KEY\) ENGINE = MyISAM CHARACTER SET big5 COLLATE big5_chinese_ci\z/)
86
+ end
87
+
88
+ it "should respect default storage engine types specified by the MySQL adapter" do
89
+ adapter = DataMapper::Spec.adapter
90
+ adapter.extend(SQL::Mysql)
91
+
92
+ adapter.storage_engine = 'MyISAM'
93
+
94
+ creator = DataMapper::Migration::TableCreator.new(adapter, :people) do
95
+ column :id, DataMapper::Property::Serial
96
+ end
97
+
98
+ creator.to_sql.should match(/^CREATE TABLE `people` \(`id` SERIAL PRIMARY KEY\) ENGINE = MyISAM CHARACTER SET \w+ COLLATE \w+\z/)
99
+ end
100
+
101
+ when :postgres
102
+ it "should output a CREATE TABLE statement when sent #to_sql" do
103
+ @creator.to_sql.should == %q{CREATE TABLE "people" ("id" SERIAL PRIMARY KEY, "name" VARCHAR(50) NOT NULL, "long_string" VARCHAR(200))}
104
+ end
105
+ when :sqlite3
106
+ it "should output a CREATE TABLE statement when sent #to_sql" do
107
+ @creator.to_sql.should == %q{CREATE TABLE "people" ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "name" VARCHAR(50) NOT NULL, "long_string" VARCHAR(200))}
108
+ end
109
+ end
110
+
111
+ context 'when the default string length is modified' do
112
+ before do
113
+ @original = DataMapper::Property::String.length
114
+ DataMapper::Property::String.length(255)
115
+
116
+ @creator = DataMapper::Migration::TableCreator.new(@adapter, :people) do
117
+ column :string, String
118
+ end
119
+ end
120
+
121
+ after do
122
+ DataMapper::Property::String.length(@original)
123
+ end
124
+
125
+ it 'uses the new length for the character column' do
126
+ @creator.to_sql.should match(/CHAR\(255\)/)
127
+ end
128
+ end
129
+ end
130
+
131
+ describe DataMapper::Migration, "#modify_table helper" do
132
+ before do
133
+ @migration = DataMapper::Migration.new(1, :create_people_table, :verbose => false) { }
134
+
135
+ end
136
+
137
+ it "should have a #modify_table helper" do
138
+ @migration.should respond_to(:modify_table)
139
+ end
140
+
141
+ case DataMapper::Spec.adapter_name.to_sym
142
+ when :postgres
143
+ before do
144
+ @modifier = DataMapper::Migration::TableModifier.new(@adapter, :people) do
145
+ change_column :name, 'VARCHAR(200)'
146
+ end
147
+ end
148
+
149
+ it "should alter the column" do
150
+ @modifier.to_sql.should == %q{ALTER TABLE "people" ALTER COLUMN "name" VARCHAR(200)}
151
+ end
152
+ end
153
+ end
154
+
155
+ describe DataMapper::Migration, "other helpers" do
156
+ before do
157
+ @migration = DataMapper::Migration.new(1, :create_people_table, :verbose => false) { }
158
+ end
159
+
160
+ it "should have a #drop_table helper" do
161
+ @migration.should respond_to(:drop_table)
162
+ end
163
+
164
+ end
165
+
166
+ describe DataMapper::Migration, "version tracking" do
167
+ before(:each) do
168
+ @migration = DataMapper::Migration.new(1, :create_people_table, :verbose => false) do
169
+ up { :ran_up }
170
+ down { :ran_down }
171
+ end
172
+
173
+ @migration.send(:create_migration_info_table_if_needed)
174
+ end
175
+
176
+ after(:each) { DataMapper::Spec.adapter.execute("DROP TABLE migration_info") rescue nil }
177
+
178
+ def insert_migration_record
179
+ DataMapper::Spec.adapter.execute("INSERT INTO migration_info (migration_name) VALUES ('create_people_table')")
180
+ end
181
+
182
+ it "should know if the migration_info table exists" do
183
+ @migration.send(:migration_info_table_exists?).should be(true)
184
+ end
185
+
186
+ it "should know if the migration_info table does not exist" do
187
+ DataMapper::Spec.adapter.execute("DROP TABLE migration_info") rescue nil
188
+ @migration.send(:migration_info_table_exists?).should be(false)
189
+ end
190
+
191
+ it "should be able to find the migration_info record for itself" do
192
+ insert_migration_record
193
+ @migration.send(:migration_record).should_not be_empty
194
+ end
195
+
196
+ it "should know if a migration needs_up?" do
197
+ @migration.send(:needs_up?).should be(true)
198
+ insert_migration_record
199
+ @migration.send(:needs_up?).should be(false)
200
+ end
201
+
202
+ it "should know if a migration needs_down?" do
203
+ @migration.send(:needs_down?).should be(false)
204
+ insert_migration_record
205
+ @migration.send(:needs_down?).should be(true)
206
+ end
207
+
208
+ it "should properly quote the migration_info table via the adapter for use in queries" do
209
+ @migration.send(:migration_info_table).should == @migration.quote_table_name("migration_info")
210
+ end
211
+
212
+ it "should properly quote the migration_info.migration_name column via the adapter for use in queries" do
213
+ @migration.send(:migration_name_column).should == @migration.quote_column_name("migration_name")
214
+ end
215
+
216
+ it "should properly quote the migration's name for use in queries"
217
+ # TODO how to i call the adapter's #escape_sql method?
218
+
219
+ it "should create the migration_info table if it doesn't exist" do
220
+ DataMapper::Spec.adapter.execute("DROP TABLE migration_info")
221
+ @migration.send(:migration_info_table_exists?).should be(false)
222
+ @migration.send(:create_migration_info_table_if_needed)
223
+ @migration.send(:migration_info_table_exists?).should be(true)
224
+ end
225
+
226
+ it "should insert a record into the migration_info table on up" do
227
+ @migration.send(:migration_record).should be_empty
228
+ @migration.perform_up.should == :ran_up
229
+ @migration.send(:migration_record).should_not be_empty
230
+ end
231
+
232
+ it "should remove a record from the migration_info table on down" do
233
+ insert_migration_record
234
+ @migration.send(:migration_record).should_not be_empty
235
+ @migration.perform_down.should == :ran_down
236
+ @migration.send(:migration_record).should be_empty
237
+ end
238
+
239
+ it "should not run the up action if the record exists in the table" do
240
+ insert_migration_record
241
+ @migration.perform_up.should_not == :ran_up
242
+ end
243
+
244
+ it "should not run the down action if the record does not exist in the table" do
245
+ @migration.perform_down.should_not == :ran_down
246
+ end
247
+
248
+ end
249
+ end
250
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec'
2
+ require 'isolated/require_spec'
3
+ require 'dm-core/spec/setup'
4
+
5
+ # To really test this behavior, this spec needs to be run in isolation and not
6
+ # as part of the typical rake spec run, which requires dm-transactions upfront
7
+
8
+ if %w[ postgres mysql sqlite oracle sqlserver ].include?(ENV['ADAPTER'])
9
+
10
+ describe "require 'dm-migrations' after calling DataMapper.setup" do
11
+
12
+ before(:all) do
13
+
14
+ @adapter = DataMapper::Spec.adapter
15
+ require 'dm-migrations'
16
+
17
+ class ::Person
18
+ include DataMapper::Resource
19
+ property :id, Serial
20
+ end
21
+
22
+ @model = Person
23
+
24
+ end
25
+
26
+ it_should_behave_like "require 'dm-migrations'"
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec'
2
+ require 'isolated/require_spec'
3
+ require 'dm-core/spec/setup'
4
+
5
+ # To really test this behavior, this spec needs to be run in isolation and not
6
+ # as part of the typical rake spec run, which requires dm-transactions upfront
7
+
8
+ if %w[ postgres mysql sqlite oracle sqlserver ].include?(ENV['ADAPTER'])
9
+
10
+ describe "require 'dm-migrations' before calling DataMapper.setup" do
11
+
12
+ before(:all) do
13
+
14
+ require 'dm-migrations'
15
+ @adapter = DataMapper::Spec.adapter
16
+
17
+ class ::Person
18
+ include DataMapper::Resource
19
+ property :id, Serial
20
+ end
21
+
22
+ @model = Person
23
+
24
+ end
25
+
26
+ it_should_behave_like "require 'dm-migrations'"
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,25 @@
1
+ shared_examples_for "require 'dm-migrations'" do
2
+
3
+ it "should include the migration api in the DataMapper namespace" do
4
+ DataMapper.respond_to?(:migrate! ).should be(true)
5
+ DataMapper.respond_to?(:auto_migrate! ).should be(true)
6
+ DataMapper.respond_to?(:auto_upgrade! ).should be(true)
7
+ DataMapper.respond_to?(:auto_migrate_up!, true).should be(true)
8
+ DataMapper.respond_to?(:auto_migrate_down!, true).should be(true)
9
+ end
10
+
11
+ %w[ Repository Model ].each do |name|
12
+ it "should include the migration api in DataMapper::#{name}" do
13
+ (DataMapper.const_get(name) < DataMapper::Migrations.const_get(name)).should be(true)
14
+ end
15
+ end
16
+
17
+ it "should include the migration api into the adapter" do
18
+ @adapter.respond_to?(:storage_exists? ).should be(true)
19
+ @adapter.respond_to?(:field_exists? ).should be(true)
20
+ @adapter.respond_to?(:upgrade_model_storage).should be(true)
21
+ @adapter.respond_to?(:create_model_storage ).should be(true)
22
+ @adapter.respond_to?(:destroy_model_storage).should be(true)
23
+ end
24
+
25
+ end
data/spec/rcov.opts ADDED
@@ -0,0 +1,6 @@
1
+ --exclude "spec,^/"
2
+ --sort coverage
3
+ --callsites
4
+ --xrefs
5
+ --profile
6
+ --text-summary
data/spec/spec.opts ADDED
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --loadby random
3
+ --format profile
4
+ --backtrace
@@ -0,0 +1,18 @@
1
+ require 'dm-migrations'
2
+ require 'dm-migrations/migration_runner'
3
+
4
+ require 'dm-core/spec/setup'
5
+ require 'dm-core/spec/lib/adapter_helpers'
6
+ require 'dm-core/spec/lib/spec_helper'
7
+ require 'dm-core/spec/lib/pending_helpers'
8
+
9
+ Spec::Runner.configure do |config|
10
+
11
+ config.extend(DataMapper::Spec::Adapters::Helpers)
12
+ config.include(DataMapper::Spec::PendingHelpers)
13
+
14
+ config.after :all do
15
+ DataMapper::Spec.cleanup_models
16
+ end
17
+
18
+ end