Calling a method before a task - ruby-on-rails

Is there any way in rails to call a method, such as before, automatically when running a rake task I've built?
Let's say we have
namespace :migrate do
def before
# do this before all tasks
end
desc 'migrate authors from legacy database'
task :authors => :environment do
# some code here
end
end
I want to the before method to run everytime a task runs.

See if this helps: http://www.rubyflow.com/items/4104

Related

Custom rails task not showing up/loading in rails 6

In rails-myapp/lib/tasks I have a custom task called orders.rake:
namespace :orders do
desc "fetch orders" do
task :fetch do
# come code
end
end
end
My understanding, this task should be available in the list when running rails -T.
I'm seeing this:
rails notes # fetch orders
Not sure this why the word of notes is showing up.
I supposed to be able to run this task by:
rails orders:fetch
Try so:
namespace :orders do
desc "fetch orders"
task :fetch do
# come code
end
end
Then:
rake orders:fetch
rake notes is a task that shows your TODO list

Does invoking multiple tasks from a parent rake loads environment for each of them

If I have one rake which invokes multiple other rakes.
Once I initiate the parent rake
rake myapp:main
Then invokes done within the rake would load environment for each task or its just one time activity done while running rake myapp:main ?
namespace :myapp do
desc "Main Run"
task :main => :environment do
Rake::Task['myapp:task1'].invoke
Rake::Task['myapp:task2'].invoke
end
task :task1 => :environment do
# Does the first task
end
task :task2 => :environment do
# Does the second task
end
end
Adding details to #Shadwell's answer..
The => :environment is specifying that the :environment task (defined by rails) is a dependency of your tasks and must be invoked before your tasks are.
You can see :environment task's definition here
https://github.com/rails/rails/blob/d70ba48c4dd6b57d8f38612ea95a3842337c1419/railties/lib/rails/application.rb#L428-432
Rake keeps track of which tasks have invoked though and when it reaches a dependency that has already been invoked it knows it can skip it.
https://github.com/jimweirich/rake/blob/5e59bccecaf480d1de565ab34fd15e54ff667660/lib/rake/task.rb#L195-204
# Invoke all the prerequisites of a task.
def invoke_prerequisites(task_args, invocation_chain) # :nodoc:
if application.options.always_multitask
invoke_prerequisites_concurrently(task_args, invocation_chain)
else
prerequisite_tasks.each { |p|
prereq_args = task_args.new_scope(p.arg_names)
p.invoke_with_call_chain(prereq_args, invocation_chain)
}
end
end
Rake maintains an intance variable #already_invoked to know if a task has already been called. The same can be seen in the below method
https://github.com/jimweirich/rake/blob/5e59bccecaf480d1de565ab34fd15e54ff667660/lib/rake/task.rb#L170-184
def invoke_with_call_chain(task_args, invocation_chain) # :nodoc:
new_chain = InvocationChain.append(self, invocation_chain)
#lock.synchronize do
if application.options.trace
application.trace "** Invoke #{name} #{format_trace_flags}"
end
return if #already_invoked
#already_invoked = true
invoke_prerequisites(task_args, new_chain)
execute(task_args) if needed?
end
rescue Exception => ex
add_chain_to(ex, new_chain)
raise ex
end
The environment would only be set up once.
The => :environment is specifying that the :environment task (defined by rails) is a dependency of your tasks and must be invoked before your tasks are. Rake keeps track of which tasks have invoked though and when it reaches a dependency that has already been invoked it knows it can skip it.
(Aside: this can cause problems if you actually want the dependency to be invoked multiple times)
You could also define your main task using dependencies:
task :main => [:task1, :task2] do
# Blank
end
When you run rake myapp:main it will look at the dependencies and invoke task1 and then task2. Because task1 has a dependency environment it will invoke that first too. It'll skip the environment dependency on task2 though.
Answer is NO, Environment is not loaded when executing another Rake task from the parent Task. Simple explanation for this is the code below :
namespace :myapp do
desc "Main Run"
task :main => :environment do
puts "Start Time : #{Time.now.to_i}"
Rake::Task['myapp:task1'].invoke
puts "End Time1 : #{Time.now.to_i}"
Rake::Task['myapp:task2'].invoke
puts "End Time2 : #{Time.now.to_i}"
end
task :task1 => :environment do
# Does the first task
puts "Executing..1"
end
task :task2 => :environment do
# Does the second task
puts "Executing..2"
end
end
But it is not a good practice to do the two or multiple rake tasks. If you want to achieve the same thing, you can modularize the code, and create two functions and call the function to achieve the same result.

How do I run all rake tasks?

Have just installed whenever gem https://github.com/javan/whenever to run my rake tasks, which are nokogiri / feedzilla dependent scraping tasks.
eg my tasks are called grab_bbc, grab_guardian etc
My question - as I update my site, I keep add more tasks to scheduler.rake.
What should I write in my config/schedule.rb to make all rake tasks run, no matter what they are called?
Would something like this work?
every 12.hours do
rake:task.each do |task|
runner task
end
end
Am new to Cron, using RoR 4.
namespace :sc do
desc 'All'
task all: [:create_categories, :create_subcategories]
desc 'Create categories'
task create_categories: :environment do
# your code
end
desc 'Create subcategories'
task create_subcategories: :environment do
# your code
end
end
in console write $ rake sc:all
write separate rake tasks for each scraping tasks. then write a aggregated task to run all those scraping rake tasks.
desc "scrape nytimes"
task :scrape_nytimes do
# scraping method
end
desc "scrape guardian"
task :scrape_guardian do
# scraping method
end
desc "perform all scraping"
task :scrape do
Rake::Task[:scrape_nytimes].execute
Rake::Task[:scrape_guardian].execute
end
then call the rake task as
rake scrape
Make sure you have a unique namespace with all the tasks in it, like:
namespace :scrapers do
desc "Scraper Number 1"
task :scrape_me do
# Your code here
end
desc "Scraper Number 2"
task :scrape_it do
# Your code here
end
end
You could then run all tasks of that namespace with a task outside of that namespace:
task :run_all_scrapers do
Rake.application.tasks.each do |task|
task.invoke if task.name.starts_with?("scrapers:")
end
end
That said, I'm pretty sure that this is not how you should run a set of scrapers. If for any reason the if part should return true you might unintenionally run tasks like rake db:drop
Either "manually" maintaining schedule.rb or a master task seems like a better option to me.
The aggregated task can be concise:
namespace :scrape do
desc "scrape nytimes"
task :nytimes do
# scraping method
end
desc "scrape guardian"
task :guardian do
# scraping method
end
end
desc "perform all scraping"
task scrape: ['scrape:nytimes', 'scrape:guardian']
Namespaces are also a good practice.
Use namespace and in_namespace to run all tasks dynamically.
I prefer this method because it keeps things clean and precludes you from having to remember to update your "parent" task if any of our namespace tasks change.
Note, the example was borrowed from Dmitry Shvetsov's excellent answer.
namespace :scrape do
desc "scrape nytimes"
task :nytimes do
# scraping method
end
desc "scrape guardian"
task :guardian do
# scraping method
end
end
desc "perform all scraping"
task :scrape do
Rake.application.in_namespace( :scrape ){ |namespace| namespace.tasks.each( &:invoke ) }
end

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

Create a script or task to modify database

I need to create a script that imports data from a file system source. How do I do that?
I already tried to create a rake task but there the models are not loaded. How do I get the whole rails environment into my task?
desc 'Do stuff with models'
task :do_stuff => :environment do
1000.times.each do |i|
Model.create :name => "model number #{i}"
end
end
You declare :environment as a dependency of your rake task. This loads up rails and all of your app code before it runs.

Resources