Execute a Rake task from within migration? - ruby-on-rails

I have a Rake task that loads configuration data into the DB from a file, is there a correct ruby/rails way to call it on a migration up?
My objective is to sync my team DB configs, without have to broadcast then to run the task lalala
def self.up
change_table :fis_situacao_fiscal do |t|
t.remove :mostrar_endereco
t.rename :serie, :modelo
end
Faturamento::Cfop.destroy_all()
#perform rake here !
end
UPDATE
How I do now, and works:
system('rake sistema:load_data file=faturamento/cfop')
And this is the suggestion from #Ryan Bigg, and it's exception:
Rake::Task['rake sistema:load_data file=faturamento/cfop'].invoke()
.
== AlterSituacaoFiscalModeloEndereco: migrating ====================
-- change_table(:fis_situacao_fiscal)
-> 0.0014s
rake aborted!
An error has occurred, this and all later migrations canceled:
Don't know how to build task 'rake sistema:load_data file=faturamento/cfop'
Where it went wrong?

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']

Note that if you call the Rake task with 'system', you need to check the process status afterwards and raise an exception if the Rake task failed. Otherwise the migration will succeed even if the Rake task fails.
You can check the process status like this:
if !($?.success?)
raise "Rake task failed"
end
Invoking the rake task is a nicer option - it will cause the migration to fail if the Rake task fails.

You can execute a rake task from within a loaded Rails environment with either Rake::Task['namespace:task'].invoke or Rake::Task['namespace:task'].execute.
You can pass data to the task inside of the invoke or execute method. Example:
Rake::Task['namespace:task'].invoke(paramValue)
This param can be handled in the rake task as follows:
namespace :namespace do
desc "Example description."
task :task, [:param] => :environment do |t, args|
puts args[:param]
...
end
end
This can be executed on the console as:
bundle exec rake namespace:task[paramValue]
More info: https://medium.com/#sampatbadhe/rake-task-invoke-or-execute-419cd689c3bd

This decision fits better, IMHO.
In your case it would be smth like this:
backup_env = ENV.slice('file') if ENV.key?('file')
ENV['file'] = 'faturamento/cfop'
Rake::Task['sistema:load_data'].invoke
ENV.delete 'file'
ENV.merge!(backup_env) if backup_env

Related

how to run rake task using rails migration

I want to run rake task using migration because we want when a user run rails db:migrate then this task will be run through migration.
my rake task is:
namespace :task_for_log do
desc "This task set by default as current date for those logs where log_date is nil"
task set_by_default_date_of_log: :environment do
Log.where("log_date IS NULL").each do |log|
log.update_attributes(log_date: log.created_at.to_date)
end
end
end
please guide what will be migration that run this task, any body here who will be save my life??
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
If you want to run your task after you run db:migrate automatically, you can use enhance.
Rake::Task['db:migrate'].enhance do
# This task runs after every time you run `db:migrate`
Rake::Task['task_for_log:set_by_default_date_of_log'].invoke
end
For a rails application, you can put this anywhere inside lib/tasks folder or put your task inline (inside of the .enhance do block)
You can go as #joseph mention better solution! Or create custom task for it.
rake:
rake cm:set_by_default_date_of_log
task:
#lib/tasks/cm.rake
#custom_migration
namespace :cm do
desc "This task set by default as current date for those logs where log_date is nil"
task set_by_default_date_of_log: ['db:migrate'] do
Log.where("log_date IS NULL").each do |log|
log.update_attributes(log_date: log.created_at.to_date)
end
end
end

Prepend to existing rake task

Defining an existing rake task again appends to the original, but I'd like to prepend to the db:migrate task. I want to raise an error beforehand in some cases. Is there a good way to prepend to an existing rake task?
Try adding a db:custom task on 'db' namespace and invoke db:migrate with enhance method
# add your custom code on db:custom
namespace 'db' do
task 'custom' do
puts "do custom db stuff"
end
end
# invoke db:migrate
Rake::Task['db:migrate'].enhance [:custom]
Might be better to define your own task and call db:migrate inside.
namespace :custom_db do
desc 'migrate db if condition true'
task :migrate do
if true #your condition
Rake::Task['db:migrate'].invoke
else
#process errors
end
end
end

How to fix "uninitialized constant" in a Rake task

I have a problem when I do:
namespace :xaaron do
task :get_roles do
roles = Xaaron::Role.all
puts roles
end
task :get_role, [:name] do |t, args|
role = Xaaron::Role.find(args[:name].parameterize)
puts role
end
end
The first task will work fine. I can even add binding.pry and run Xaaron::Role and get information about Roles back. But the second task fails with:
NameError: uninitialized constant Xaaron::Role
I run each task in my main app because these tasks are inside an engine, using:
bin/rake xaaron:get_roles` and `bin/rake xaaron:get_role
I can run bin/rails c in the main application that uses the engine and run Xaaron::Role and get information about Roles table.
Why is the second one failing but the first one is not? Is there scoping with arguments?
I'm not sure why either works, but if this is Rails and those are Rails models, your tasks should depend on the environment:
task :get_roles => [ :environment ] do
By depending on the :environment task, it first loads Rails.
Also see: What's the 'environment' task in Rake?.
You can also run a Rake task as
bundle exec rake environment xaaron:get_role
This will load the Rails environment first.
I kept getting uninitialized constant errors for a Rake task, even after depending on :environment and running with bundle exec.
The issue was that I was making a Rake::TestTask and, even though the Rake task had access to all constants, the test files themselves did not have access to constants.
The solution was to add this line to the top of my test file:
require_relative '../config/environment'
This is the Rake task:
require "rake/testtask"
Rake::TestTask.new(:test) do |t|
t.libs << "test"
t.libs << "lib"
t.test_files = FileList["test/**/test_*.rb"]
end
To add, as of Ruby 1.9 and above, you can use this hash syntax:
namespace :xaaron do
desc "Rake task to get roles"
task get_roles: :environment do
roles = Xaaron::Role.all
puts roles
end
#####
end
And then you can run the command below to run the Rake task:
rake xaaron:get_roles
or
bundle exec rake xaaron:get_roles

Change to Rake tasks and argument structure in Rails 3.2?

I have seen the other posts but I am still having trouble. Below is my code. I have several rake tasks where I pass in zero, one or even five arguments. What am I missing?
namespace :my_namespace do
desc 'shows user accounts within the database for the specified customer.'
task :show_user_accounts, [:customer_id] => :environment do |t, args|
cust = Customer.find( args.customer_id.to_i )
cust.users.each do |user|
puts "User Name: #{user.name}\tUser ID: #{user.id}\t"
end
end
end
I am running the task with the following command:
$ rake my_namespace:show_user_accounts customer_id=110
Error:
rake aborted!
Couldn't find Customer with id=0
After much searching around I found that not only did the syntax for a rake task change, but the execution syntax did as well. So, the code of my rake task (above) is correct but my invocation was wrong.
The correct way for running above rake task is:
$ rake my_namespace:show_user_accounts[110]
I found the answer here: http://www.redconfetti.com/2012/01/example-rake-task/

How do I pass parameter to a rake task that is invoked using Rake::Task

Here is my rake task
task :lab => :enviroment do
Rake::Task["db:rollback"].invoke('STEP=5')
end
It is not doing what I want. What I want is
rake db:rollback STEP=5
I am using Rails 3.2.1 on ruby 1.9.2.
On the command line I want to execute
rake lab
The real case is much more complicated but this is the jist.
task :lab => :enviroment do
ENV['STEP'] ||= 5
Rake::Task["db:rollback"].invoke
end
Options can be passed into rake by specifying key/value pairs on the rake command:
rake options:show opt1=value1
These command line options are then automatically set as environment variables which can be accessed within your rake task:
namespace :options do
desc "Show how to read in command line options"
task :show do
p "option1 is #{ENV['opt1']}"
end
end
Passing this as an environment variable might be your best bet. Try:
task :lab => :enviroment do
Rake::Task["db:rollback"].invoke(ENV['STEP'])
end
rake db:rollback STEP=5

Resources