molo 0.5.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.
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