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?
Related
This is a 2nd part to the following question:
Where to put model "utility" functions in Ruby on Rails
Problem is, I need access to these utility functions from a rake task as well. Using the accepted technique in in the other thread, I get an "undefined method" error when accessing my model from a rake task.
What is the best way to fix this?
Thanks
You probably need to define your rake task as dependent on the Rails environment:
task :my_task => :environment do
# Will load Rails stack before executing this block
MyModel.foo
end
The default behavior is to load almost nothing, so you won't have access to your models unless you ask for it.
Given a rake task that references both a namespaced and non-namespaced model:
namespace :thing do
task :thingo => :environment do
Klass.first.some_method
Namespaced::Klass.first.some_other_method
end
end
Using ruby 1.9.2, rails 3.0.9, and rake 0.9.2, this yields an exception, like so:
undefined method 'some_other_method' for #<Klass:0x007fcfafbaa6e0>
Two things:
Why doesn't rails return the proper namespacing in the rake environment (in a debugger session), but it does in a console session?
Why does changing the order of reference work? (That is, if the environment is already calling "Namespaced::Klass" as "Klass", then calling "Klass" should fail with undefined method 'some_method' for #<Klass:0x007fcfafbaa6e0> right?
By the way, I've tried ::Namespaced::Klass.first.some_other_method
If the answer isn't simple, I'll put together a test app - let me know! :-)
First, some background on metaphor shear - two different kinds of namespaces:
Although Rake Namepsaces and Ruby Namespaces share the word Namespace, they are separate concepts. Rake namespaces are just organizing containers for Rake Tasks, not Ruby namespaces/modules. So code inside your thing:thingo rake task is actually executing at the top-level Ruby namespace.
Second: If Klass is a single class not in a namespace, you can reference it directly. If the class exists as Foo::Klass then you'll need to use the fully-qualified Foo::Klass reference unless the scope of the reference is already within the Foo namespace.
Because Rake namespaces aren't Ruby modules, you are not in the context of a Ruby namespace within your task. This is why Klass.some_method works if Klass isn't in a module.
If this doesn't explain the question, please post the class definition for Klass including any module/namespace membership.
I have a model, let's call it Foobar. I want to be able to run a cron job to update an attribute of all objects that are instances of Foobar. So, in pseudocode, it might be something like this:
Foobar.all.each do |foobar|
foobar.update_attributes({:my_attribute => 'updated'});
end
Now, let's say I wrap that in a class method called Foobar.run_update().
Calling Foobar.run_update() would work fine from the controller, or even from a view. But, what I want to do is run run_update() from the Rakefile so that I can tie it into a cron run. But, the Foobar class is not available to Rake when it is called from crontab.
How can I resolve that? How can I access the class methods of Foobars from Rake, when Rake is called from cron?
Thank you very much for your help.
By rake, if you mean a rake task then adding => :environment loads the rails environment for the task and you be able to call the Foobar.run_update method there. Like,
namespace :foobar do
task :update => :environment do
Foobar.run_update
end
end
And you should just be able to call rake foobar:update from the console and have it scheduled as a cronjob.
You can load up the Rails environment by requiring config/environment.rb:
ENV["RAILS_ENV"] ||= "production"
require '/where/your/rails/project/is/config/environment.rb'
In my Sinatra apps I typically have the file models/init.rb which requires Sequel, sets up my DB connection, and then uses require_relative to require all my model files. My main application then does require_relative "models/init".
With this setup any other script (including IRB) all I have to do is require the models/init.rb file myself, and I have full access to the same models and DB connection that the application has.
Trying to integrate some friendly_id gem functionality on a controller method.
Essentially, I have a Market object, which has its URL created based on a custom method. Since it's based on a custom method, friendly_id won't update the URL when the Market object gets updated. Friendly_id does offer a redo_slugs rake task, but when I call it from within my controller, it tells me that it can't build the task. Running the command outside works just fine.
The code for my controller looks like this:
require 'rake'
require 'friendly_id'
class Admin::MarketsController < ApplicationController
def update
if #market.update_attributes(params[:market])
rake_market_slugs
end
end
protected
def rake_market_slugs
Rake::Task["friendly_id:redo_slugs MODEL=Market"].invoke
end
end
Am I missing something? Or can I just not do this inside my controller?
Thank you.
Calling a rake task from a controller to update a model object is terrible. Looking at the code for that rake task, you can see that redo_slugs is simply running the delete_slugs and make_slugs tasks. So there's another reason not to do this. You'll be generating slugs for every Market in your table, instead of just the one that you need.
If you look at the code for make_slugs you can see that there's no magic there. All it does is load your model objects in blocks of 100 and then save them.
So, that would be the first thing I would try. Simply reload and save your model. After that, I'd need to see some logs to dig deeper.
def rake_market_slugs
MODEL="Market"
Rake::Task["friendly_id:redo_slugs"].invoke(MODEL)
end
Try it...
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