ActiveRecord::Dirty and Rake - ruby-on-rails

I have a Model (let's call it A) in a Rails project that checks an attribute (let's call it a) with the ActiveRecord::Dirty a_changed? function on before_save. I want to be able to save an instance of A in a Rake task, but simply including :environment isn't cutting it--I'm getting a "no method a_changed? defined on A" message in the Rake task. How do I get ActiveRecord to remember about ActiveRecord::Dirty within a Rake task?
Rails version is 2.3.11
namespace :some_namespace do
namespace :some_subnamespace do
desc "This is a Rake Task"
task :some_taskname, [:some_arg] => [:environment] do |t,arg|
foo = A.find(11111)
foo.save #<=== fails with "no method a_changed? defined on A"
end
end
end
Since that's a pretty dense bunch of info, here's the breakdown:
I have a model A with an attribute a.
Model A has a before_save trigger defined that calls a_changed?, which is a method added by ActiveRecord::Dirty in the Rails environment. There are no problems calling this from a controller.
In my Rake task, however, the a_changed? call in the before_save trigger causes a NoMethodError exception to be raised, presumably because the [:environment] requirement is not sufficient to include ActiveRecord::Dirty. My question is how to make this not happen (my workaround is to rescue NoMethodError from inside the before_save, which is an obvious hack).

Looks like your question has already been answered on a previous question asked on StackOverflow.
In order to determine what methods your object has you can do this:
...
desc "This is a Rake Task"
task :some_taskname, [:some_arg] => :environment do |t, args|
foo = A.find(11111)
p foo.methods
...
This will print out a list of the available methods. If the array includes :some_attr_changed? (where some_attr is an attribute), then you can be certain that ActiveRecord::Dirty is indeed working fine in the rake task. If those methods don't show up in the array, then your assumptions are correct.

Related

Uninitialized constant error when trying to refer to a database record within a service

I'm trying to create a rake task that uses a service. Within that service, I want to load the last saved record of a MonthlyMetrics table within my database.
Within my rake file:
require 'metrics_service'
namespace :metrics do
#metrics_service = MetricsService.new
task :calculate_metrics => [:check_for_new_month, :update_customers, :update_churn, :do_more_stuff] do
puts "Donezo!"
end
# ...more cool tasks
end
And my MetricsService within lib/metrics_service.rb:
class MetricsService
def initialize
#metrics = MonthlyMetric.last
#total_customer_count = total_customers.count
assign_product_values
end
# Methods to do all my cool things...
end
Whenever I try to run something like rake:db:migrate, I get the following error:
NameError: uninitialized constant MetricsService::MonthlyMetric
I'm not sure why it's trying to refer to MonthlyMetric the way it is... As a class within the MetricsService namespace..? It's not like I'm trying to define MonthlyMetric as a nested class within MetricsService... I'm just trying to refer to it as an ActiveRecord query.
I've done other ActiveRecord queries, for example User, in other services within the same directory.
What am I doing wrong here?
I think if you just add => :environment to the end of your rake task, that may fix the problem.
As in:
task :calculate_metrics => [:check_for_new_month, :update_customers, :update_churn, :do_more_stuff] => :environment do
I've run into similar problems where Rails does not initialize the correct environment without this tacked on to each rake task.

How to call the function from the controller to the rake file : Ruby on Rails

I'm trying to add the function that is defined in the controller.rb file to the rake file. The Name of this controller file is "home_controller.rb". Here I put a part of the code.
class HomeController < ApplicationController
def rclass_creation
#output = "rclass is created"
a = Rclass.all
a.destroy_all...
I made my_namespace.rake file under tasks in the lib folder. Here I put the code a bit.
namespace :my_namespace do
desc "TODO"
task my_task2: :environment do
a = Userpool.all
a.each do |user|
puts user.name
end
puts "wow it is working"
I was able to call the database. I think this is because the rake file has an access to the database ( Userpool ). In this rake file, I want to call the function "rclass_creation" that was stated in the "home_controller.rb" because I want to avoid the situation that I have to do hardcopy.
The sudo code might look like this ( I hope (: )
namespace :my_namespace do
desc "TODO"
task my_task2: :environment do
status = rclass_creation <= from home_controller.rb
a = Userpool.all
a.each do |user|
puts user.name
end
puts "wow it is working"
The function I want to call is "rclass_creation" from the home_controller.
How can I call the function from the controller to rake file ? I'm looking forward to seeing opinions from the experts!!
You don't call controller methods from anywhere but the controller.
If you want to use a controller action from a rake file you should invoke it with a HTTP request. Just like any client would do.
You can initialize a controller and call methods on it but that's really hacky. Don't.
If you need to share code between your rake task and controller than it should not be in a controller at all.
So where do you put the code?
Does it act on a model? Is it independent from application state? Does it belong in a model? Put in a your model.
Is a mixin? Or just a plain method with takes some input and shoots something out? Put in into a helper or a plain old module.
Is it some kind of service that takes input and does something with models? Put it in a service object.

Method calls in one rake file task being handled by methods of same name in another rake file

Using Rails 2.3.*
Say I have a method called some_method() in two rake files - A.rake and B.rake.
I'm finding that if I call some_method() in B.rake, the method in A.rake is what actually gets called.
So what's the best approach to defining helpers methods inside rake files that will be "local" to the rake task defined in that file?
Thanks
You can define your helper within a task to make it available to that task and all subsequent ones:
desc 'has access to local helper'
task :accessible do
def helper
return "the helper"
end
puts "I have access to #{helper}"
end
desc 'has access too'
task 'after-accessible' => ['accessible'] do
puts "this ran after 'accessible' but still has access to '#{helper}"
end
desc 'does not have access to the helper'
task :outside do
puts helper # fails if runs before :accessible
end
Perhaps the best thing to do though is to refactor your Rakefiles and the helper code so that the two Rakefiles do not load each other.

How to invoke Rails cache sweeper from a rake task?

I set up a Rails cache sweeper like below:
class ArticleSweeper < ActionController::Caching::Sweeper
observe Article
def sweep(article)
expire_page articles_path
expire_page author_path(article.author)
FileUtils.rm_rf "#{page_cache_directory}/articles/page"
end
alias_method :after_update, :sweep
alias_method :after_create, :sweep
end
I connected this cache sweeper with the article controller.
The cache sweeping works as a result of controller action of create and update.
However, the main way I update my database records are through a rake task, which uses active record methods of save, update_attributes, etc on the database records.
I thought about adding a model observer class and let this class invoke the sweeper method.
There're alot of comments online about how the cache sweeper should be triggered by model actions instead of controller actions.
But for my application, the rake task may add 100 records when it executes. So I don't want the cache sweeper to execute 100 times. As it won't be efficient.
I think it's better to execute it once at the end of the rake task.
So I tried adding this in my rake task:
ArticleSweeper.instance.sweep(Article.last)
I put a "debugger" statement in there and see that the sweep() method WAS being executed.
However, the cache files are NOT being removed, even though the same code works when it's triggered by controller action.
So my question is: is it possible to trigger the cache sweeper method from a rake task?
And if so, what can I do to make it work?
Thanks
I was having the same problem and stumbled across this: http://www.madebymade.co.uk/blog/2013/08/cache-sweepers-invalidating-caches-from-outside-the-controller/

Rails: invoking a mailer from a rake task with parameter/env variable

Using this railscast http://railscasts.com/episodes/127-rake-in-background?autoplay=true as an example/inspiration (i.e. I'm not trying to implement the rails cast code, just use it as an inspiration), I tried to move a mailer, that was triggered by an after_create callback in the user.rb model, into a rake task, so it would run in the background. The mailer was working before I moved it into a rake task, but it's not working anymore.
Instead of calling the mailer from the User.rb model, which it was how it was set up originally (see the commented out code in user.rb), I instead call the rake task, which then invokes the UserMailer.welcome_email method.
In the original code, "self" was submitted (from User.rb) as a parameter to the method welcome_email(user) in user_mailer.rb. In my attempt to turn it into a rake task, I assigned "self" to USER_INSTANCE, which is supposed to be picked up in the mailer.rake as ENV["USER_INSTANCE"]. This was also suggested by the railscast.
Somewhere along the way it's not working. Any ideas?
User.rb
after_create :send_welcome_email
def send_welcome_email
system "rake :send_mailing USER_INSTANCE=self &" #note the & forks the task
#UserMailer.welcome_email(self).deliver <-- how it was originally.
end
mailer.rake
desc "Send mailing"
task :send_mailing => :environment do
UserMailer.welcome_email(ENV["USER_INSTANCE"]).deliver #moved from user.rb to here but now with environment variable instead of parameter
end
unchanged User_mailer.rb
class UserMailer < ActionMailer::Base
default :from => "blahblah#gmail.com"
def welcome_email(user)
mail(:to => user.email, :subject => "Invitation Request Received")
end
end
currently you are doing this
system "rake :send_mailing USER_INSTANCE=self &"
which is the same as going to the command line and typing
rake :send_mailing USER_INSTANCE=self &
self is just a literal string, I think what you are trying to do is this
system "rake :send_mailing USER_INSTANCE=#{self} &"
but that will end up being the equivalent of running this on the command line
rake :send_mailing USER_INSTANCE=<User::xxxxx>
rake won't serialize this into your User ActiveRecord object;
when you shell out with system there is no relation to the calling code
an alternative - your rake task could take an integer - user_id and then access the record via User.find
but it gets more complicated as after_create is going to be running in a transaction so once your rake task runs it may or may not have finished that transaction
I would advise against trying to re-invent a way to do background processing in rails, there are already good tried and true solutions available
see http://railscasts.com/?tag_id=32 for some options

Resources