Invoke rake task from ActiveRecord observer - ruby-on-rails

Can I do the following?
def ModelObserver < ActiveRecord
def after_save
Rake::Task[name].invoke
end
end
At the moment, this returns the following error:
Don't know how to build task 'name'
Any idea?

Use the system command :
def ModelObserver < ActiveRecord
def after_save
system "rake #{name}"
end
end

Consider using delayed job or similar plugin to handle background execution. In observer (or controller) just notify background job daemon, that it should take care of some action, instead of running this task directly.

In Rails3 if you still want to call rake task like this:
Rake::Task[name].invoke
you have to put
[Application].load_tasks
before invoke command, where application is your application name. For example I had to put
Ead::Application.load_tasks

I suppose you have to load the Rake environment first, and the Rakefile. I would not try to invoke the fullblown command line to do that. You probably need to use "import" as can be found in the Rake API

Related

Do something in a rake task before Rails models are loaded

I have a rake task that works with models. But I need to tell the models that they are being used from Rake instead of within the app. My best idea is to set an environment variable on the command line before running the rake task, but that requires everyone on the team to remember to use the environment variable. Specifically, I have an if statement to skip loading Delayed::Job's handle_asynchronously while running the rake task.
class Thing < ActiveRecord::Base
searchable do
string :title
text :title, :content
end
handle_asynchronously unless ENV['MIGRATINGDATA']
end
When we run our migration task we do this:
MIGRATINGDATA=true bundle exec rake project:migrate_data
I'd like to get rid of the need for the special addition to the command line. Our migration code loads a fake Sunspot instance to turn off indexing while migrating the data.
Just set something at the top of your Rakefile:
ENV['HELLO_RAKE'] = true
require_relative 'config/application'
Rails.application.load_tasks
Instead of using ENV you could, if you wanted, set a constant, e.g. HELLO_RAKE = true and then check defined?(HELLO_RAKE).
An alternative is to just check if the running program is rake:
handle_asynchronously unless File.basename($0) == "rake"
A downside to both of these approaches is that they will be in effect any time you're using Rake, which will include other Rake tasks not related to migrations.
If your models care if you are using them from a rake task or not, you are doing something wrong. Instead you can add parameters to certain methods for example. In your specific use case, you can run jobs immediately instead. Put this at the start of your rake task:
Delayed::Worker.delay_jobs = false

Rails create Tasks for Scheduler

I'm trying to use the Heroku Rails scheduler.
https://devcenter.heroku.com/articles/scheduler
I don't know where to write my function that is supposed to be accomplished. Where do I write this?
In their case the function is called NewsFeed.update
i didn't used that heroku scheduler but i can give you some idea. it is very simple. You just have to write some worker or rake task. We will use rake task. For that you have create file lib/tasks/news_feeds_update.rake
and put this
namespace :news_feed do
task :update => :environment do
# body of method
# not sure what you wanted, so i put the below method.
NewsFeed.update
end
end
Now, you can add this task/method into heroku scheduler . Also you can call this task or method by doing this in command line
rake news_feed:update

Invoke ActionMailer from cron job in Rails 3?

Is it possible to invoke ActionMailer from a cron job? We're using Rails 3.
Ideally, a cron job triggers a script to read users from a database, passes these users and some variables to an ActionMailer class to fire off emails.
Is this possible in Rails 3.2.12?
Yes it is possible. You could use a task to invoke with the rake command. Your task could be something like this:
# lib/tasks/cron.rake
namespace :cron do
desc "Send account emails"
task deliver_emails: :environment do
accounts_for_delivery = Account.where(condition: true)
# ... whatever logic you need
accounts_for_delivery.each do |account|
Postman.personalized_email_for(account).deliver
end
end
end
And your mailer and the corresponding view could look like this:
# app/mailers/postman.rb
class Postman < ActionMailer::Base
def personalized_email_for(account)
#account = account
mail to: account.email
end
end
# app/views/postman/personalized_email_for.text.haml
= #account.inspect
Now you can set the crontab to run your rake task just like you perform rake tasks. I recommend you use the whenever gem, that really provides a nice way to define cronjobs for your application that looks like this:
# config/schedule.rb
every 6.hours do
rake 'cron:deliver_email'
end
So now the cronjob definitions are bound your application. It works well with Capistrano between deployments as well. You can also pass variables at your task or execute system commands.
If everything else fails you can just create a normal controller action and let the cronjob call it with curl.
Otherwise any script in your Rails apps script folder can be started with rails runner script/myscript.rb from the commandline and has full access to all Rails features.
You can use rails r (rails runner) to run a script in your rails app. It runs it, loading in the full context of your rails app before doing so, so all your models etc. are available. I use it a lot. For example,
rails r utilities/some_data_massaging_script.rb
From cron, you'd obviously need to give it the full path to your app.
The old-fashioned way was to have something like:
require "#{File.dirname(__FILE__)}/../config/environment.rb"
at the top of your script (adjusting the relative bit of the path depending on the subdirectory level of your script in your app of course) and then just run your script using ruby, but rails r makes that unnecessary.

Rails execute script as background job

I've a ruby script that has been implemented as an independent functionality.
Now I would like to execute this script in my rails environament, with the added difficulty of executing it as a background job, because it needs a great amount of time processing.
After adding the delayed_job gem, I've tryied calling the following sentence:
delay.system("ruby my_script.rb")
And this is the error I get:
Completed 500 Internal Server Error in 95ms
TypeError (can't dump anonymous module: #<Module:0x007f8a9ce14dc0>):
app/controllers/components_controller.rb:49:in `create'
Calling the self.delay method from your controller won't work, because DJ will try to serialize your controller into the Job. You'd better create a class to handle your task then flag its method as asynchronous :
class AsyncTask
def run
system('ruby my_script.rb')
end
handle_asynchronously :run
end
In your controller :
def create
...
AsyncTask.new.run
...
end
See the second example in the "Queing Jobs" section of the readme.
Like Jef stated the best solution is to create a custom job.
The problem with Jef's answer is that its syntax (as far as I know) is not correct and that's his job handles a single system command while the following will allow you more customization:
# lib/system_command_job.rb
class SystemCommandJob < Struct.new(:cmd)
def perform
system(cmd)
end
end
Note the cmd argument for the Struct initializer. It allows you to pass arguments to your job so the code would look like:
Delayed::Job.enqueue(SystemCommandJob.new("ruby my_script.rb"))

Call Application Controller functions from Rake task

I want to call a function in my ApplicationController from a rake task. I've added the => :environment tag, but it just doesn't want to work.
Here is my stripped down code-
lib\taks\autoscrape.rake:
desc "This task will scrape all the movies without info"
task(:autoscrape => :environment) do
require 'application' #probably extraneous
require File.dirname(__FILE__) + '/../../config/environment' #probably extraneous
unless ApplicationController.is_admin?
logger.error = "Sorry, you're not allowed to do that"
return
end
app\controller\application_controller.rb:
class ApplicationController < ActionController::Base
helper :all # include all helpers, all the time
def is_admin?
session[:is_admin] && session[:is_admin] > 0
end
end
result:
rake scrape:autoscrape --trace
** Invoke scrape:autoscrape (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute scrape:autoscrape
rake aborted!
undefined method `is_admin?' for ApplicationController:Class
E:/Dropbox/My Dropbox/Ruby/moviecat/lib/tasks/autoscrape.rake:11
My other controllers call this code all the time, no problems. How can my Rake task call this code? This is greatly simplified, it is part of a bigger problem, i would like to reuse more code.
Thanks!!
First, the error you are getting is because you are calling ApplicationController.is_admin? which isn't defined because your method is defined on instances of ApplicationController, not on the ApplicationController class.
Second, the concept of session (at least to me) doesn't really make too much sense in a rake task. There are no real sessions other than your user's session at the command line which is not what you would be getting.
To be honest, I don't know the best way for going about calling a Controller action/method from anywhere outside of classes that inherit from ApplicationController or ActionController::Base, or why you would want to. Those actions/methods are specifically designed to be used during a request, not some action that you call whenever. If you really need something and don't want to redefine it, put it in a model/library and include it.
Create a instance for controller and call the method.method inside the controller should be public method.
Example:
objEmail = EmailController.new
objEmail.message
Maybe the reason of rajat does not make sense, but the question is valid.
I need to do some maintenance and I need to execute a function inside a class. It is a workaround to get things fixed one time (like migration), and it is not imperative to do it by the proper way.
It can be done calling a url instead of calling a rake (with suitable setting of route and parameters).
It can be done by defining functions out of class (making it public).
Or it can be done as William Richerd has proposed, which, by the way, it works.
In your rake task you have:
unless ApplicationController.is_admin?
You should add .new to controller name:
unless ApplicationController.new.is_admin?

Resources