Execute a Rake Task from Within Migration

Execute a Rake task from within migration?

Yes there's a way to do that:

Rake::Task['your_task'].invoke

Update

Do not put rake inside the brackets, just the name of the task. You should set an ENV variable when running this:

In the console

FILE=somefile.text rake db:sistema:load_data

Calling it separately

FILE=somefile.text rake some:other:task:that:calls:it

This will be available in your tasks as ENV['file']

how to run rake task using rails migration

Migrations are really just Ruby files following a convention, so if you want to run a rake task inside of them you can just call the Rake class.

class ExampleMigration < ActiveRecord::Migration[5.0]
def change
Rake::Task['task_for_log'].invoke
end
end

However, migration files should be used specifically to handle the database schema. I would rethink how you are approaching the problem for a better solution. For example, you could run a SQL statement that updates your log attributes instead of calling a rake task.

class ExampleMigration < ActiveRecord::Migration[5.0]
def change
execute <<-SQL
UPDATE logs SET log_date = created_at WHERE log_date IS NULL
SQL
end
end

References:

  • Rails how to run rake task
  • https://edgeguides.rubyonrails.org/active_record_migrations.html

Rails 5 - generate and run migrations inside a rake task

You can use Rake's sh method and just call the rails shell commands.

sh "rails g migration AddPay#{initials}ToShoppingLists pay#{initials}:decimal"
sh "rails g migration AddPay#{initials}ToPayments pay#{initials}:decimal"

When using sh as opposed to ruby's built-in backtick delimiters for shell commands, if the command has an exit status other than 0 it will raise an exception and abort the task.

To see if your migration has already been created, you can just check for the existence of a migration file matching the naming pattern.

files = Dir.glob Rails.root.join('db/migrate/*')

migration_patterns = {
/add_pay_#{initials.downcase}_to_shopping_lists/ => "rails g migration AddPay#{initials}ToShoppingLists pay#{initials}:decimal",
/add_pay_#{initials.downcase}_to_payments/ => "rails g migration AddPay#{initials}ToPayments pay#{initials}:decimal"
}

migration_patterns.each do |file_pattern, migration_command|
if files.none? { |file| file.match? file_pattern }
sh migration_command
end
end

Rake::Task['db:migrate'].invoke

This assumes you won't have any migration naming collisions that raise false positives in none?. But Rails won't let you have migration naming collisions anyway, so the check might not be necessary. It seems like you are bound to run into this problem eventually given the way you're naming the migrations and columns. What if two users have the same initials?

Might there be a way you can accomplish what you need to by using an additional database table (maybe a polymorphic join table?) instead of adding columns for every user? Something along these lines could work:

class CreateDisbursements < ActiveRecord::Migration[5.1]
def change
create_table :disbursements do |t|
t.decimal :amount
t.integer :payable_id
t.string :payable_type
t.integer :receivable_id
t.string :receivable_type

t.timestamps
end

add_index :disbursements, [:payable_type, :payable_id]
add_index :disbursements, [:receivable_id, :receivable_type]
end
end

class Disbursement < ApplicationRecord
belongs_to :payable, polymorphic: true
belongs_to :receivable, polymorphic: true
end

class ShoppingList < ApplicationRecord
has_many :disbursements, as: :payable
has_many :users, through: :disbursements, source: :receivable, source_type: 'User'
end

class Payment < ApplicationRecord
has_many :disbursements, as: :payable
has_many :users, through: :disbursements, source: :receivable, source_type: 'User'
end

class User < ApplicationRecord
has_many :disbursements, as: :receivable
has_many :payments, through: :disbursements, source: :payable, source_type: 'Payment'
has_many :shopping_lists, through: :disbursements, source: :payable, source_type: 'ShoppingList'
end

user = User.find params[:user_id]
payment = Payment.find params[:payment_id]
amount = params[:amount]

payment.disbursements.create(amount: amount, receivable: user)
user.disbursements.create(amount: amount, payable: payment)
Disbursement.create(amount: amount, payable: payment, receivable: user)
user.payments
payment.users

Execute a Rake task from within migration?

Yes there's a way to do that:

Rake::Task['your_task'].invoke

Update

Do not put rake inside the brackets, just the name of the task. You should set an ENV variable when running this:

In the console

FILE=somefile.text rake db:sistema:load_data

Calling it separately

FILE=somefile.text rake some:other:task:that:calls:it

This will be available in your tasks as ENV['file']

Rails: How to manage rake tasks likewise migrations


  1. Correct way: use deploy automation. Capistrano is a good choice. Then you'll never need to worry about things like running rake task
  2. I think the rake tasks should have no side effects if you execute it multiple times. If the task is implemented that way, then there's no need to worry about which has been done and which is not.
  3. I think if you want to get a status tracking for the Rake Task, a simple way is to implemented a model to record the execution status of the rake task, and update the model each time rake task is done.

Running rake task from within war file

Finally found something that works.... i first tried

java -jar lib/jruby-complete-1.6.7.jar -S rake db_migrate[1] 

which was working fine on my personal machine but giving me something like the message below on production

rake aborted!
cannot load Java class com.mysql.jdbc.Driver

Tasks: TOP => db_migrate
(See full trace by running task with --trace)

this was because i was using gems like sequel, logger etc inside my rake task.... i head those installed on my machine but not on production machine.... installing those gems on production was not an option.... so i installed the gems required in the rake task in a separate directory and converted it into a jar file( http://blog.nicksieger.com/articles/2009/01/10/jruby-1-1-6-gems-in-a-jar)... this command finally worked...

java -jar lib/jruby-complete-1.6.7.jar -rlib/mygems.jar -S rake db_migrate[1]

point to note: no matter where you place the jar file, warbler 'll always send this to lib directory although you 'll still see a dummy jar file at the original location...
i think the solution can be a bit neater if worked out in a couple of ways, although haven't tried this....

i>by including the gem files in jruby-complete-1.6.7.jar itself as mentioned in the blog mentioned above...

java -jar lib/jruby-complete-1.6.7.jar -S rake db_migrate[1]

should work then...

ii>by writing some kind of a manifest file and include it in the mygems.jar to make this run independently... if this happens

java -jar myapp.jar -S rake db_migrate[1] 

should work

Rails: run rake tasks like migrations


need to be only run once after a specific code change

update certain existing users records

These are migrations. Use migrations. Unless there's some additional constraints you didn't mention, using anything but migrations would be a hack.

How to run db:migrate from another rake task with parameters?

Once ActiveRecord sets the environment you have to tell it directly to change the environment. So this will work.

ActiveRecord::Tasks::DatabaseTasks.env = 'test'
Rake::Task["db:drop"].execute
Rake::Task["db:create"].execute
Rake::Task["db:migrate"].execute

ActiveRecord::Tasks::DatabaseTasks.env = 'development'
Rake::Task["db:drop"].execute
Rake::Task["db:create"].execute
Rake::Task["db:migrate"].execute

How to run rake db:migrate with sidekiq

First you should load rake tasks, then invoke:

class DatabaseMigrationWorker
include Sidekiq::Worker

def perform
Name_Of_Your_App::Application.load_tasks
Rake::Task['db:migrate'].invoke
end
end


Related Topics



Leave a reply



Submit