molo 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ schema.rb
2
+ spec/tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,33 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ Molo (0.4.0)
5
+ activerecord (~> 3.0.3)
6
+ rake (~> 0.8)
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ activemodel (3.0.3)
12
+ activesupport (= 3.0.3)
13
+ builder (~> 2.1.2)
14
+ i18n (~> 0.4)
15
+ activerecord (3.0.3)
16
+ activemodel (= 3.0.3)
17
+ activesupport (= 3.0.3)
18
+ arel (~> 2.0.2)
19
+ tzinfo (~> 0.3.23)
20
+ activesupport (3.0.3)
21
+ arel (2.0.6)
22
+ builder (2.1.2)
23
+ i18n (0.5.0)
24
+ rake (0.8.7)
25
+ tzinfo (0.3.23)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ Molo!
32
+ activerecord (~> 3.0.3)
33
+ rake (~> 0.8)
data/Molo.gemspec ADDED
@@ -0,0 +1,57 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{molo}
8
+ s.version = "0.5.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Joel Moss", "Todd Huss", "Michael Grosser"]
12
+ s.date = %q{2010-12-13}
13
+ s.email = %q{joel@developwithstyle.com}
14
+ s.extra_rdoc_files = [
15
+ "README.markdown"
16
+ ]
17
+ s.files = [
18
+ ".gitignore",
19
+ "Gemfile",
20
+ "Gemfile.lock",
21
+ "Molo.gemspec",
22
+ "README.markdown",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "lib/tasks/molo.rb",
26
+ "spec/molo_spec.rb",
27
+ "vendor/migration_helpers/MIT-LICENSE",
28
+ "vendor/migration_helpers/README.markdown",
29
+ "vendor/migration_helpers/init.rb",
30
+ "vendor/migration_helpers/lib/migration_helper.rb"
31
+ ]
32
+ s.homepage = %q{http://codaset.com/joelmoss/molo}
33
+ s.rdoc_options = ["--charset=UTF-8"]
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = %q{1.3.7}
36
+ s.summary = %q{A thin wrapper to use Rails Migrations in non Rails projects}
37
+ s.test_files = [
38
+ "spec/molo_spec.rb"
39
+ ]
40
+
41
+ if s.respond_to? :specification_version then
42
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
43
+ s.specification_version = 3
44
+
45
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
46
+ s.add_runtime_dependency(%q<activerecord>, ["~> 3.0.3"])
47
+ s.add_runtime_dependency(%q<rake>, ["~> 0.8"])
48
+ else
49
+ s.add_dependency(%q<activerecord>, ["~> 3.0.3"])
50
+ s.add_dependency(%q<rake>, ["~> 0.8"])
51
+ end
52
+ else
53
+ s.add_dependency(%q<activerecord>, ["~> 3.0.3"])
54
+ s.add_dependency(%q<rake>, ["~> 0.8"])
55
+ end
56
+ end
57
+
data/README.markdown ADDED
@@ -0,0 +1,92 @@
1
+ Molo
2
+ ====
3
+
4
+ Rails migrations in non-Rails (and non Ruby) projects.
5
+
6
+ Install Ruby, RubyGems and a ruby-database driver (e.g. `gem install mysql`) then:
7
+
8
+ sudo gem install molo
9
+
10
+ Add to `Rakefile` in your projects base directory:
11
+
12
+ begin
13
+ require 'tasks/molo'
14
+ MigratorTasks.new do |t|
15
+ # t.migrations = "db/migrations"
16
+ # t.config = "db/config.yml"
17
+ # t.schema = "db/schema.rb"
18
+ # t.env = "DB"
19
+ # t.default_env = "development"
20
+ # t.verbose = true
21
+ # t.log_level = Logger::ERROR
22
+ end
23
+ rescue LoadError => e
24
+ puts "gem install molo to get db:migrate:* tasks! (Error: #{e})"
25
+ end
26
+
27
+ Add database configuration to `db/config.yml` in your projects base directory e.g.:
28
+
29
+ development:
30
+ adapter: sqlite3
31
+ database: db/development.sqlite3
32
+ pool: 5
33
+ timeout: 5000
34
+
35
+ production:
36
+ adapter: mysql
37
+ encoding: utf8
38
+ reconnect: false
39
+ database: somedatabase_dev
40
+ pool: 5
41
+ username: root
42
+ password:
43
+ socket: /var/run/mysqld/mysqld.sock
44
+
45
+ test: &test
46
+ adapter: sqlite3
47
+ database: db/test.sqlite3
48
+ pool: 5
49
+ timeout: 5000
50
+
51
+ ### To create a new database migration:
52
+
53
+ rake db:new_migration name=FooBarMigration
54
+ edit db/migrations/20081220234130_foo_bar_migration.rb
55
+
56
+ ... and fill in the up and down migrations [Cheatsheet](http://dizzy.co.uk/ruby_on_rails/cheatsheets/rails-migrations).
57
+
58
+ If you're lazy and want to just execute raw SQL:
59
+
60
+ def self.up
61
+ execute "insert into foo values (123,'something');"
62
+ end
63
+
64
+ def self.down
65
+ execute "delete from foo where field='something';"
66
+ end
67
+
68
+ ### To apply your newest migration:
69
+
70
+ rake db:migrate
71
+
72
+ ### To migrate to a specific version (for example to rollback)
73
+
74
+ rake db:migrate VERSION=20081220234130
75
+
76
+ ### To migrate a specific database (for example your "testing" database)
77
+
78
+ rake db:migrate DB=test
79
+
80
+ ### To execute a specific up/down of one single migration
81
+
82
+ rake db:migrate:up VERSION=20081220234130
83
+
84
+ Contributors
85
+ ============
86
+ This work is based on [Lincoln Stoll's blog post](http://lstoll.net/2008/04/stand-alone-activerecord-migrations/) and [David Welton's post](http://journal.dedasys.com/2007/01/28/using-migrations-outside-of-rails).
87
+
88
+ - [Joel Moss](http://developwithstyle.com/)
89
+ - [Todd Huss](http://gabrito.com/)
90
+ - [Michael Grosser](http://pragmatig.wordpress.com)
91
+ - [Eric Lindvall](http://bitmonkey.net)
92
+ - [Steve Hodgkiss](http://stevehodgkiss.com/)
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ task :default => :spec
2
+ require 'spec/rake/spectask'
3
+ Spec::Rake::SpecTask.new {|t| t.spec_opts = ['--color']}
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gem|
8
+ gem.name = 'molo'
9
+ gem.summary = "A thin wrapper to use Rails Migrations in non Rails projects"
10
+ gem.email = "joel@developwithstyle.com"
11
+ gem.homepage = "http://codaset.com/joelmoss/molo"
12
+ gem.authors = ["Joel Moss","Todd Huss", "Michael Grosser"]
13
+ gem.files += ["lib/tasks/*"]
14
+ gem.add_dependency "activerecord", "~> 3.0.3"
15
+ gem.add_dependency "rake", "~> 0.8"
16
+ end
17
+
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install jeweler"
21
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.5.0
data/lib/tasks/molo.rb ADDED
@@ -0,0 +1,279 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+ require 'logger'
4
+
5
+ class MigratorTasks < ::Rake::TaskLib
6
+ attr_accessor :name, :base, :vendor, :config, :schema, :env, :default_env, :verbose, :log_level, :logger
7
+ attr_reader :migrations
8
+
9
+ def initialize(name = :migrator)
10
+ @name = name
11
+ base = File.expand_path('.')
12
+ here = File.expand_path(File.dirname(File.dirname(File.dirname((__FILE__)))))
13
+ @base = base
14
+ @vendor = "#{here}/vendor"
15
+ @migrations = ["#{base}/db/migrations"]
16
+ @config = "#{base}/db/config.yml"
17
+ @schema = "#{base}/db/schema.rb"
18
+ @env = 'DB'
19
+ @default_env = 'development'
20
+ @verbose = true
21
+ @log_level = Logger::ERROR
22
+ yield self if block_given?
23
+ # Add to load_path every "lib/" directory in vendor
24
+ Dir["#{vendor}/**/lib"].each{|p| $LOAD_PATH << p }
25
+ define
26
+ end
27
+
28
+ def migrations=(*value)
29
+ @migrations = value.flatten
30
+ end
31
+
32
+ def define
33
+ namespace :db do
34
+ task :ar_init do
35
+ require 'active_record'
36
+ ENV[@env] ||= @default_env
37
+
38
+ require 'erb'
39
+
40
+ if @config.is_a?(Hash)
41
+ ActiveRecord::Base.configurations = @config
42
+ else
43
+ ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read(@config)).result)
44
+ end
45
+ ActiveRecord::Base.establish_connection(ENV[@env])
46
+ if @logger
47
+ logger = @logger
48
+ else
49
+ logger = Logger.new($stderr)
50
+ logger.level = @log_level
51
+ end
52
+ ActiveRecord::Base.logger = logger
53
+ end
54
+
55
+ desc "Migrate the database using the scripts in the migrations directory. Target specific version with VERSION=x. Turn off output with VERBOSE=false."
56
+ task :migrate => :ar_init do
57
+ require "#{@vendor}/migration_helpers/init"
58
+ ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
59
+ @migrations.each do |path|
60
+ ActiveRecord::Migrator.migrate(path, ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
61
+ end
62
+ Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
63
+ end
64
+
65
+ namespace :migrate do
66
+ desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
67
+ task :redo => :ar_init do
68
+ if ENV["VERSION"]
69
+ Rake::Task["db:migrate:down"].invoke
70
+ Rake::Task["db:migrate:up"].invoke
71
+ else
72
+ Rake::Task["db:rollback"].invoke
73
+ Rake::Task["db:migrate"].invoke
74
+ end
75
+ end
76
+
77
+ desc 'Runs the "up" for a given migration VERSION.'
78
+ task :up => :ar_init do
79
+ ActiveRecord::Migration.verbose = @verbose
80
+ version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
81
+ raise "VERSION is required" unless version
82
+
83
+ migration_path = nil
84
+ if @migrations.length == 1
85
+ migration_path = @migrations.first
86
+ else
87
+ @migrations.each do |path|
88
+ Dir[File.join(path, '*.rb')].each do |file|
89
+ if File.basename(file).match(/^\d+/)[0] == version.to_s
90
+ migration_path = path
91
+ break
92
+ end
93
+ end
94
+ end
95
+ raise "Migration #{version} wasn't found on paths #{@migrations.join(', ')}" if migration_path.nil?
96
+ end
97
+
98
+ ActiveRecord::Migrator.run(:up, migration_path, version)
99
+ Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
100
+ end
101
+
102
+ desc 'Runs the "down" for a given migration VERSION.'
103
+ task :down => :ar_init do
104
+ ActiveRecord::Migration.verbose = @verbose
105
+ version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
106
+ raise "VERSION is required" unless version
107
+
108
+ migration_path = nil
109
+ if @migrations.length == 1
110
+ migration_path = @migrations.first
111
+ else
112
+ @migrations.each do |path|
113
+ Dir[File.join(path, '*.rb')].each do |file|
114
+ if File.basename(file).match(/^\d+/)[0] == version.to_s
115
+ migration_path = path
116
+ break
117
+ end
118
+ end
119
+ end
120
+ raise "Migration #{version} wasn't found on paths #{@migrations.join(', ')}" if migration_path.nil?
121
+ end
122
+
123
+ ActiveRecord::Migrator.run(:down, migration_path, version)
124
+ Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
125
+ end
126
+
127
+ desc "Display status of migrations"
128
+ task :status => :ar_init do
129
+ config = ActiveRecord::Base.configurations[ENV[@env]]
130
+ ActiveRecord::Base.establish_connection(config)
131
+ unless ActiveRecord::Base.connection.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
132
+ puts 'Schema migrations table does not exist yet.'
133
+ next # means "return" for rake task
134
+ end
135
+ db_list = ActiveRecord::Base.connection.select_values("SELECT version FROM #{ActiveRecord::Migrator.schema_migrations_table_name}")
136
+ file_list = []
137
+ @migrations.each do |dir|
138
+ Dir.foreach(dir) do |file|
139
+ # only files matching "20091231235959_some_name.rb" pattern
140
+ if match_data = /(\d{14})_(.+)\.rb/.match(file)
141
+ status = db_list.delete(match_data[1]) ? 'up' : 'down'
142
+ file_list << [status, match_data[1], match_data[2]]
143
+ end
144
+ end
145
+ end
146
+ # output
147
+ puts "\ndatabase: #{config['database']}\n\n"
148
+ puts "#{"Status".center(8)} #{"Migration ID".ljust(14)} Migration Name"
149
+ puts "-" * 50
150
+ file_list.each do |file|
151
+ puts "#{file[0].center(8)} #{file[1].ljust(14)} #{file[2].humanize}"
152
+ end
153
+ db_list.each do |version|
154
+ puts "#{'up'.center(8)} #{version.ljust(14)} *** NO FILE ***"
155
+ end
156
+ puts
157
+ end
158
+ end
159
+
160
+ desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
161
+ task :rollback => :ar_init do
162
+ step = ENV['STEP'] ? ENV['STEP'].to_i : 1
163
+ @migrations.each do |path|
164
+ ActiveRecord::Migrator.rollback(path, step)
165
+ end
166
+ Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
167
+ end
168
+
169
+ desc "Retrieves the current schema version number"
170
+ task :version => :ar_init do
171
+ puts "Current version: #{ActiveRecord::Migrator.current_version}"
172
+ end
173
+
174
+ desc "Raises an error if there are pending migrations"
175
+ task :abort_if_pending_migrations => :ar_init do
176
+ @migrations.each do |path|
177
+ pending_migrations = ActiveRecord::Migrator.new(:up, path).pending_migrations
178
+
179
+ if pending_migrations.any?
180
+ puts "You have #{pending_migrations.size} pending migrations:"
181
+ pending_migrations.each do |pending_migration|
182
+ puts ' %4d %s' % [pending_migration.version, pending_migration.name]
183
+ end
184
+ abort %{Run "rake db:migrate" to update your database then try again.}
185
+ end
186
+ end
187
+ end
188
+
189
+ namespace :schema do
190
+ desc "Create schema.rb file that can be portably used against any DB supported by AR"
191
+ task :dump => :ar_init do
192
+ if schema_file = ENV['SCHEMA'] || @schema
193
+ require 'active_record/schema_dumper'
194
+ File.open(schema_file, "w") do |file|
195
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
196
+ end
197
+ end
198
+ end
199
+
200
+ desc "Load a ar_schema.rb file into the database"
201
+ task :load => :ar_init do
202
+ file = ENV['SCHEMA'] || @schema
203
+ load(file)
204
+ end
205
+ end
206
+
207
+ namespace :test do
208
+ desc "Recreate the test database from the current schema.rb"
209
+ task :load => ['db:ar_init', 'db:test:purge'] do
210
+ ActiveRecord::Base.establish_connection(:test)
211
+ ActiveRecord::Schema.verbose = false
212
+ Rake::Task["db:schema:load"].invoke
213
+ end
214
+
215
+ desc "Empty the test database"
216
+ task :purge => 'db:ar_init' do
217
+ config = ActiveRecord::Base.configurations['test']
218
+ case config["adapter"]
219
+ when "mysql"
220
+ ActiveRecord::Base.establish_connection(:test)
221
+ ActiveRecord::Base.connection.recreate_database(config["database"], config)
222
+ when "postgresql" #TODO i doubt this will work <-> methods are not defined
223
+ ActiveRecord::Base.clear_active_connections!
224
+ drop_database(config)
225
+ create_database(config)
226
+ when "sqlite", "sqlite3"
227
+ db_file = config["database"] || config["dbfile"]
228
+ File.delete(db_file) if File.exist?(db_file)
229
+ when "sqlserver"
230
+ drop_script = "#{config["host"]}.#{config["database"]}.DP1".gsub(/\\/,'-')
231
+ `osql -E -S #{config["host"]} -d #{config["database"]} -i db\\#{drop_script}`
232
+ `osql -E -S #{config["host"]} -d #{config["database"]} -i db\\test_structure.sql`
233
+ when "oci", "oracle"
234
+ ActiveRecord::Base.establish_connection(:test)
235
+ ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl|
236
+ ActiveRecord::Base.connection.execute(ddl)
237
+ end
238
+ when "firebird"
239
+ ActiveRecord::Base.establish_connection(:test)
240
+ ActiveRecord::Base.connection.recreate_database!
241
+ else
242
+ raise "Task not supported by #{config["adapter"].inspect}"
243
+ end
244
+ end
245
+
246
+ desc 'Check for pending migrations and load the test schema'
247
+ task :prepare => ['db:abort_if_pending_migrations', 'db:test:load']
248
+ end
249
+
250
+ desc "Create a new migration"
251
+ task :new_migration do |t|
252
+ unless migration = ENV['name']
253
+ puts "Error: must provide name of migration to generate."
254
+ puts "For example: rake #{t.name} name=add_field_to_form"
255
+ abort
256
+ end
257
+
258
+ class_name = migration.split('_').map{|s| s.capitalize }.join
259
+ file_contents = <<eof
260
+ class #{class_name} < ActiveRecord::Migration
261
+ def self.up
262
+ end
263
+
264
+ def self.down
265
+ raise ActiveRecord::IrreversibleMigration
266
+ end
267
+ end
268
+ eof
269
+ migration_path = @migrations.first
270
+ FileUtils.mkdir_p(migration_path) unless File.exist?(migration_path)
271
+ file_name = "#{migration_path}/#{Time.now.utc.strftime('%Y%m%d%H%M%S')}_#{migration}.rb"
272
+
273
+ File.open(file_name, 'w'){|f| f.write file_contents }
274
+
275
+ puts "Created migration #{file_name}"
276
+ end
277
+ end
278
+ end
279
+ end
data/spec/molo_spec.rb ADDED
@@ -0,0 +1,268 @@
1
+ describe 'Molo' do
2
+ def write(file, content)
3
+ raise "cannot write nil" unless file
4
+ file = tmp_file(file)
5
+ folder = File.dirname(file)
6
+ `mkdir -p #{folder}` unless File.exist?(folder)
7
+ File.open(file,'w'){|f| f.write content}
8
+ end
9
+
10
+ def read(file)
11
+ File.read(tmp_file(file))
12
+ end
13
+
14
+ def migration(name)
15
+ m = `cd spec/tmp/db/migrations && ls`.split("\n").detect{|m| m =~ name}
16
+ m ? "db/migrations/#{m}" : m
17
+ end
18
+
19
+ def tmp_file(file)
20
+ "spec/tmp/#{file}"
21
+ end
22
+
23
+ def run(cmd)
24
+ `cd spec/tmp && #{cmd} 2>&1 && echo SUCCESS`
25
+ end
26
+
27
+ def make_migration(name)
28
+ migration = run("rake db:new_migration name=#{name}").match(%r{db/migrations/\d+.*.rb})[0]
29
+ content = read(migration)
30
+ content.sub!(/def self.down.*?\send/m, "def self.down;puts 'DOWN-#{name}';end")
31
+ content.sub!(/def self.up.*?\send/m, "def self.up;puts 'UP-#{name}';end")
32
+ write(migration, content)
33
+ migration.match(/\d{14}/)[0]
34
+ end
35
+
36
+ def write_rakefile(config=nil)
37
+ write 'Rakefile', <<-TXT
38
+ $LOAD_PATH.unshift '#{File.expand_path('lib')}'
39
+ begin
40
+ require 'tasks/molo'
41
+ MigratorTasks.new do |t|
42
+ t.log_level = Logger::INFO
43
+ #{config}
44
+ end
45
+ rescue LoadError => e
46
+ puts "gem install molo to get db:migrate:* tasks! (Error: \#{e})"
47
+ end
48
+ TXT
49
+ end
50
+
51
+ def write_multiple_migrations
52
+ write_rakefile %{t.migrations = "db/migrations", "db/migrations2"}
53
+ write "db/migrations/20100509095815_create_tests.rb", <<-TXT
54
+ class CreateTests < ActiveRecord::Migration
55
+ def self.up
56
+ puts "UP-CreateTests"
57
+ end
58
+
59
+ def self.down
60
+ puts "DOWN-CreateTests"
61
+ end
62
+ end
63
+ TXT
64
+ write "db/migrations2/20100509095816_create_tests2.rb", <<-TXT
65
+ class CreateTests2 < ActiveRecord::Migration
66
+ def self.up
67
+ puts "UP-CreateTests2"
68
+ end
69
+
70
+ def self.down
71
+ puts "DOWN-CreateTests2"
72
+ end
73
+ end
74
+ TXT
75
+ end
76
+
77
+ before do
78
+ `rm -rf spec/tmp` if File.exist?('spec/tmp')
79
+ `mkdir spec/tmp`
80
+ write_rakefile
81
+ write 'db/config.yml', <<-TXT
82
+ development:
83
+ adapter: sqlite3
84
+ database: db/development.sql
85
+ test:
86
+ adapter: sqlite3
87
+ database: db/test.sql
88
+ TXT
89
+ end
90
+
91
+ describe 'db:new_migration' do
92
+ context "single migration path" do
93
+ it "fails if i do not add a name" do
94
+ run("rake db:new_migration").should_not =~ /SUCCESS/
95
+ end
96
+
97
+ it "generates a new migration with this name and timestamp" do
98
+ run("rake db:new_migration name=test_abc").should =~ %r{Created migration .*spec/tmp/db/migrations/\d+_test_abc\.rb}
99
+ run("ls db/migrations").should =~ /^\d+_test_abc.rb$/
100
+ end
101
+ end
102
+
103
+ context "multiple migration paths" do
104
+ before do
105
+ write_rakefile %{t.migrations = "db/migrations", "db/migrations2"}
106
+ end
107
+ it "chooses the first path" do
108
+ run("rake db:new_migration name=test_abc").should =~ %r{Created migration .*db/migrations/\d+_test_abc\.rb}
109
+ end
110
+ end
111
+ end
112
+
113
+ describe 'db:migrate' do
114
+ context "single migration path" do
115
+ it "does nothing when no migrations are present" do
116
+ run("rake db:migrate").should =~ /SUCCESS/
117
+ end
118
+
119
+ it "migrates if i add a migration" do
120
+ run("rake db:new_migration name=xxx")
121
+ result = run("rake db:migrate")
122
+ result.should =~ /SUCCESS/
123
+ result.should =~ /Migrating to Xxx \(#{Time.now.year}/
124
+ end
125
+ end
126
+
127
+ context "multiple migration paths" do
128
+ before do
129
+ write_multiple_migrations
130
+ end
131
+ it "runs the migrator on each migration path" do
132
+ result = run("rake db:migrate")
133
+ result.should =~ /Migrating to CreateTests \(#{Time.now.year}/
134
+ result.should =~ /Migrating to CreateTests2 \(#{Time.now.year}/
135
+ end
136
+ end
137
+ end
138
+
139
+ describe 'db:migrate:down' do
140
+ context "single migration path" do
141
+ it "migrates down" do
142
+ make_migration('xxx')
143
+ sleep 1
144
+ version = make_migration('yyy')
145
+ run 'rake db:migrate'
146
+
147
+ result = run("rake db:migrate:down VERSION=#{version}")
148
+ result.should =~ /SUCCESS/
149
+ result.should_not =~ /DOWN-xxx/
150
+ result.should =~ /DOWN-yyy/
151
+ end
152
+
153
+ it "fails without version" do
154
+ make_migration('yyy')
155
+ result = run("rake db:migrate:down")
156
+ result.should_not =~ /SUCCESS/
157
+ end
158
+ end
159
+
160
+ context "multiple migration paths" do
161
+ before do
162
+ write_multiple_migrations
163
+ end
164
+
165
+ it "runs down on the correct path" do
166
+ run 'rake db:migrate'
167
+ result = run 'rake db:migrate:down VERSION=20100509095815'
168
+ result.should =~ /DOWN-CreateTests/
169
+ result.should_not =~ /DOWN-CreateTests2/
170
+ end
171
+
172
+ it "fails if migration number isn't found" do
173
+ run 'rake db:migrate'
174
+ result = run 'rake db:migrate:down VERSION=20100509095820'
175
+ result.should_not =~ /SUCCESS/
176
+ result.should =~ /wasn't found on path/
177
+ end
178
+ end
179
+ end
180
+
181
+ describe 'db:migrate:up' do
182
+ context "single migration path" do
183
+ it "migrates up" do
184
+ make_migration('xxx')
185
+ run 'rake db:migrate'
186
+ sleep 1
187
+ version = make_migration('yyy')
188
+ result = run("rake db:migrate:up VERSION=#{version}")
189
+ result.should =~ /SUCCESS/
190
+ result.should_not =~ /UP-xxx/
191
+ result.should =~ /UP-yyy/
192
+ end
193
+
194
+ it "fails without version" do
195
+ make_migration('yyy')
196
+ result = run("rake db:migrate:up")
197
+ result.should_not =~ /SUCCESS/
198
+ end
199
+ end
200
+
201
+ context "multiple migration paths" do
202
+ before do
203
+ write_multiple_migrations
204
+ end
205
+
206
+ it "runs down on the correct path" do
207
+ result = run 'rake db:migrate:up VERSION=20100509095815'
208
+ result.should =~ /UP-CreateTests/
209
+ result.should_not =~ /UP-CreateTests2/
210
+ end
211
+
212
+ it "fails if migration number isn't found" do
213
+ result = run 'rake db:migrate:up VERSION=20100509095820'
214
+ result.should_not =~ /SUCCESS/
215
+ result.should =~ /wasn't found on path/
216
+ end
217
+ end
218
+ end
219
+
220
+ describe 'schema:dump' do
221
+ it "dumps the schema" do
222
+ result = run('rake db:schema:dump')
223
+ result.should =~ /SUCCESS/
224
+ read('db/schema.rb').should =~ /ActiveRecord/
225
+ end
226
+ end
227
+
228
+ describe 'db:schema:load' do
229
+ it "loads the schema" do
230
+ run('rake db:schema:dump')
231
+ schema = "db/schema.rb"
232
+ write(schema, read(schema)+"\nputs 'LOADEDDD'")
233
+ result = run('rake db:schema:load')
234
+ result.should =~ /SUCCESS/
235
+ result.should =~ /LOADEDDD/
236
+ end
237
+ end
238
+
239
+ describe 'db:abort_if_pending_migrations' do
240
+ it "passes when no migrations are pending" do
241
+ run("rake db:abort_if_pending_migrations").should_not =~ /try again/
242
+ end
243
+
244
+ it "fails when migrations are pending" do
245
+ make_migration('yyy')
246
+ result = run("rake db:abort_if_pending_migrations")
247
+ result.should =~ /try again/
248
+ result.should =~ /1 pending migration/
249
+ end
250
+ end
251
+
252
+ describe 'db:test:load' do
253
+ it 'loads' do
254
+ write("db/schema.rb", "puts 'LOADEDDD'")
255
+ run("rake db:test:load").should =~ /LOADEDDD.*SUCCESS/m
256
+ end
257
+
258
+ it "fails without schema" do
259
+ run("rake db:test:load").should =~ /no such file to load/
260
+ end
261
+ end
262
+
263
+ describe 'db:test:purge' do
264
+ it "runs" do
265
+ run('rake db:test:purge').should =~ /SUCCESS/
266
+ end
267
+ end
268
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Jesús García Sáez
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,92 @@
1
+
2
+ DESCRIPTION
3
+ ===========
4
+
5
+ Helpers for migrations of ActiveRecord for dealing with foreign keys and primary keys.
6
+
7
+ FEATURES
8
+ ========
9
+
10
+ * **foreign keys**
11
+ * foreign_key(table, field, referenced_table, referenced_field, on_cascade)
12
+ * drop_foreign_key(table, field)
13
+ * **primary keys**
14
+ * primary_key(table, field)
15
+
16
+ Examples
17
+ ========
18
+
19
+ Typical use:
20
+
21
+ def self.up
22
+ create_table :profiles do |t|
23
+ t.string :first_name
24
+ t.string :last_name
25
+ t.string :email
26
+ t.boolean :is_disabled
27
+ end
28
+ create_table :users do |t|
29
+ t.string :login
30
+ t.string :crypted_password
31
+ t.string :salt
32
+ t.integer :profile_id
33
+ end
34
+
35
+ foreign_key :users, :profile_id, :profiles
36
+ end
37
+
38
+ def self.down
39
+ drop_foreign_key :users, :profile_id
40
+ drop_table :users
41
+ drop_table :profiles
42
+ end
43
+
44
+
45
+ Also, if we don't defined a common :id (exactly it's rails who define it), we should create a primary key:
46
+
47
+ def self.up
48
+ create_table :foo, :id => false do |t|
49
+ t.string :foo, :bar
50
+ end
51
+
52
+ primary_key :foo, [ :foo, :bar ]
53
+ end
54
+
55
+ In the parameter where a field is required (like the second parameter in *primary_key*) you can specified and symbol (or string) or an array of symbols (or strings).
56
+
57
+
58
+ REQUIREMENTS
59
+ ============
60
+
61
+ * It's been tested with Mysql adapter and Jdbcmysql adapter
62
+
63
+ INSTALL
64
+ =======
65
+
66
+ * script/plugin install git://github.com/blaxter/migration_helpers.git
67
+
68
+ LICENSE
69
+ =======
70
+
71
+ (The MIT License)
72
+
73
+ Copyright (c) 2008 Jesús García Sáez <jgarcia@warp.es>
74
+
75
+ Permission is hereby granted, free of charge, to any person obtaining
76
+ a copy of this software and associated documentation files (the
77
+ 'Software'), to deal in the Software without restriction, including
78
+ without limitation the rights to use, copy, modify, merge, publish,
79
+ distribute, sublicense, and/or sell copies of the Software, and to
80
+ permit persons to whom the Software is furnished to do so, subject to
81
+ the following conditions:
82
+
83
+ The above copyright notice and this permission notice shall be
84
+ included in all copies or substantial portions of the Software.
85
+
86
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
87
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
88
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
89
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
90
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
91
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
92
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,3 @@
1
+ require 'migration_helper'
2
+
3
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.send :include, MigrationConstraintHelpers
@@ -0,0 +1,51 @@
1
+ module MigrationConstraintHelpers
2
+
3
+ # Creates a foreign key from +table+.+field+ against referenced_table.referenced_field
4
+ #
5
+ # table: The tablename
6
+ # field: A field of the table
7
+ # referenced_table: The table which contains the field referenced
8
+ # referenced_field: The field (which should be part of the primary key) of the referenced table
9
+ # cascade: delete & update on cascade?
10
+ def foreign_key(table, field, referenced_table, referenced_field = :id, cascade = true)
11
+ execute "ALTER TABLE #{table} ADD CONSTRAINT #{constraint_name(table, field)}
12
+ FOREIGN KEY #{constraint_name(table, field)} (#{field_list(field)})
13
+ REFERENCES #{referenced_table}(#{field_list(referenced_field)})
14
+ #{(cascade ? 'ON DELETE CASCADE ON UPDATE CASCADE' : '')}"
15
+ end
16
+
17
+ # Drops a foreign key from +table+.+field+ that has been created before with
18
+ # foreign_key method
19
+ #
20
+ # table: The table name
21
+ # field: A field (or array of fields) of the table
22
+ def drop_foreign_key(table, field)
23
+ execute "ALTER TABLE #{table} DROP FOREIGN KEY #{constraint_name(table, field)}"
24
+ end
25
+
26
+ # Creates a primary key for +table+, which right now HAS NOT primary key defined
27
+ #
28
+ # table: The table name
29
+ # field: A field (or array of fields) of the table that will be part of the primary key
30
+ def primary_key(table, field)
31
+ execute "ALTER TABLE #{table} ADD PRIMARY KEY(#{field_list(field)})"
32
+ end
33
+
34
+ private
35
+
36
+ # Creates a constraint name for table and field given as parameters
37
+ #
38
+ # table: The table name
39
+ # field: A field of the table
40
+ def constraint_name(table, field)
41
+ "fk_#{table}_#{field_list_name(field)}"
42
+ end
43
+
44
+ def field_list(fields)
45
+ fields.is_a?(Array) ? fields.join(',') : fields
46
+ end
47
+
48
+ def field_list_name(fields)
49
+ fields.is_a?(Array) ? fields.join('_') : fields
50
+ end
51
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: molo
3
+ version: !ruby/object:Gem::Version
4
+ hash: 11
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 5
9
+ - 0
10
+ version: 0.5.0
11
+ platform: ruby
12
+ authors:
13
+ - Joel Moss
14
+ - Todd Huss
15
+ - Michael Grosser
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2010-12-13 00:00:00 +00:00
21
+ default_executable:
22
+ dependencies:
23
+ - !ruby/object:Gem::Dependency
24
+ name: activerecord
25
+ prerelease: false
26
+ requirement: &id001 !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ~>
30
+ - !ruby/object:Gem::Version
31
+ hash: 1
32
+ segments:
33
+ - 3
34
+ - 0
35
+ - 3
36
+ version: 3.0.3
37
+ type: :runtime
38
+ version_requirements: *id001
39
+ - !ruby/object:Gem::Dependency
40
+ name: rake
41
+ prerelease: false
42
+ requirement: &id002 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ hash: 27
48
+ segments:
49
+ - 0
50
+ - 8
51
+ version: "0.8"
52
+ type: :runtime
53
+ version_requirements: *id002
54
+ description:
55
+ email: joel@developwithstyle.com
56
+ executables: []
57
+
58
+ extensions: []
59
+
60
+ extra_rdoc_files:
61
+ - README.markdown
62
+ files:
63
+ - .gitignore
64
+ - Gemfile
65
+ - Gemfile.lock
66
+ - Molo.gemspec
67
+ - README.markdown
68
+ - Rakefile
69
+ - VERSION
70
+ - lib/tasks/molo.rb
71
+ - spec/molo_spec.rb
72
+ - vendor/migration_helpers/MIT-LICENSE
73
+ - vendor/migration_helpers/README.markdown
74
+ - vendor/migration_helpers/init.rb
75
+ - vendor/migration_helpers/lib/migration_helper.rb
76
+ has_rdoc: true
77
+ homepage: http://codaset.com/joelmoss/molo
78
+ licenses: []
79
+
80
+ post_install_message:
81
+ rdoc_options:
82
+ - --charset=UTF-8
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ hash: 3
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ hash: 3
100
+ segments:
101
+ - 0
102
+ version: "0"
103
+ requirements: []
104
+
105
+ rubyforge_project:
106
+ rubygems_version: 1.3.7
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: A thin wrapper to use Rails Migrations in non Rails projects
110
+ test_files:
111
+ - spec/molo_spec.rb