Overriding rails' default rake tasks - ruby-on-rails

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

Related

How can I get around-hook-like behavior for rake tasks?

I have a number of rake tasks for which I would like to implement around-hook-like behavior. Specifically, I'm looking for a way to ensure that all of my Rake tasks execute in a particular (complicated, derived) Time.use_zone block.
For analogy, I have this in my ApplicationController:
around_filter :use_time_zone
def use_time_zone
time_zone = non_trivial_derivation
Time.use_zone(time_zone) { yield }
end
And now all of my controller actions will appropriately execute in the specified time zone. I would like some mechanism like this for Rake. I'd be willing to change or modify the dependency chain for my rake tasks, but I don't want to insert the actual time zone derivation code at the top of each rake task, out of concerns that that would lead to maintenance fragility. I'm pretty sure that Rake dependencies hold the solution--after all, Rake dependencies allow me to execute code in the context of my Rails application. But I can't figure out how to get that done for this use case.
I came up with a simple solution that doesn't require any external dependencies or gems such as rake-hooks:
desc "rake around hook"
task :use_timezone, [:subtask] => :environment do |name, args|
puts "using timezone"
Rake::Task[args[:subtask]].invoke
puts "end using timezone"
end
task :testing do
puts "testing"
end
The idea is that you execute the main use_timezone task and pass in your actual task as an argument:
$ rake use_timezone[testing]
That outputs:
> using timezone
> testing
> end using timezone
For your case you can write it like this:
task :use_timezone, [:subtask] => :environment do |name, args|
time_zone = non_trivial_derivation
Time.use_zone(time_zone) { Rake::Task[args[:subtask]].invoke }
end
And use it like this:
$ rake use_timezone[your_task]
Hope that helps.

A General Method to be invoked before every rake execution

In my rails project (Rails 3.1, Ruby 1.9.3) there are around 40 rake tasks defined. The requirement is that I should be able to create an entry (the rake details) in a database table right when we start each rake. The details I need are the rake name, arguments, start time and end time. For this purpose, I don't want rake files to be updated with the code. Is it possible to do this outside the scope of rake files.
Any help would be greatly appreciated!!
Try this
https://github.com/guillermo/rake-hooks
For example in your Rakefile
require 'rake/hooks'
task :say_hello do
puts "Good Morning !"
end
before :say_hello do
puts "Hi !"
end
#For multiple tasks
namespace :greetings do
task :hola do puts "Hola!" end ;
task :bonjour do puts "Bonjour!" end ;
task :gday do puts "G'day!" end ;
end
before "greetings:hola", "greetings:bonjour", "greetings:gday" do
puts "Hello!"
end
rake greetings:hola # => "Hello! Hola!"
This seems to be a bit awkward, But it may help others.
Rake.application.top_level_tasks
will return an array of information including Rake name and its arguments.
Reference attached below.
pry(main)> a = Rake.application.top_level_tasks
=> ["import_data[client1,", "data.txt]"]
When you create rake task, you can pass a parent task which will run before your task:
task my_task: :my_parent_task do
# ...
end
If your task depends from more than 1 task, you can pass an array of parent tasks
task my_task: [:my_prev_task, :my_another_prev_task] do
# ...
end

Testing a method defined in a rake task

I want to test a method defined in a rake task.
rake file
#lib/tasks/simple_task.rake
namespace :xyz do
task :simple_task => :environment do
begin
if task_needs_to_run?
puts "Lets run this..."
#some code which I don't wish to test
...
end
end
end
def task_needs_to_run?
# code that needs testing
return 2 > 1
end
end
Now, I want to test this method, task_needs_to_run? in a test file
How do I do this ?
Additional note: I would ideally want test another private method in the rake task as well... But I can worry about that later.
The usual way to do this is to move all actual code into a module and leave the task implementation to be only:
require 'that_new_module'
namespace :xyz do
task :simple_task => :environment do
ThatNewModule.doit!
end
end
If you use environmental variables or command argument, just pass them in:
ThatNewModule.doit!(ENV['SOMETHING'], ARGV[1])
This way you can test and refactor the implementation without touching the rake task at all.
You can just do this:
require 'rake'
load 'simple_task.rake'
task_needs_to_run?
=> true
I tried this myself... defining a method inside a Rake namespace is the same as defining it at the top level.
loading a Rakefile doesn't run any of the tasks... it just defines them. So there is no harm in loading your Rakefile inside a test script, so you can test associated methods.
When working within a project with a rake context (something like this) already defined:
describe 'my_method(my_method_argument)' do
include_context 'rake'
it 'calls my method' do
expect(described_class.send(:my_method, my_method_argument)).to eq(expected_results)
end
end

Newbie, about Rake task syntax

In Rake task definition, like following:
desc 'SOME description'
task :some_task => :environment do
# DO SOMETHING
end
What does the :some_task in task :some_task => :environment means?
Is it the method name which will be invoked in the DO SOMETHING part?
Can :some_task be any arbitrary string which describe the task?
In fact, when you're creating a rake task, :some_task is the name of the task you are calling.
For instance, in this case, you will call rake some_task
You also could define namespaces for your tasks :
namespace :my_tasks do
desc "My first task"
task :first_task => :environment do
# DO SOMETHING
end
end
And then you will call rake my_tasks:first_task in your console.
Hope it will help you,
Edit:
As explained by Holger Just, the :environment executes the "environment" task and if you are on rails, loads the environment. This could take a long time but il also helps you if your tasks works with the database.
With your example, you define a task called some_task which can be invoked by calling rake some_task on the command line.
It will depend on the environment task which will be rune before your new some_task. In rails, the environment task sets up the rails environment (loading libraries, preparing database connection, ...) which is quite expensive and thus optional.

How to prevent Rake test to call task db:test:prepare

Every time I want to run Rake test the task db:test:prepare is being called and it rebuilds my test environment database from schema.rb and migrations. What I would like to achive is to disable the call of db:test:prepare when I want to test make Rails application. Is it possible without modifying Rails gem?
Here's a solution I've seen around:
In your Rakefile:
Rake::TaskManager.class_eval do
def remove_task(task_name)
#tasks.delete(task_name.to_s)
end
end
In lib/tasks/db/test.rake:
Rake.application.remove_task 'db:test:prepare'
namespace :db do
namespace :test do
task :prepare do |t|
# rewrite the task to not do anything you don't want
end
end
end
There is a plugin that takes care of this for you: override_rake_task. Here is a quick usage example:
namespace :db do
namespace :test do
override_task :prepare do; end
end
end
For some older version of rails - you can place Rake::Task['db:test:prepare'].clear at the end of your Rakefile

Resources