I have delayed_job installed as a Gem and in my Gemfile. However, when I try to use delay in my controller as:
def send_warn_admin_email
UserMailer.delay.warn_admin_email(self).deliver
end
I get: undefined methoddelay' for UserMailer:Class`
What can cause this problem?
Thanks
From Documentation.
Due to how mailers are implemented in Rails 3, we had to do a little work
around to get delayed_job to work.
# without delayed_job
Notifier.signup(#user).deliver
# with delayed_job
Notifier.delay.signup(#user)
Remove the #.deliver# method to make it work. It's not ideal, but it's
the best we could do for now.
Bye
Related
I am using Rails 4 w/ the impressionist and resque gem.
I am using impressionist to log unique session hits on my article show page. Due to performance issues and no need to display hits to users (it is for admins only), I would like to move logging impressions off into the background.
Normally I would log an impression using impressionist(#article, unique: [:session_hash]) but to move it off into the bg via resque I am now doing something like this...
articles_controller:
def show
.
.
.
Resque.enqueue(ImpressionLogger, #article.id)
end
app/workers/impression_logger.rb:
class ImpressionLogger
#queue = :impression_queue
def self.perform(article_id)
article = Article.find(article_id)
impressionist(article, unique: [:session_hash])
end
end
When I set it up like this, when resque tries to process the job, it is returning undefined method "impressionist" for ImpressionLogger:Class. What do you guys think the best way to go about this is? I am not sure how to include impressionist methods inside of my resque worker.
The issue
Your problem stems from the fact that it looks like Impressionist works on the controller level due to including a module with the impressionist method in an engine initializer on any instances of ActionController:
https://github.com/charlotte-ruby/impressionist/blob/master/lib/impressionist/engine.rb#L11
You're trying to call the impressionist method from a regular class being invoked in a Resque job, so it's not going to have that method defined.
Solution
It's kind of gross, but if you really want to use impressionist, we can delve into this... Looking at the actual implementation of the impressionist method found here, we see the following:
def impressionist(obj,message=nil,opts={})
if should_count_impression?(opts)
if obj.respond_to?("impressionable?")
if unique_instance?(obj, opts[:unique])
obj.impressions.create(associative_create_statement({:message => message}))
end
else
# we could create an impression anyway. for classes, too. why not?
raise "#{obj.class.to_s} is not impressionable!"
end
end
end
Assuming that you'd be calling something like this manually (as you want to from a resque job) the key are these three lines:
if unique_instance?(obj, opts[:unique])
obj.impressions.create(associative_create_statement({:message => message}))
end
The if wrapper only seems to be important if you want to implement this functionality. Which it looks like you do. The call to associative_create_statement seems to be pulling parameters based off of the controller name as well as parameters passed from Rack such as the useragent string and ip address (here). So, you'll have to resolve these values prior to invoking the Resque job.
What I would suggest at this point is implementing a Resque class that takes in two parameters, an article_id and the impression parameters that you want. The resque class would then just directly create the impression on the impressionable object. Your Resque class would become:
class ImpressionLogger
#queue = :impression_queue
def self.perform(article_id, impression_params = {})
article = Article.find(article_id)
article.impressions.create(impression_params)
end
end
And your controller method would look something like this:
def show
.
.
.
Resque.enqueue(ImpressionLogger, #article.id, associative_create_statement({message: nil})) if unique_instance?(#article, [:session_hash])
end
Disclaimer
There's a fairly big disclaimer that comes with doing it this way though... the method associative_create_statement is marked protected and unique_instance? is marked private... so neither of these is part of the impressionist gem's public API, so this code might break between versions of the gem.
Is impressionist installed properly with bundler? If so Rails should be loading it into your environment. I would check whether you can access impressionist functionality elsewhere in your Rails code (i.e. without going through Resque) as the first step to debugging this.
How are you starting your resque workers? If you need your Rails environment loaded, try rake environment resque:work.
https://github.com/resque/resque/wiki/FAQ#how-do-i-ensure-my-rails-classesenvironment-is-loaded
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.
I am running delayed_job 3.0.5 (delayed_job_active_record 0.4.1) with Rails 3.2.12. I am having problems with some of my jobs failing because of "Deserialization". Here is a really simple example of one of the failing handlers:
--- !ruby/struct:Delayed::PerformableMethod
object: LOAD;Project;924951
method: :send_project_open_close_without_delay
args: []
When I try to invoke this job:
Delayed::DeserializationError: Job failed to load: undefined method `members' for nil:NilClass.
Everyone seems to think this is caused by an AR object that no longer exists. In my case, I can run the handler just fine:
Project.find(924951).send_open_close_without_delay
What else could be causing the error?
I think I also experienced this issue when we upgrade to rails 3.2. The error I got was caused by the yaml handler used by delayed job. Try adding the following to config/boot.rb
require 'rubygems'
require 'yaml'
YAML::ENGINE.yamler = 'syck'
Just in case anyone ever runs into this issue. I figured out the cause was some of the jobs were injected with an older version of Delayed Job. So when the newer Delayed Job attempted to process them, it was unable to deserialize the handler.
This was caused by an upgrade to Delayed::Job that changed the serialization handler AND changed the type of object used for PerformableMethod. If you want to fix all old jobs and stick with the new defaults for Delayed::Job, you can fix failing ones with this query:
Delayed::Job.where('failed_at is not null').each do |dj|
dj.handler = dj.handler.gsub('struct:Delayed', 'object:Delayed')
Delayed::Worker.new.run(dj)
end
The change occurred around here: https://github.com/collectiveidea/delayed_job/commit/7b8a79a72c0ee5d8bac4bc0b183d1cce9cedff85
Making PerformableMethod a class instead of a Struct.
I'm working to learn how to user delayed_job on my rails 3 + heroku app.
I currently have the following which emails on a request (not delayed job) but it works!
UserMailer.conversation_notification(record.commentable, participant, record, #comments).deliver
I updated that to this to start using delayed_job:
Delayed::Job.enqueue UserMailer.conversation_notification(record.commentable, participant, record, #comments).deliver
But that error'd with: "ArgumentError (Cannot enqueue items which do not respond to perform):"
I also tried:
UserMailer.delay.conversation_notification(record.commentable, participant, record, #comments)
But that error'd with:
NoMethodError (undefined method `delay' for UserMailer:Class):
Any delayed_job guru's out there? Thanks
From the docs https://github.com/collectiveidea/delayed_job
Your second method was correct which removes the .deliver method:
UserMailer.delay.conversation_notification(record.commentable, participant, record, #comments)
If you are getting an undefined method delay did you add DelayedJob to the Gemfile?
gem "delayed_job"
Since including the delayed_job will add the "delay" method to everything.
I have mixed results with using delay, and I've found it very challenging to debug. So you are not alone! But when you get it working, its worth it.
I've learned to save my object before calling delay on it. Typically I will trigger my job from an after_save call back.
As an experiment, for awhile I was using a different pattern. I'd create a job object for each job that I have. For example, I would call
Delayed::Job.enqueue(PersonJob.new(#person.id))
Elsewhere in my project I would create the job object. In Rails 2, I put these in lib/ if you do that with rails 3, you need to alter the application.rb config.autload_path
class PersonJob < Struct.new(:person_id)
def perform
person = Person.find(person_id)
#do work
end
end
config.autoload_paths += Dir["#{config.root}/lib/**/"]
I just had a look at the documentation, it's been a while since I actually used delayed_job...
Jobs are Ruby objects with a method called perform, so you'd need enqueue an object which does
UserMailer.conversation_notification(record.commentable, participant, record, #comments).deliver
in its perform method.
Alternatively, you can use send_later:
UserMailer.conversation_notification(record.commentable, participant, record, #comments).send_later(:deliver)
In order to override the table_exists? method in the Rails PostgreSQL adapter I have tried the following in an initializer file:
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
def table_exists?(name)
raise 'got here'
end
end
This will raise the the following error:
uninitialized constant ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
I believe this would have worked in previous versions of rails and I even found a small plugin that did something like this in Rails 2.3.6. Also I only encounter this error when I am trying to run a rake task like db:migrate and not when I start my application server.
Could someone show me the right way to do this and / or explain why PostgreSQLAdapter doesn't seem to be loaded when I am in an initializer file?
Instead of config/initializers, place that code in lib/ folder.
While this means that the active_record is loaded after the rails initializers, which is unusual. I ll update this with more detail, once I am done investigating the whole flow. If you want some more details about the rails 3 initialization process, check out this link:
http://ryanbigg.com/guides/initialization.html
I had success by moving this code into a Rails plugin. It is a little bit more overhead, but it is working consistently when I run rails s and when I run rake db:migrate.
I just followed the rails guide page on the topic and ran
rails generate plugin rails_patches --with-generator
and moved my init.rb file into rails as recommended.
~vendor/
`~plugins/
`~rails_patches/
|~lib/
| `-rails_patches.rb
|~rails/
| `-init.rb
|+test/
|-install.rb
|-MIT-LICENSE
|-Rakefile
|-README
`-uninstall.rb
I put this code in init.rb:
require 'rails_patches'
I put this code in rails_patches.rb:
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
def table_exists?(name)
raise 'got here'
end
end
This now behaves as I expected.