83

I'd like to run a rake task in my controller. Is there any way to do this?

Arslan Ali
  • 17,418
  • 8
  • 58
  • 76
user143482
  • 2,341
  • 3
  • 15
  • 7

4 Answers4

65

I agree with ddfreynee, but in case you know what you need code can look like:

require 'rake'

Rake::Task.clear # necessary to avoid tasks being loaded several times in dev mode
Sample::Application.load_tasks # providing your application name is 'sample'

class RakeController < ApplicationController

  def run
    Rake::Task[params[:task]].reenable # in case you're going to invoke the same task second time.
    Rake::Task[params[:task]].invoke
  end

end

You can require 'rake' and .load_tasks in an initializer instead.

Grimmo
  • 1,485
  • 14
  • 13
  • Where would I find out what my "application name" is? – pdxleif Sep 23 '12 at 19:50
  • @pxdleif It's in `config/application.rb` as the module name about 12 lines down, assuming you're using Rails. – Tim Fletcher Oct 03 '12 at 14:38
  • 2
    You can also reference your application by calling `Rails.application` – declan Feb 26 '13 at 22:02
  • 2
    This works great! Just so it's clear for others, this method is for synchronous calling of rake tasks (page does not render until rake is done). For async usage, use call_rake from this rails cast: http://railscasts.com/episodes/127-rake-in-background – benathon Nov 20 '13 at 12:27
  • Actually after a bit more work, my app on Rails 3.2.13 only needed the Sample::Application.load_tasks line. If i include the Task.clear line my app works until I call the first rake, then it gives me an error about a custom override I made for doc:app. But that's just my app – benathon Nov 20 '13 at 13:03
  • what if `params[:talk]` is equal to `"db:drop"`? – MrYoshiji Jun 20 '17 at 20:59
  • Is this open as new thread? I've added in my controller and task is initialized but controller function will not respond untill rake task completed. – faisal bhatti Sep 13 '18 at 13:37
62

I don't find it good style to call a rake task in code. I recommend putting the code for the task that you want to execute somewhere outside a rake task, and have the rake task call this code.

This not only has the advantage of being easy to call outside rake (which is what you want), but it also makes it much easier to test the rake task.

Denis Defreyne
  • 2,213
  • 15
  • 18
  • 6
    +1 I concur: this is a perfect example of things that should be refactored and called in two different ways. – James A. Rosen Jul 23 '09 at 12:29
  • 7
    http://railscasts.com/episodes/127-rake-in-background Seems an excellent reason to run a rake.. – baash05 Mar 15 '12 at 00:40
  • 4
    Where would a good "somewhere" be for "somewhere outside the rake task"? – user456584 May 31 '13 at 14:59
  • @user456584 if it's related to models, put it in the model class. if it's related to something else, create a new file that holds them. –  Aug 31 '13 at 15:57
  • Service Objects. https://blog.engineyard.com/2014/keeping-your-rails-controllers-dry-with-services – max Jul 22 '15 at 02:01
  • 2
    In my case I needed to run a task from a 3rd party lib, so I couldn't refactor it (without going through the trouble of forking, etc) – César Izurieta Jul 29 '15 at 18:40
  • @ddfreyne What about if your services are tasks **with dependencies**? Doesn't it make sense to use rakes dependency management inside of your rails app instead of writing an own implementation? – Alexander Presber Dec 27 '16 at 20:46
23

Instead of trying to call a rake task in a controller, call a service objects that contains whatever logic you are trying to execute.

class SomeController < ApplicationController
  def whatever
    SomeServiceObject.call
  end
end

...and then, assuming you are talking about a custom rake task, have it call the service object as well:

namespace :example do
  desc 'important task'
  task :important_task do
    SomeServiceObject.call
  end
end

In case you are not familiar with service objects, they are just plain old ruby classes that do a specific job. If you are trying to call some of the default rake tasks (ie: db:migrate) I would highly recommend not doing that sort of thing from a controller.

Jarrod Spillers
  • 284
  • 2
  • 7
  • 1
    This is a very clean way of doing it. Thanks. – juliangonzalez Jul 24 '16 at 20:45
  • This concept is very straight forward. it works great! I am not adding code in `SomeServiceObject`. I am writing in model with `self` and calling from rake task like this: `User.subscribe_to_fcm` – mahfuz Apr 14 '21 at 06:56
14

You can do this in your controller:

%x[rake name_task]

with: name_task is the name of your task

JMax
  • 26,109
  • 12
  • 69
  • 88
olibouli
  • 189
  • 2
  • 3
  • 2
    how do you pass environment to it? – serengeti12 Jul 02 '12 at 10:41
  • 5
    i would never shell out to rake from my controller in production. – Duke Nov 29 '12 at 01:41
  • 21
    @Duke when you answer or comment can you also explain your reasoning and not just make an assertion (that a controller in production shouldn't call rake tasks)? This would help people understand your reasoning instead of just knowing what you wouldn't do (but not hearing your explanation as to why). – Matt May 17 '13 at 00:59
  • 7
    @Matt shelling out means that Rake will execute in another process. This means it will launch a new ruby interpreter. It's also a possibility that injection can occur which leads to arbitrary commands being executed (a few typos away from doing that). –  Aug 31 '13 at 15:57