Rails - passed parameter not recognized in custom service [NoMethodError] - ruby-on-rails

I've written a custom service for my project:
#app\services\quiz_data_creation.rb:
class QuizDataCreation
def initialize(user)
#quiz_session = user.quiz_session #<-- here is the problem
end
def create
create_answer_data
end
private
def create_answer_data
#quiz_session.quiz.questions[#quiz_session.current_question_index].answers
end
end
I call the create method in my ActionCable channel like this:
class QuizDataChannel < ApplicationCable::Channel
def send_data
logger.debug "[AC] before quiz data creation service - current user: #{current_user.inspect}"
#answers = QuizDataCreation.new(user: current_user).create
#then send answers
end
end
The problem I'm having is that the service does not recognize the user object I pass when calling the create method, even though the logger shows me that the correct object is being found! It gives me the Error Message:
NoMethodError - undefined method quiz_session for #<Hash:0xc86cfa0>
I'm using a Devise generated User. Also, it might be relevant that I'm using 2 models that inherit from the User model (Teacher and Student), but all the attributes are stored in the users table.
A user record looks like this:
[AC] before quiz data creation service - current user: #<Teacher id: 1, email: "bob#teacher.edu", created_at: "2017-05-11 07:24:48", updated_at: "2017-07-26 08:40:27", name: "Bob", quiz_session_id: 7>
I would be very happy if anyone could point out to me how to solve this issue - thank you! :)

You declared a positional parameter
def initialize(user)
but you pass a keyword argument:
QuizDataCreation.new(user: current_user)
Change it to
QuizDataCreation.new(current_user)
or change method signature to accept keyword args.

Related

Create resource from model when User signs up

I'm trying to create a resource Mission when a new user is signed up. Mission has a foreign key Location as well.
class User < ApplicationRecord
after_create :create_mission
private
def create_mission
Mission.create(user: self, location_id: 1, name: 'Mission 1')
end
end
But this code doesn't work unfortunately. How can I solve it?
How about this:
class User < ApplicationRecord
after_create :create_mission
private
def create_mission
missions.create! location_id: 1, name: 'Mission 1'
end
end
Now your error could be visible with create!. Try this.
I do not recommend you to create relations in callbacks. It will hurt you if you will need to create user in another action or in console. Better to create it in controller after creation (in some service object in the future)
def create
if #user.save
#user.missions.create(...)
end
end
And you can use debugger to check errors dynamically (shipped with rails https://github.com/deivid-rodriguez/byebug, just insert 'debugger' in your code), probably you have some validation error.

Calling a Rails application helper in Sidekiq worker

I have Googled this and can't seem to find an
class MyWorker
include Sidekiq::Worker
include ApplicationHelper
worker code.... etc....
myapphelper(arg)
end
I have a simple worker which at the end calls an application helper but I get:
NoMethodError: undefined method `myapphelper'
I thought adding the include ApplicationHelper would do the trick.
UPDATE
So lets add some more detail. The helper (which in fact was actually a method in my application controller) in question was originally this:
def add_history(resource, action, note)
resource.history.create(action: action, note: note, user_id: current_user.id) if resource.present? && action.present? && note.present?
end
The idea here is I have a quick way to add a paper trail to a Model. I realized that I should perhaps not pass an actual object into the method because (like the Sidekiq docs indicate) if that object changes you could get into trouble. So I changed it to this:
def add_history(klass, id , action, note)
resource = klass.constantize.find_by(id: id)
resource.history.create(action: action, note: note, user_id: current_user.id) if resource.present? && action.present? && note.present?
end
Now when I include this as a Module the current_user.id fails because that's set in the ApplicationController.
So lets revise my question: would the best practice to just add current_user.id as a argument to my module method or somehow keep this in the Application Controller etc.?
If I am totally off track here and this type of logic should go somewhere else please let me know.
You could accomplish the behavior by doing something like:
class HistoryWorker
include Sidekiq::Worker
include History # or whatever you want to call it
def perform(klass, id , action, note, user_id)
add_history(klass, id, action, note, user_id)
end
end
module History
def add_history(klass, id, action, note, user_id)
resource = klass.constantize.find_by(id: id)
resource.history.create(action: action, note: note, user_id: user_id) if resource.present? && action.present? && note.present?
end
end
class ApplicationController < ActionController::Base
after_filter :save_history
def save_history
HistoryWorker.perform_async(class: resource.class.name, id: resource.id, action: params[:action], note: 'some note', user_id: current_user.id)
end
end
Apologize for any dumb syntax errors, but that's more or less the structure you want.
That being said, using a Module is probably overkill in this case, especially if you do not intend on re-using its method elsewhere. In that case, I'd just add a private method inside the worker.

Sidekiq: ArgumentError: When assigning attributes, you must pass a hash as an argument

I guess this question is common with Rails 4, but my situation is different.
I am using Sidekiq to delay the creation of jobs; think this is possible as with simple data, it works. By means of simple data:
def perform
Foo.create(bar: "staff")
end
Here's my data with issues:
supports_controller.rb:
def create
params = support_params // seems to be issues here?
DelayedJobs.perform_in(1.minutes, current_user.id, params)
...
end
private
def support_params
params.require(:support).permit(:foo1, :foo2, :foo3)
end
app/workers/delayed_jobs.rb:
class DelayedJobs
include Sidekiq::Worker
def perform(user_id, params)
u = User.find(user_id)
support = u.supports.build(params)
Support.create(support) // create and save to db
end
end
Via web (localhost:3000/sidekiq/scheduled, I see the details. Great. But after a minute it goes to retries with the error. Any help on this one?
EDIT:
In the sidekiq web argument:
40, {"foo1"=>"a", "foo2"=>"b", "foo3"=>"c"}
Why is that the user_id (40) is outside?
The problem isn't with Sidekiq; it's an ActiveRecord problem with this line:
Support.create(support)
create only takes a hash, but you're giving it a Support.
This should work:
class DelayedJobs
include Sidekiq::Worker
def perform(user_id, params)
u = User.find(user_id)
u.supports.create!(params) # `create!` will raise an error if the save fails; allowing you to catch invalid params
end
end
Protip: you can eliminate Sidekiq as a suspect by running the body of your perform method in a Rails console. You'll see that you get the same error even when Sidekiq isn't involved.
I suggest that you call save method on support object because when you are using build method it returns a new instance of support so you need only to save it.
class DelayedJobs
include Sidekiq::Worker
def perform(user_id, params)
u = User.find(user_id)
support = u.supports.build(params)
support.save // save to db
end
end
In your controller try to change params to:
def create
params = params[:support]

Print variables coming to model's methods

Having following code:
class MyModel < ActiveRecord::Base
before_create :do_process
def do_process
#I want to print here everything that came to this method
self.my_stuff = my_stuff
end
Say we have model with name and description attributes and
I'm going into console and enter something like
MyModel.create! name: 'test', description: 'test'
So how can I view what arguments passed to method do_process?
You can do it this way:
def do_process(*args)
puts args
# body of method
end
It seems that what you really want is to see the attributes of the object as it enters the do_process method. For that, you can use the attributes method:
def do_process
$stderr.puts attributes
self.my_stuff = my_stuff
end

Access a instance method from other model in ruby on rails?

I have a user model in my application. Now I want to replace some user model coding into 2 categories likely employ.rb and customer.rb under a module users, to avoid more number of codes in a single model. I want to access a method send_mail in customer.rb after a user created.
user.rb
after_create:send_msg_on_order
def send_msg_on_order
Users::Customer.send_mail
end
users/customer.rb
def send_mail
Mailer.send_mail_to_customer.deliver
end
And I am getting undefined method `send_mail' for Users::Customer:Module error.
You have defined send_mail method as instance method but calling it as a class method. Either make it a class method or create an instance of Customer model and call it.
Making the method a class method:
def self.send_mail
Mailer.send_mail_to_customer.deliver
end
If you wish to keep it an instance method, then call it like this:
after_create:send_msg_on_order
def send_msg_on_order
Users::Customer.new.send_mail
end
HTH
You can also call like this
def send_msg_on_order
Customer.send_mail
end

Resources