delayed_job: NoMethodError - ruby-on-rails

Here is my tiny Rails3 controller:
class HomeController < ApplicationController
def index
HomeController.delay.do_stuff
end
def self.do_stuff
puts "Hello"
end
end
Upon accessing index, the job gets correctly inserted in database:
--- !ruby/struct:Delayed::PerformableMethod
object: !ruby/object:Class HomeController
method_name: :do_stuff
PROBLEM: When executing bundle exec rake jobs:work, I get:
Class#do_stuff failed with NoMethodError:
undefined method `do_stuff' for #<Class:0x0000000465f910>
Despite the fact that HomeController.do_stuff works perfectly. Any idea?

See https://github.com/collectiveidea/delayed_job/wiki/Common-problems#wiki-undefined_method_xxx_for_class in documentation.
It seems that you should have
..object: !ruby/class HomeController method_name ...
in the database, but you have
..object: !ruby/object:Class HomeController method_name ...
instead. Which is bad.
Even delayed_job author don't know the reason. It somehow depends on the webserver you run in on. Try the wiki's recommendation.

I had the same problem.
I found several discussions stating that different yaml parsers were used by when the job was put in the queue by the web application and when it was executed later.
Some suggest the psych parser should be used. Some suggests syck. First I tried psych but ended up with incompability issues with other gems. So I picked syck.
I wasn't able to sort out which configuration files that are used by the web server and the queue. After a lot of experimentation I ended up with the following configurations (all of them in the top of the file):
#application.rb
require File.expand_path('../boot', __FILE__)
require 'rails/all'
require 'yaml'
YAML::ENGINE.yamler= 'syck'
# ...
and
#environment.rb
require 'yaml'
YAML::ENGINE.yamler= 'syck'
# ...
and
#boot.rb
require 'yaml'
YAML::ENGINE.yamler= 'syck'
require 'rubygems'
# ...
I'm using Ruby 1.9.3, Rails 3.2.8, Webrick, delayed_job 3.0.3

I my case the problem was mainly because I was passing Hash as a parameter to object that was passed to delayed_job queue.
But after 25 trails I have come to conclusion that delayed_job accepts objects only with integer as parameter. Hence I stored all the parameters in the database and then passed that record id as parameter to delayed_job and inside the perform function we can access all the parameters with that record id and delete that record after fetching that data.
Delayed::Job.enqueue LeadsJob.new(params[:customer]) # this job will be queued but will never run, this is because of the way Delayed_job serializes and De-serializes the objects.
instead do something like this
#customer = Customer.create(params[:customer])
Delayed::Job.enqueue LeadsJob.new(#customer.id)
If the customer details was just to pass the parameters then delete that record inside the function.
Please ping me if you need more details on the same.
The problem might be also because of the YAML parser that Delayed_Job uses but I haven't tried out that option that is mentioned by #Stefan Pettersson

Related

Module method in Rails 6 YAML fixtures

One model in a Rails application I'm working on has a JSON field. This can be created in the fixtures with:
first_record:
name: "First Record"
metadata: <%= File.read("#{Rails.root}/test/fixtures/files/metadata/default.json") %>
...and so on. But, for some tests a value other than the default is needed in the metadata; this could be done by having separate metadata files for each record, or embedding JSON in the YAML file, but there are many records and there is much metadata. So, another option is to create a module in lib/modules with something like this:
module TestMetadataHelper
def create_metadata(args)
metadata = JSON.parse(File.read("#{Rails.root}/test/fixtures/files/metadata/default.json"))
args.each do |key, value|
metadata[key] = value
end
metadata.to_json
end
end
And, to make this accessible in tests, this goes into test_helper.rb:
ActiveRecord::FixtureSet.context_class.send :include, TestMetadataHelper
The, in the fixtures:
metadata: <%= create_metadata(field_to_change: "custom value") %>
Tests run perfectly well using this method, but there's one problem:
$ RAILS_ENV=test bundle exec rake db:fixtures:load
rake aborted!
NoMethodError: undefined method `create_metadata' for #<#<Class:0x00007feeba246958>:0x00007feeba246890>
So, how about fixing this by loading my module in config/environments/test.rb?
DEPRECATION WARNING: Initialization autoloaded the constant TestMetadataHelper.
Being able to do this is deprecated. Autoloading during initialization is going
to be an error condition in future versions of Rails.
Reloading does not reboot the application, and therefore code executed during
initialization does not run again. So, if you reload TestMetadataHelper, for example,
the expected changes won't be reflected in that stale Module object.
This autoloaded constant has been unloaded.
Please, check the "Autoloading and Reloading Constants" guide for solutions.
Having looked at that guide it's not entirely clear to me how best to handle this situation, so if anyone has any thoughts (e.g. how to fix this warning, or a better way to deal with the test requirements), please do comment.

ActiveJob uninitialized constant

I have a weird problem with ActiveJob.
From a controller I'm executing the following sentence:
ExportJob.set(wait: 5.seconds).perform([A series of parameters, basically strings and integers])
ExportJob.rb
require_relative 'blablabla/resource_manager'
class ExportJob < ActiveJob::Base
def perform
ResourceManager.export_process([A series of parameters, basically strings and integers])
end
end
When the controller/action is executed for the first time the process goes fine, but the second time an error is thrown:
uninitialized constant ExportJob::ResourceManager
The weird thing is that this is not the only job I have in my project, the other ones are being executed without any problem.
I'm Attaching some information of my project:
development/production.rb
config.active_job.queue_adapter = :delayed_job
Gemfile:
gem 'delayed_job'
gem 'delayed_job_active_record'
Any clue would be a help for me.
Thanks in advance!
Constants don't have global scope in Ruby. Constants can be visible from any scope, but you must specify where the constant is to be found.
Without :: Ruby looks for the ResourceManager constant in lexical scope of the currently executing code (which is ExportJob class, so it looks for ExportJob::ResourceManager).
The following should work (assuming that ResourceManager is defined as a top level constant (eg not nested under any module/class):
class ExportJob < ActiveJob::Base
def perform
::ResourceManager.export_process(*args)
end
end

Active Merchant - uninitialized constant ActiveSupport::XmlMini_REXML::StringIO

I have activemerchant 1.16.0 and rails 3.0.5.
I am trying to build a basic code to communicate with PayPal's gateway using active merchant.
if credit_card.valid?
# or gateway.purchase to do both authorize and capture
response = gateway.authorize(1000, credit_card, :ip => "127.0.0.1")
if response.success?
gateway.capture(1000, response.authorization)
puts "Purchase complete!"
else
puts "Error: #{response.message}"
end
else
puts "Error: credit card is not valid. #{credit_card.errors.full_messages.join('. ')}"
end
I get the following error:
/Library/Ruby/Gems/1.8/gems/activesupport-3.0.9/lib/active_support/xml_mini/rexml.rb:20:in `parse': uninitialized constant ActiveSupport::XmlMini_REXML::StringIO (NameError)
This error propagates from the gateway.authorize() call.
Any idea what's wrong with my setup?
Thanks.
According to the question, it doesn't work when the code is by itself, but works when require "stringio" is added.
My suspicion is that ActiveMerchant is unit-tested, but for some reason the dependency on StringIO isn't detected by those unit tests, possibly because other parts of the unit testing code indirectly requires stringio.
One thing I recently found out was that require 'yaml' gives you the stringio library as a side effect:
StringIO.new
# NameError: uninitialized constant StringIO
# from (irb):1
require "yaml"
# => true
StringIO.new
# => #<StringIO:0x7fb7d48ce360>
RUBY_VERSION
# => "1.8.7"
and it wouldn't be hard to imagine unit tests for ActiveMerchant (or other parts of Rails) requiring yaml.
However, this is only speculation. I haven't checked, as I don't use Rails.
Andrew Grimm pretty much hit the nail on the head with his original comment on this question. The missing require 'stringio' is indeed the issue. But it is a bug with Rails, more specifically ActiveSupport 3.0.9 (which is where the error seems to be coming from). We can trace it down using the git commit history of rails.
First we need to check out rails and switch to the v3.0.9 tag. If we now look at activesupport/lib/active_support/xml_mini/rexml.rb we see that require 'stringio' is not there. In and of itself this is not significant, but bear with me. We can now switch to the next tag (v3.0.10.rc1), and we'll see that the file hasn't been updated (it is likely that this version of rails will have the same issue). Next tag in line is v3.1.0.beta1, notice that this time around there is a require 'stringio' at the top of the file.
We can check out the commit that brought in this change (this one here from Jan 19th 2011). The commit message says:
fixed a missing require that causes trouble when using AS in a
non-rails env
This would indicate that as long as you're in a rails environment this issue wouldn't surface. So, my guess is something about the environment caused the issue to come up, may have something to do with the fact that the OP says they are using rails 3.0.5, but the error is coming from activesupport-3.0.9. Perhaps the code was called from a rake task that forgot to inherit from :environment (difficult to say without more info). Either way, putting require 'stringio' at the top of the code is definitely the fix, until you can upgrade to Rails 3.1 (once it comes out) at which point the require will no longer be needed.

Access a class method from a model in the Rakefile / Ruby on Rails 3

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.

How do i integrate Hoptoad with DelayedJob and DaemonSpawn?

I have been happily using the DelayedJob idiom:
foo.send_later(:bar)
This calls the method bar on the object foo in the DelayedJob process.
And I've been using DaemonSpawn to kick off the DelayedJob process on my server.
But... if foo throws an exception Hoptoad doesn't catch it.
Is this a bug in any of these packages... or do I need to change some configuration... or do I need to insert some exception handling in DS or DJ that will call the Hoptoad notifier?
In response to the first comment below.
class DelayedJobWorker < DaemonSpawn::Base
def start(args)
ENV['RAILS_ENV'] ||= args.first || 'development'
Dir.chdir RAILS_ROOT
require File.join('config', 'environment')
Delayed::Worker.new.start
end
Try monkeypatching Delayed::Worker#handle_failed_job :
# lib/delayed_job_airbrake.rb
module Delayed
class Worker
protected
def handle_failed_job_with_airbrake(job, error)
say "Delayed job failed -- logging to Airbrake"
HoptoadNotifier.notify(error)
handle_failed_job_without_airbrake(job, error)
end
alias_method_chain :handle_failed_job, :airbrake
end
end
This worked for me.
(in a Rails 3.0.10 app using delayed_job 2.1.4 and hoptoad_notifier 2.4.11)
Check out the source for Delayed::Job... there's a snippet like:
# This is a good hook if you need to report job processing errors in additional or different ways
def log_exception(error)
logger.error "* [JOB] #{name} failed with #{error.class.name}: #{error.message} - #{attempts} failed attempts"
logger.error(error)
end
I haven't tried it, but I think you could do something like:
class Delayed::Job
def log_exception_with_hoptoad(error)
log_exception_without_hoptoad(error)
HoptoadNotifier.notify(error)
end
alias_method_chain :log_exception, :hoptoad
end
Hoptoad uses the Rails rescue_action_in_public hook method to intercept exceptions and log them. This method is only executed when the request is dispatched by a Rails controller.
For this reason, Hoptoad is completely unaware of any exception generated, for example, by rake tasks or the rails script/runner.
If you want to have Hoptoad tracking your exception, you should manually integrate it.
It should be quite straightforward. The following code fragment demonstrates how Hoptoad is invoked
def rescue_action_in_public_with_hoptoad exception
notify_hoptoad(exception) unless ignore?(exception) || ignore_user_agent?
rescue_action_in_public_without_hoptoad(exception)
end
Just include Hoptoad library in your environment and call notify_hoptoad(exception) should work. Make sure your environment provides the same API of a Rails controller or Hoptoad might complain.
Just throwing it out there - your daemon should require the rails environment that you're working on. It should look something along the lines of:
RAILS_ENV = ARGV.first || ENV['RAILS_ENV'] || 'production'
require File.join('config', 'environment')
This way you can specify environment in which daemon is called.
Since it runs delayed job chances are daemon already does that (it needs activerecord), but maybe you're only requiring minimal activerecord to make delayed_job happy without rails.

Resources