Class: MigratorTasks

Inherits:
Rake::TaskLib
  • Object
show all
Defined in:
lib/tasks/molo.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name = :migrator) {|_self| ... } ⇒ MigratorTasks

Returns a new instance of MigratorTasks.

Yields:

  • (_self)

Yield Parameters:

  • _self (MigratorTasks)

    the object that the method was called on



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/tasks/molo.rb', line 9

def initialize(name = :migrator)
  @name = name
  base = File.expand_path('.')
  here = File.expand_path(File.dirname(File.dirname(File.dirname((__FILE__)))))
  @base = base
  @vendor = "#{here}/vendor"
  @migrations = ["#{base}/db/migrations"]
  @config = "#{base}/db/config.yml"
  @schema = "#{base}/db/schema.rb"
  @env = 'DB'
  @default_env = 'development'
  @verbose = true
  @log_level = Logger::ERROR
  yield self if block_given?
  # Add to load_path every "lib/" directory in vendor
  Dir["#{vendor}/**/lib"].each{|p| $LOAD_PATH << p }
  define
end

Instance Attribute Details

#baseObject

Returns the value of attribute base.



6
7
8
# File 'lib/tasks/molo.rb', line 6

def base
  @base
end

#configObject

Returns the value of attribute config.



6
7
8
# File 'lib/tasks/molo.rb', line 6

def config
  @config
end

#default_envObject

Returns the value of attribute default_env.



6
7
8
# File 'lib/tasks/molo.rb', line 6

def default_env
  @default_env
end

#envObject

Returns the value of attribute env.



6
7
8
# File 'lib/tasks/molo.rb', line 6

def env
  @env
end

#log_levelObject

Returns the value of attribute log_level.



6
7
8
# File 'lib/tasks/molo.rb', line 6

def log_level
  @log_level
end

#loggerObject

Returns the value of attribute logger.



6
7
8
# File 'lib/tasks/molo.rb', line 6

def logger
  @logger
end

#migrationsObject

Returns the value of attribute migrations.



7
8
9
# File 'lib/tasks/molo.rb', line 7

def migrations
  @migrations
end

#nameObject

Returns the value of attribute name.



6
7
8
# File 'lib/tasks/molo.rb', line 6

def name
  @name
end

#schemaObject

Returns the value of attribute schema.



6
7
8
# File 'lib/tasks/molo.rb', line 6

def schema
  @schema
end

#vendorObject

Returns the value of attribute vendor.



6
7
8
# File 'lib/tasks/molo.rb', line 6

def vendor
  @vendor
end

#verboseObject

Returns the value of attribute verbose.



6
7
8
# File 'lib/tasks/molo.rb', line 6

def verbose
  @verbose
end

Instance Method Details

#defineObject



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/tasks/molo.rb', line 32

def define
  namespace :db do
    task :ar_init do
      require 'active_record'
      require 'erb'
      require 'yaml_db'
      require 'rails_sql_views'
      
      ENV[@env] ||= @default_env

      if @config.is_a?(Hash)
        ActiveRecord::Base.configurations = @config
      else
        ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read(@config)).result)
      end
      ActiveRecord::Base.establish_connection(ENV[@env])
      if @logger
        logger = @logger
      else
        logger = Logger.new($stderr)
        logger.level = @log_level
      end
      ActiveRecord::Base.logger = logger
    end

    desc "Migrate the database using the scripts in the migrations directory. Target specific version with VERSION=x. Turn off output with VERBOSE=false."
    task :migrate => :ar_init  do
      require "#{@vendor}/migration_helpers/init"
      ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true        
      @migrations.each do |path|
        ActiveRecord::Migrator.migrate(path, ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
      end
      Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
    end

    namespace :migrate do
      desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
      task :redo => :ar_init do
        if ENV["VERSION"]
          Rake::Task["db:migrate:down"].invoke
          Rake::Task["db:migrate:up"].invoke
        else
          Rake::Task["db:rollback"].invoke
          Rake::Task["db:migrate"].invoke
        end
      end
      
      desc 'Runs the "up" for a given migration VERSION.'
      task :up => :ar_init do
        ActiveRecord::Migration.verbose = @verbose
        version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
        raise "VERSION is required" unless version
        
        migration_path = nil
        if @migrations.length == 1
          migration_path = @migrations.first
        else
          @migrations.each do |path|
            Dir[File.join(path, '*.rb')].each do |file|
              if File.basename(file).match(/^\d+/)[0] == version.to_s
                migration_path = path
                break
              end
            end
          end
          raise "Migration #{version} wasn't found on paths #{@migrations.join(', ')}" if migration_path.nil?
        end
        
        ActiveRecord::Migrator.run(:up, migration_path, version)
        Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
      end

      desc 'Runs the "down" for a given migration VERSION.'
      task :down => :ar_init do
        ActiveRecord::Migration.verbose = @verbose
        version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
        raise "VERSION is required" unless version
        
        migration_path = nil
        if @migrations.length == 1
          migration_path = @migrations.first
        else
          @migrations.each do |path|
            Dir[File.join(path, '*.rb')].each do |file|
              if File.basename(file).match(/^\d+/)[0] == version.to_s
                migration_path = path
                break
              end
            end
          end
          raise "Migration #{version} wasn't found on paths #{@migrations.join(', ')}" if migration_path.nil?
        end
        
        ActiveRecord::Migrator.run(:down, migration_path, version)
        Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
      end
      
      desc "Display status of migrations"
      task :status => :ar_init do
        config = ActiveRecord::Base.configurations[ENV[@env]]
        ActiveRecord::Base.establish_connection(config)
        unless ActiveRecord::Base.connection.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
          puts 'Schema migrations table does not exist yet.'
          next  # means "return" for rake task
        end
        db_list = ActiveRecord::Base.connection.select_values("SELECT version FROM #{ActiveRecord::Migrator.schema_migrations_table_name}")
        file_list = []
        @migrations.each do |dir|
          Dir.foreach(dir) do |file|
            # only files matching "20091231235959_some_name.rb" pattern
            if match_data = /(\d{14})_(.+)\.rb/.match(file)
              status = db_list.delete(match_data[1]) ? 'up' : 'down'
              file_list << [status, match_data[1], match_data[2]]
            end
          end
        end
        # output
        puts "\ndatabase: #{config['database']}\n\n"
        puts "#{"Status".center(8)}  #{"Migration ID".ljust(14)}  Migration Name"
        puts "-" * 50
        file_list.each do |file|
          puts "#{file[0].center(8)}  #{file[1].ljust(14)}  #{file[2].humanize}"
        end
        db_list.each do |version|
          puts "#{'up'.center(8)}  #{version.ljust(14)}  *** NO FILE ***"
        end
        puts
      end
    end

    desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
    task :rollback => :ar_init do
      step = ENV['STEP'] ? ENV['STEP'].to_i : 1
      @migrations.each do |path|
        ActiveRecord::Migrator.rollback(path, step)
      end
      Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
    end

    desc "Retrieves the current schema version number"
    task :version => :ar_init do
      puts "Current version: #{ActiveRecord::Migrator.current_version}"
    end

    # desc "Raises an error if there are pending migrations"
    task :abort_if_pending_migrations => :ar_init do
      @migrations.each do |path|
        pending_migrations = ActiveRecord::Migrator.new(:up, path).pending_migrations
        
        if pending_migrations.any?
          puts "You have #{pending_migrations.size} pending migrations:"
          pending_migrations.each do |pending_migration|
            puts '  %4d %s' % [pending_migration.version, pending_migration.name]
          end
          abort %{Run "rake db:migrate" to update your database then try again.}
        end
      end
    end

    namespace :schema do
      desc "Create schema.rb file that can be portably used against any DB supported by AR"
      task :dump => :ar_init do
        if schema_file = ENV['SCHEMA'] || @schema
          require 'active_record/schema_dumper'
          File.open(schema_file, "w") do |file|
            ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
          end
        end
      end

      desc "Load a ar_schema.rb file into the database"
      task :load => :ar_init do
        file = ENV['SCHEMA'] || @schema
        load(file)
      end
    end

  	desc "Dump schema and data to db/schema.rb and db/data.yml"
  	task(:dump => [ "db:schema:dump", "db:data:dump" ])

  	desc "Load schema and data from db/schema.rb and db/data.yml"
  	task(:load => [ "db:schema:load", "db:data:load" ])

  	namespace :data do
      table_options = {
        :only => ENV['only'] || [],
        :except => ENV['except'] || []
      }
  	  
  		def db_dump_data_file(extension = "yml")
  		  "#{dump_dir}/data.#{extension}"
      end
          
      def dump_dir(dir = "")
        "#{base}/db#{dir}"
      end

  		desc "Dump contents of database to db/data.extension (defaults to yaml)"
  		task :dump => :ar_init do
        format_class = ENV['class'] || "YamlDb::Helper"
        helper = format_class.constantize
  			SerializationHelper::Base.new(helper).dump db_dump_data_file(helper.extension), table_options
  		end

  		desc "Dump contents of database to db/data/tablename.extension (defaults to yaml)"
  		task :dump_dir => :ar_init do
        format_class = ENV['class'] || "YamlDb::Helper"
        dir = ENV['dir'] || "data"
        SerializationHelper::Base.new(format_class.constantize).dump_to_dir dump_dir("/#{dir}"), table_options
  		end

  		desc "Load contents of db/data.extension (defaults to yaml) into database"
  		task :load => :ar_init do
        format_class = ENV['class'] || "YamlDb::Helper"
        helper = format_class.constantize
  			SerializationHelper::Base.new(helper).load db_dump_data_file(helper.extension), table_options
  		end

  		desc "Load contents of db/data/* into database"
  		task :load_dir  => :ar_init do
        dir = ENV['dir'] || "data"
        format_class = ENV['class'] || "YamlDb::Helper"
  	    SerializationHelper::Base.new(format_class.constantize).load_from_dir dump_dir("/#{dir}"), table_options
  		end
  	end

    namespace :test do
      desc "Recreate the test database from the current schema.rb"
      task :load => ['db:ar_init', 'db:test:purge'] do
        ActiveRecord::Base.establish_connection(:test)
        ActiveRecord::Schema.verbose = false
        Rake::Task["db:schema:load"].invoke
      end

      desc "Empty the test database"
      task :purge => 'db:ar_init' do
        config = ActiveRecord::Base.configurations['test']
        case config["adapter"]
        when "mysql"
          ActiveRecord::Base.establish_connection(:test)
          ActiveRecord::Base.connection.recreate_database(config["database"], config)
        when "postgresql" #TODO i doubt this will work <-> methods are not defined
          ActiveRecord::Base.clear_active_connections!
          drop_database(config)
          create_database(config)
        when "sqlite", "sqlite3"
          db_file = config["database"] || config["dbfile"]
          File.delete(db_file) if File.exist?(db_file)
        when "sqlserver"
          drop_script = "#{config["host"]}.#{config["database"]}.DP1".gsub(/\\/,'-')
          `osql -E -S #{config["host"]} -d #{config["database"]} -i db\\#{drop_script}`
          `osql -E -S #{config["host"]} -d #{config["database"]} -i db\\test_structure.sql`
        when "oci", "oracle"
          ActiveRecord::Base.establish_connection(:test)
          ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl|
            ActiveRecord::Base.connection.execute(ddl)
          end
        when "firebird"
          ActiveRecord::Base.establish_connection(:test)
          ActiveRecord::Base.connection.recreate_database!
        else
          raise "Task not supported by #{config["adapter"].inspect}"
        end
      end
  
      desc 'Check for pending migrations and load the test schema'
      task :prepare => ['db:abort_if_pending_migrations', 'db:test:load']
    end

    desc "Create a new migration"
    task :new_migration do |t|
      unless migration = ENV['name']
        puts "Error: must provide name of migration to generate."
        puts "For example: rake #{t.name} name=add_field_to_form"
        abort
      end

      class_name = migration.split('_').map{|s| s.capitalize }.join
      file_contents = <<eof
class #{class_name} < ActiveRecord::Migration
def self.up
end

def self.down
  raise ActiveRecord::IrreversibleMigration
end
end
eof
      migration_path = @migrations.first
      FileUtils.mkdir_p(migration_path) unless File.exist?(migration_path)
      file_name  = "#{migration_path}/#{Time.now.utc.strftime('%Y%m%d%H%M%S')}_#{migration}.rb"

      File.open(file_name, 'w'){|f| f.write file_contents }

      puts "Created migration #{file_name}"
    end
  end
end