how to run rake task using rails migration - ruby-on-rails

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

Related

The case of the disappearing ActiveRecord attribute

Following the instructions in https://stackoverflow.com/a/24496452/102675 I wound up with the following:
namespace :db do
desc 'Drop, create, migrate, seed and populate sample data'
task seed_sample_data: [:drop, :create, :migrate, :seed, :populate_sample_data] do
puts 'Sample Data Populated. Ready to go!'
end
desc 'Populate the database with sample data'
task populate_sample_data: :environment do
puts Inspector.column_names.include?('how_to_fix')
# create my sample data
end
end
As you would expect, I get true if I run bundle exec rake db:populate_sample_data
BUT if I run bundle exec rake db:seed_sample_data I get all the migration output and then false. In other words I can't see the Inspector attribute how_to_fix even though it definitely exists as proved by the other rake run. Where did my attribute go?
My guess is that this is a "caching" problem. Can you try the following?
task populate_sample_data: :environment do
Inspector.reset_column_information
# ...
end
P.S. We used to have a similar problem working with different databases having the exact same schema (only except some columns here and there)

How to run rake db:migrate with sidekiq

I am trying to run the command rake db:migrate using a sidekiq worker but it seems like it just won't work and I am curious if there is a way to do this or not. I am creating a scaffold using sidekiq but cannot migrate it afterwards
This works
class ScaffoldGeneratorWorker
include Sidekiq::Worker
def perform(id)
`rails g scaffold test_#{id} title:string body:text slug:string visible:boolean`
end
end
But I cannot get this to run afterwards and work
class DatabaseMigrationWorker
include Sidekiq::Worker
def perform
`rake db:migrate`
end
end
Is this possible, and, if so, how can I get it to work. Any help is greatly appreciated.
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
This code automagically loads the Rake tasks for your Rails application without you even knowing how your application is named (this was the case for me). It also makes the code easier to share between various Rails projects.
class MySidekiqTask
include Sidekiq::Worker
def perform
application_name = Rails.application.class.parent_name
application = Object.const_get(application_name)
application::Application.load_tasks
Rake::Task['db:migrate'].invoke
end
end
If you need to invoke the Rake task with parameters, you can simply pass them in through the invoke method (https://www.rubydoc.info/gems/rake/Rake%2FTask:invoke):
Rake::Task['db:migrate'].invoke(params)
Did you try adding require 'rake' at the top of your file?
a possible duplicate of How do I run rake tasks within my rails application

Is it possible to use Rails rake tasks on secondary databases?

If you are using multiple databases (e.g. to shard tables across databases) for a given environment is it possible to use the built in Rails rake tasks to manipulate the databases beyond the primary one for said environment?
e.g. if I use specific connection details for a set of models, can I use rake db:create to create said database?
If so, how?
I've actually overwritten the built-in Rails rake tasks in order to manipulate a second database. The neat thing is that it totally works with the original rake tasks as well.
This is what I did... credit to Nikolay Sturm, as I based my rake task off of his blog.
I added new database settings in my database.yml, as detailed in the blog I linked.
You also create a base model that has these database settings:
class BaseModel < ActiveRecord::Base
establish_connection "database_connection_name"
self.abstract_class = true #you want to make this an abstract class because you don't have a table for this
end
You then have any model that connects to this secondary database inherit from your BaseModel.
In your rake task, you set it up as follows (keep in mind that this is for a Postgres connection.)
namespace :db do
desc 'create database'
task create: :environment do
connection_config = BaseModel.connection_config #rename BaseModel to name of your model
ActiveRecord::Base.establish_connection connection_config.merge('database => 'postgres') #you don't need this bit if you're not using Postgres
ActiveRecord::Base.connection.create_database connection_config[:database], connection_config
ActiveRecord::Base.establish_connection connection_config
end
desc 'migrate database'
task migrate: :environment do
ActiveRecord::Base.establish_connection BaseModel.connection_config
ActiveRecord::Migrator.migrate('db/migrate/')
end
desc 'drop database'
task drop: :environment do
connection_config = BaseModel.connection_config
ActiveRecord::Base.establish_connection connection_config.merge('database' => 'postgres')
ActiveRecord::Base.connection.drop_database connection_config[:database]
end
Now, if you do a rake -T in your project, you should be able to see the your descriptions for the rake tasks instead of the default. When you run them, it will run the db:create, db:migrate, and db:drop for both your default and your secondary database. Hope this helps!

Overriding rails' default rake tasks

I have a Rails 2.2 project in which I want to override the functionality of the rake db:test:prepare task. I thought this would work, but it doesn't:
#lib/tasks/db.rake
namespace :db do
namespace :test do
desc "Overridden version of rails' standard db:test:prepare task since the schema dump used in that can't handle DB enums"
task :prepare => [:environment] do
puts "doing db:structure:dump"
Rake::Task['db:structure:dump'].invoke
puts "doing db:test:clone_structure"
Rake::Task['db:test:clone_structure'].invoke
end
end
end
I get the standard task's behaviour. If I change the name of the task to :prepare2 and then do rake db:test:prepare2, then it works fine. The natural conclusion I draw from this is that my rake tasks are being defined before the built-in Rails ones, so mine is overridden by the standard :prepare task.
Can anyone see how I can fix this? I'd rather override it than have to use a new task. Thanks, max
If you define a rake task that already exists, its execution gets appended to the original task's execution; both tasks will be executed.
If you want to redefine a task you need to clear the original task first:
Rake::Task["db:test:prepare"].clear
It's also useful to note that once a task has been executed in rake, it won't execute again even if you call it again. This is by design but you can call .reset on a task to allow it to be run again.
You have to remove the default task before adding your own:
Rake.application.instance_variable_get('#tasks').delete('db:test:prepare')
namespace 'db' do
namespace 'test' do
task 'prepare' do
# ...
end
end
end
A fairly popular idiom is to create a convenience method called remove_task like so:
Rake::TaskManager.class_eval do
def remove_task(task_name)
#tasks.delete(task_name.to_s)
end
end
def remove_task(task_name)
Rake.application.remove_task(task_name)
end
(Source: drnic/newgem)
Create a new project.rake file at lib/tasks/, and paster below code into it.
namespace :mv do
desc "Display hint and info for your rails 4 project"
task info: :environment do
puts 'Run rake test to test'
end
end
task(:default).clear.enhance ['mv:info']
inspired by Krasimir Angelov's blog

Execute a Rake task from within migration?

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

Resources