How can I run an ActiveJob in Rails console for debugging? - ruby-on-rails

I currently have an ActiveJob that I've created and use Sidekiq to queue it. I'm wanting to debug the job, but for me to see any messages I program into it I have to check my log files. I feel like it would be more convenient to be able to see my puts messages in my job in the Rails Console. When I run the perform_later method though in rails console it just queues the job up and I never see the messages in console. Is there a way to make it where I will see them in the console?

You can run a job with perform_now.
For example...
class Foo < ActiveJob::Base
def perform(arg1)
puts "Hello #{arg1}"
end
end
Foo.perform_now('world')

You can temporarily set your queue adapter to inline.
Right now your code in application.rb will look something like this:
Rails.application.config.active_job.queue_adapter = :sidekiq
Just comment out the line
# Rails.application.config.active_job.queue_adapter = :sidekiq
This will run your job inline, and you should see the results in the console.

You can run with new, Eg.Foo.new.perform(ar_1, ar_2)

There is a configuration line you can add in development.rb
require 'sidekiq/testing/inline'
This should enable inline testing for development.

Related

How can I turn all my 'perform_later's into 'perform_now's locally?

I'm working on a product that calls of perform_later jobs. This works for our product in production because we have a series of workers who will run all the jobs.
But, when I'm using the app locally, I don't have access to these workers, and I'd like to change all the perform_laters into perform_nows only when I use the app locally.
What's the best way to do this? One idea I had was to add something in my env file that would add a variable to make all perform_laters into perform_nows -- but I'm not sure what a flag or variable like that would look like.
Ideas?
The clean solution is to change the adapter in development environment.
In your /config/environments/development.rb you need to add:
Rails.application.configure do
config.active_job.queue_adapter = :inline
end
"When enqueueing jobs with the Inline adapter the job will be executed immediately."
In your app you can have:
/my_app/config/initializers/jobs_initializer.rb
module JobsExt
extend ActiveSupport::Concern
class_methods do
def perform_later(*args)
puts "I'm on #{Rails.env} envirnoment. So, I'll run right now"
perform_now(*args)
end
end
end
if Rails.env != "production"
puts "including mixin"
ActiveJob::Base.send(:include, JobsExt)
end
This mixin will be included on test and development environments only.
Then, if you have the job in:
/my_app/app/jobs/my_job.rb
class MyJob < ActiveJob::Base
def perform(param)
"I'm a #{param}!"
end
end
You can execute:
MyJob.perform_later("job")
And get:
#=> "I'm a job!"
Instead of the job instance:
#<MyJob:0x007ff197cd1938 #arguments=["job"], #job_id="aab4dbfb-3d57-4f6d-8994-065a178dc09a", #queue_name="default">
Remember: Doing this, ALL your Jobs will be executed right now on test and dev environments. If you want to enable this functionality for a single job, you will need to include the JobsExt mixin in that job only.
We solved this by calling an intermediate method which then called perform_later or perform_now depending on Rails config:
def self.perform(*args)
if Rails.application.config.perform_later
perform_later(*args)
else
perform_now(*args)
end
end
And simply updated environments configs accordingly

Pry like debugger for Sidekiq?

I've tried pry and remote-pry, but no luck. I'm familiar with logging, but I want to be able to step thru my code and look at variables.
Does anyone know of anything I can use to debug Sidekiq?
Workers are designed to be trivial to run. Put pry in your worker code and run it in the rails console.
> MyWorker.new.perform(some_args)
The best thing I've come up with is this gem gem 'pry-remote' it works great and stops all processes from running. And it works like pry just put in binding.remote_pry and you've got a stopping point.
You can use byebug but you have to require it inside the class definition of the job.
For instance,
class SomeJob < ActiveJob::Base
require 'byebug'
def perform(*args)
byebug
end
end
then in your rails console run
SomeJob.perform_now(*args)
This will cause a breakpoint to appear where ever you have byebug called with your typical byebug prompt inside your rails console.

Using Whenever gem with Rails Active Job to schedule a batch email job

I'm trying to understand how to use whenever properly, or if I'm even using it for the right thing. I've created a job:
class ScheduleSendNotificationsJob < ActiveJob::Base
queue_as :notification_emails
def perform(*args)
user_ids = User.
joins(:receipts).
where(receipts: {is_read: false}).
select('DISTINCT users.id').
map(&:id)
user_ids.each do |user_id|
SendNotificationsJob.create(id: user_id)
Rails.logger.info "Scheduled a job to send notifications to user #{user_id}"
end
end
end
I'd like to perform this job ever day at a set time. The job polls to see if there are any outstanding notifications, batches them, and then sends them to users so that a user can get one email with a bunch of notifications instead of a bunch of emails with one notification per email. I tried doing this with Delayed Job, but it seems it's not designed to schedule something on a recurring basis. So now I'm trying to do it with the whenever gem, but I can't seem to figure out how to set it up properly.
This is what I have in my config/schedule.rb file:
every 1.minute do
runner ScheduleSendNotifications.create
end
When I run whenever -i in the console I get the following:
Lorenzs-MacBook-Pro:Heartbeat-pods lorenzsell$ whenever -i
config/schedule.rb:13:in `block in initialize': uninitialized constant Whenever::JobList::ScheduleSendNotifications (NameError)
What am I doing wrong here? Should I be using something else? I'm just learning ruby and rails so any help is very much appreciated. Thank you.
The whenever gem takes a string as the argument to the runner function. Whenever doesn't actually load the Rails environment so it doesn't know about your ScheduleSendNotifications class.
The code below should get the crontab set up correctly to run your job.
every 1.minute do
runner "ScheduleSendNotifications.create"
end
From your project directory run whenever -w to set up the crontab file. Run crontab -l to view the written crontab file. Every minute the system will execute your Rails runner. From there you may need to debug your ScheduleSendNotifications.create code if something isn't working.

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.

How do I use a custom log for my rake tasks in Ruby on Rails?

I have a rake task that calls functions like this:
namespace :blah do
task :hello_world => :environment do
logger.info("Hello World")
helloworld2
end
end
def helloworld2
logger.info("Hello Again, World")
end
I want the log output to a custom log, and I really don't want to have to pass a log reference every time I make a function call. I found this somewhere (can't find it again):
def logger
##logger ||= Logger.new("#{RAILS_HOME}/log/blah.log")
end
But this does not work for me and I am not sure what it even does because I grabbed the code a long time ago and haven't used it until now. I can't search for ## on google (tried +"##" rails) to see what it does. Any help on this issue would be great. I am hoping for a quick solution and not having to install a gem or plugin (unless there is a really really good reason to.
Thanks!
rake disables logging in production mode. make sure you're running in development mode if you want it to log
What do you mean by "does not work for me"? I just tried this same code and it worked - created a new log file and put some text in it.
##logger is a class variable, it's a language issue, not Rails' one. I believe there's no need in further explanations :)
You've probably mistaken typing "function helloworld2" :)
Advanced Rails Recipes Recipe 84 from #topfunky shows how to define a custom logger. He has some code in the environment config file (production would look like this): RAILS_ROOT/config/environments/production.rb:
config.logger = RAILS_DEFAULT_LOGGER = Logger.new(config.log_path)
I'd test that out instead of redefining the class variable as you have. He might have something on http://nubyonrails.com to check as well.

Resources