Thor task list from within the class? - task

I have looked ALL over (google searched) and I cannot for the life of me figure out how to do this. Is it possible to get a list of the thor tasks defined in the current class? I'm trying to write a method that determines whether or not the argument(s) passed to thor are valid and to do this, I need a list of all the defined tasks. I could just create a list in some constant but I'd rather use built in tools if possible.
Ex:
#!/usr/bin/env ruby
require 'thor'
class Foo < Thor
desc 'task_1', 'The first task'
def task_1
puts 1
end #task_1
desc 'task_2', 'The second task'
def task_2
puts 2
end #task_2
desc 'check_args', 'Checks that the arguments are valid.', :hide => true
# get a list of the tasks defined in this class and check against ARGV
if !valid
invoke :help
exit
end #if
end #check_args
end #Foo
If my question isn't clear enough or I am just going about this ALL wrong, please let me know :)
Thanks

thor -T would list all your tasks as well.

And of course, right after posting this question, I figure it out.
Foo.tasks would return a hash {task_name => [array of task info]}

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.

Passing arguments to rake task from model not working

I am trying to call a rake task to send an email to a list of subscribers from a controller action like so:
Controller action:
def send_digest
#article_ids = params[:article_ids]
#subject = params[:subject]
EmailDigest.send_email_digest("weekly_digest_task", #article_ids, #subject)
redirect_to new_digests_path
end
email_digest.rb:
require 'rake'
class EmailDigest < ActiveRecord::Base
def self.send_email_digest(weekly_digest_task, article_ids, subject)
load File.join(Rails.root, 'lib', 'tasks', 'send_email_digest.rake')
Rake::Task['weekly_digest_task'].invoke("\\\"#{article_ids}\\\"","\\\"#{subject}\\\"")
end
end
And here is the send_email_digest.rake:
task :weekly_digest_task, [:article_ids,:subject] => :environment do |task, args|
articles = args.article_ids
subject = args.subject
article_objects = []
articles.each do |m|
article_objects << Article.find_by_id(m)
end
EmailDigest.all.each do |subscriber|
DigestMailer.weekly_digest(subscriber, article_objects, subject).deliver
end
end
The redirect occurs and I am taken to that page with no errors, however no emails get sent. As such, I have no idea why this is not working.
Any ideas?
You are passing a string to your task, so unless you extract the array of ids back from the string, you are essentially enumerating over the string rather than the items. I would like to know what version of ruby you are using because in 1.9+ string is no longer an enumerable and so you should be getting an error.
However all this is not necessary because you can directly pass an array to invoke.
Your code is also unnecessarily convoluted. Especially it is not advisable to load file from within a method (as it will cause the file contents to be evaluated on each invocation of the method). It could easily be required at the top.
Ideally, it would be an better if you move the logic from rake task to a method in the model or a mediator and just call that from both the rake task as well as the mailer.

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.

Rails tire:import with custom logic

I have a Ruby on rails 3.2 application where I'm trying to use Tire and Elastic Search.
I have a User model that has the following declarations:
include Tire::Model::Search
include Tire::Model::Callbacks
I then carried out an initial import of records into Elastic Search by calling:
rake environment tire:import CLASS=User FORCE=true
Is it possible to customise the import task, such that it skips one user? I have a system user that I would prefer not to be indexed?
First, the Rake task is only a convenience method for the most usual cases, when trying elasticsearch/Tire out, etc. For more complex situations, you should write your own indexing code -- it should be very easy.
Second, if you have certain conditions whether the record is indexed or not, you should do what the README instructs you: don't include Tire::Model::Callbacks and manage the indexing lifecycle yourself, eg with:
after_save do
update_index if state == 'published'
end
I've found a rough solution to my problem and wanted to post something back, just in case someone else comes across this. If anyone has any better suggestions, please let me know.
In the end I wrote a tire task that calls the regular import all and then subsequently deletes the system account from the index.
namespace :tire do
desc 'Create search index on User'
task :index_users => :environment do
ENV['CLASS'] = 'User'
ENV['FORCE'] = 'TRUE'
Rake::Task['tire:import'].invoke
#user = User.find_by_type('System')
User.tire.index.remove #user
end
end

ActiveRecord::Dirty and Rake

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.

Resources