undefined method for `perform_later' on an ApplicationJob - ruby-on-rails

Running Rails 7 and creating a simple job that extends ApplicationJob, the perform method works, but not the perform_later. Do I need to install a queuing backend for that method to work?
In app/jobs/simple_file_write_test_job.rb :
1 class SimpleFileWriteTestJob < ApplicationJob
2 queue_as :default
3
4 def perform(*args)
5 f = File.open("/tmp/project_job_test.txt", "a")
6 f.puts("#{self.job_id}: #{DateTime.now}")
7 f.close
8 end
9 end
perform works:
irb(main):003:0> reload!; j = SimpleFileWriteTestJob.new; j.perform()
Reloading...
=> nil
I can see the file got written to:
/opt/project-web # tail -f /tmp/project_job_test.txt
e82cf2d1-f480-4bdc-bef2-975b084fe68d: 2022-07-12T19:10:53+00:00
But trying to run perform_later :
irb(main):004:0> reload!; j = SimpleFileWriteTestJob.new; j.perform_later()
Reloading...
(irb):4:in `<main>': undefined method `perform_later' for #<SimpleFileWriteTestJob:0x00007f6b007f0f90 #arguments=[], #job_id="07a747ad-fef7-4d28-8bd5-66ac7e31e2ac", #queue_name="default", #priority=nil, #executions=0, #exception_executions={}, #timezone="UTC"> (NoMethodError)
reload!; j = SimpleFileWriteTestJob.new; j.perform_later()
^^^^^^^^^^^^^^
Did you mean? perform
This stackoverflow answer says active-job is included starting from Rails 4.2. So I'm assuming I don't need it in the gem file.
Running Rails 7:
irb(main):005:0> Rails.version
=> "7.0.3"
Feels like I'm missing something embarrassingly simple.

#dbugger had the right answer in the comments of the question. As #engineersmnky stated, perform_later is a class method:
irb(main):010:0> reload!; j = SimpleFileWriteTestJob.new; SimpleFileWriteTestJob.perform_later(j)
Reloading...
Enqueued SimpleFileWriteTestJob (Job ID: 4d562bed-6336-4ccb-8325-f16e1bffec36) to Async(default)
=>
#<SimpleFileWriteTestJob:0x00007f6aff855180
#arguments=[],
#exception_executions={},
#executions=0,
#job_id="4d562bed-6336-4ccb-8325-f16e1bffec36",
#priority=nil,
#provider_job_id="8fd297e3-eeaf-474b-984b-0ff7c66113db",
#queue_name="default",
#successfully_enqueued=true,
#timezone="UTC">
irb(main):011:0> Performing SimpleFileWriteTestJob (Job ID: 4d562bed-6336-4ccb-8325-f16e1bffec36) from Async(default) enqueued at 2022-07-12T19:24:57Z
Performed SimpleFileWriteTestJob (Job ID: 4d562bed-6336-4ccb-8325-f16e1bffec36) from Async(default) in 12.25ms
Here's hoping they clarify the guide.

Related

Ruby FrozenString error in Sidekiq Worker

I am using Ruby on Rails and trying to use a Sidekiq worker, but at some point I'm running into an issue where the worker calls a view, the view calls a concern, and then the concern isn't able to update a variable in its function because of the FrozenString error.
For example, here's how my worker looks:
class ReportGeneratorWorker
include Sidekiq::Worker, ReportHelper
sidekiq_options queue: Rails.env.to_sym
def perform
ac_base = ApplicationController.new
body_html = ac_base.render_to_string template: "common/report_templates/generate_pdf.html.erb", layout: false
end
end
Again, the view inserts text that leverages a concern, but the concern doesn't allow it to update. See below for example:
[3] pry(#<#<Class:0x00007fec8ceea400>>)> html
=> "<ul>"
[4] pry(#<#<Class:0x00007fec8ceea400>>)> html.class.name
=> "String"
[5] pry(#<#<Class:0x00007fec8ceea400>>)> html << "Hello"
FrozenError: can't modify frozen String
from (pry):5:in `replacement_text'
Any idea why this is happening? If I define the variable again from the Pry console, then it actually works:
[1] pry(#<#<Class:0x0000557f07a25130>>)> html
=> "<ul>"
[2] pry(#<#<Class:0x0000557f07a25130>>)> html << "TEST"
FrozenError: can't modify frozen String
from (pry):2:in `replacement_text'
[3] pry(#<#<Class:0x0000557f07a25130>>)> html = "<ul>"
=> "<ul>"
[4] pry(#<#<Class:0x0000557f07a25130>>)> html << "TEST"
=> "<ul>TEST"
[5] pry(#<#<Class:0x0000557f07a25130>>)>
I was able to resolve this issue by replacing << with +=.

ArgumentError testing State Machine

I am using the following versions:
ruby 2.5.5
rails 5.2.3
state_machines-activerecord 0.6.0
I have a model Foo that has a state machine on it:
class Foo < ApplicationRecord
def post_activate
puts "hello from sunny post_activate"
end
state_machine :state, initial: :initial do
state :active
after_transition any => :active, :do => :post_activate
event :activate do
transition initial: :active
end
end
end
I'm trying to write an rspec test to ensure post_activate gets called after the transition to state :active.
I can test ok from the Rails console:
2.5.5 :001 > thinger = Foo.new
=> #<Foo id: nil, state: "initial", created_at: nil, updated_at: nil>
2.5.5 :002 > thinger.activate
(0.1ms) begin transaction
Foo Create (0.4ms) INSERT INTO "foos" ("state", "created_at", "updated_at") VALUES (?, ?, ?) [["state", "active"], ["created_at", "2019-06-28 21:35:22.555917"], ["updated_at", "2019-06-28 21:35:22.555917"]]
hello from sunny post_activate
(0.7ms) commit transaction
=> true
However, when I run my test:
describe 'Foo' do
it 'should fire post_activate' do
foo = Foo.new
expect(foo).to receive(:post_activate)
foo.activate
end
end
I get the following error:
1) Foo should fire post_activate
Failure/Error: foo.activate
ArgumentError:
Wrong number of arguments. Expected 0, got 1.
Interestingly, another test runs correctly with no errors:
it 'should change the state' do
foo = Foo.new
expect{
foo.activate
}.to change {
foo.state
}.from('initial').to('active')
end
This test passes and prints my debug message.
I tried putting a parameter into the post_activate definition:
def post_activate(arg)
puts arg
puts "hello from sunny post_activate"
end
Now the test passes - and the puts statement reveals that the object passed in is a StateMachines::Transition
#<StateMachines::Transition:0x00007fd5d3534228>
hello from sunny post_activate
I'm not sure how this works - I know how to make a parameter optional, but I don't know how to optionally pass an argument.
In any case - the test passes if I add a parameter to my method definition. But I don't want to add a parameter I'm not using just to get my specs to pass. How do I resolve this?
RSpec verifying proxy in validate_arguments! getting call for post_activate with current transition. You can see it if you add something like byebug to your Gemfile, and add same byebug conditionally into validate_arguments! of Rspec:
#method_reference.with_signature do |signature|
verifier = Support::StrictSignatureVerifier.new(signature, actual_args)
byebug unless verifier.valid?
raise ArgumentError, verifier.error_message unless verifier.valid?
That will give you access to actual_args:
(byebug) actual_args
[#<StateMachines::Transition ...]
So to fix your problem, you just need to add omitted parameter to your callback (as you clearly already figure out):
def post_activate(_)
And to be sure why underscore used that way, take a look at Ruby Style Guide.

How do I programmatically find out the schedule of a delayed mailer job with Resque Mailer and Resque scheduler?

I am trying to display the next time an email is scheduled using any or all of the below arguments as inputs. I'm using resque, resque-scheduler and resque-mailer.
For example, above are the delayed jobs as displayed in the resque web interface. So I'd like to input "game_starting_reminder" and/or 226 and/or "Beat Box" and be able to then display the timestamp as such:
"Next scheduled email: 2017-10-31 at 9:30 pm".
However, when I try to call for the information in the console, the below is the output I receive
I've tried extending the delay_extensions and methods and using the find_delayed_selection method but that doesn't seem to work.
For example this:
[18] pry(main)> Resque.find_delayed_selection { |job| job["class"] == QuizMailer}
TypeError: no implicit conversion of String into Integer
Or this:
[32] pry(main)> Resque.find_delayed_selection { {
[32] pry(main)* "class": "QuizMailer",
[32] pry(main)* "args": ["game_starting_reminder", [226, "Beat Box"]],
[32] pry(main)* "queue": "mailer"
[32] pry(main)* }}
=> ["{\"class\":\"QuizMailer\",\"args\":[\"game_starting_reminder\",[226,\"Beat Box\"]],\"queue\":\"mailer\"}",
"{\"class\":\"QuizMailer\",\"args\":[\"game_ending_reminder\",[226,\"Beat Box\"]],\"queue\":\"mailer\"}"]
Any other method I can use here? Or tips.
Thank you!
Figured it out. The scheduled_at method is the best candidate here for the job.
First step is to add the DelayingExtensions module to the project. I just added the file from the resque source code on Github to initializers and then in resque.rb added the line:
#resque.rb
rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..'
rails_env = ENV['RAILS_ENV'] || 'development'
resque_config = YAML.load_file(rails_root + '/config/resque.yml')
Resque.redis = resque_config[rails_env]
include DelayingExtensions
I modified the scheduled_at method from the github source code slightly because I couldn't get it to work as is and changed the name of the method to scheduled_for_time
#delaying_extensions.rb
def scheduled_for_time(klass, *args)
args = args[0]
search = encode(job_to_hash(klass, args))
redis.smembers("timestamps:#{search}").map do |key|
key.tr('delayed:', '').to_i
end
end
In this case, we can do the following in the console:
[2] pry(main)> klass =QuizMailer
=> QuizMailer
[4] pry(main)> args = ["game_starting_reminder", [230, "Beat Box"]]
=> ["game_starting_reminder", [230, "Beat Box"]]
[5] pry(main)> Resque.scheduled_for_time(QuizMailer, args)
=> [1515081600]
[6] pry(main)> Time.at(_.first)
=> 2018-01-04 21:30:00 +0530
Voila!

Getting undefined method in Controller yet method exists and works in Rails console

I am attempting to build a simple web app that takes data passed in to a text field and delivers it to an API. I have created a model with some methods to do some basic calls to the API. I have set up a form with a text field and a submit button to pass along the data to a controller. When I submit I am getting an error that the method I am calling is not defined. Yet if I go in the console and try the same thing it works as expected. Any suggestions on what I am missing?
Model:
def update(serial, deploy_value)
HTTParty.post("#{BASE_URL}devices/update/serial/#{serial}?#{API_KEY}",
:body => {"customFieldValues": [{"name": "Deploy","value": "#{deploy_value}"}] }.to_json,
:headers => { 'Content-Type' => 'application/json'}
)
end
Controller
class GroundControlController < ApplicationController
require 'GroundControl'
def update
serial = params[:serial]
GroundControl.new.update(serial, "TEST")
redirect_to(:back)
end
end
The error I am getting:
NoMethodError in GroundControlController#update
undefined method `update' for GroundControl:0x007fa062aa1298
From the console I can do the following:
2.3.0 :002 > require 'GroundControl'
=> true
2.3.0 :003 > test = GroundControl.new
=> #<GroundControl:0x007fa2676a8838>
2.3.0 :004 > GroundControl.new.update("ABC123", "TEST")
=> #<HTTParty::Response:0x7fa2671fb218 parsed_response={"id"=>43270, "serial"=>"ABC123", "udid"=>nil, "name"=>nil, "model"=>nil, "os"=>nil, "connected"=>false, "customFieldValues"=>[{"name"=>"Deploy", "value"=>"TEST"}]}, #response=#<Net::HTTPOK 200 OK readbody=true>, #headers={"server"=>["nginx/1.6.3"], "date"=>["Tue, 12 Apr 2016 15:09:27 GMT"], "content-type"=>["application/json; charset=utf-8"], "content-length"=>["150"], "connection"=>["close"], "access-control-allow-origin"=>["*"]}>
2.3.0 :005 >
More info on the error I am getting:
NoMethodError in GroundControlController#update
undefined method `update' for #
Extracted source (around line #10):
8 def update
9 serial = params[:serial]
10 GroundControl.new.update(serial, "TEST")
11 redirect_to(:back)
12
13 end

Rails Active Record ID vs. Object ID + Active::Relation

I've been receiving messages like this:
warning: Object#id will be deprecated; use Object#object_id
I read and attempted the tricks from Ruby Object#id warnings and Active Record without success:
108-125-94-123:toptickets johnnygoodman$ rails c
Loading development environment (Rails 3.0.3)
>> ticket_id = 8899
=> 8899
>> ticket = Ticket.where(:number => ticket_id)
=> [#<Ticket id: 97, name: "Set Up API to Feed Customer Info into Bronto ", number: "8899", category_id: 15, created_at: "2011-01-31 21:24:29", updated_at: "2011-01-31 21:24:29", position: 20>]
>> ticket.id
(irb):3: warning: Object#id will be deprecated; use Object#object_id
=> 2175680980
>> ticket[:id]
TypeError: Symbol as array index
from /Library/Ruby/Gems/1.8/gems/activerecord-3.0.3/lib/active_record/relation.rb:363:in `[]'
from /Library/Ruby/Gems/1.8/gems/activerecord-3.0.3/lib/active_record/relation.rb:363:in `send'
from /Library/Ruby/Gems/1.8/gems/activerecord-3.0.3/lib/active_record/relation.rb:363:in `method_missing'
from (irb):4
>> ticket.class
=> ActiveRecord::Relation
I'd expect that when I queried for ticket it would be of class ActiveRecord::Base. I'm not sure what to do to get that going or if its the direction I should head in.
Goal: Query for a ticket, print its id. In the example above, the id's value should be 97.
ticket = Ticket.where(:number => ticket_id) returns an ActiveRecord::Relation (which when evaluated in IRB, performs the database query and returns an array of tickets). So ticket.id is trying to perform .id on the entire array of tickets, not one actual ticket.
Maybe you only want the first result?
>> ticket = Ticket.where(:number => ticket_id).first
>> puts ticket.id
=> 97

Resources