I am creating a job for the first time in Rails to send an email to the current_user when they create a form.
Create method
def create
#pdform = Pdform.new(pdform_params)
if user_signed_in?
#pdform.user_id = current_user.id
end
respond_to do |format|
if #pdform.save
SubmissionJob.perform_later(current_user, #pdform)
format.html { redirect_to new_pdform_requisition_path(#pdform) }
else
format.html { render :new }
end
end
end
I am passing the current_user and the #pdform in the job arguments.
Submission job
class SubmissionJob < ApplicationJob
queue_as :default
def perform(*args)
#user = current_user
NewPdform.notify_user(#user, #pdform).deliver
end
end
If I am passing the current_user object in the parameters in the arguments andassigning it to the #user variable how is there no defined method for current_user?
I have this check to set the user_id of the pdform to the current_user id and that is being populated when I create the form which means there is a user. So why would this result in a NameError?
The current_user object is only available by default in controllers and views.
You need to pass it to your job as an argument:
Replace:
def perform(*args)
By:
def perform(current_user, pdform)
or even :
def perform(user, pdform)
NewPdform.notify_user(user, pdform).deliver
end
Related
I want to add service objects in to my controller. Is there any chance to include flash messages in to this service object?
user_stocks_controller
class UserStocksController < ApplicationController
def create
#user_stock = UserStocksCreator.new(current_user, params).call
redirect_to my_portfolio_path
end
end
service objects user_stocks_creator
class UserStocksCreator
def initialize(current_user, params)
#current_user = current_user
#params = params[:stock_ticker]
end
def call
stock = Stock.find_by_ticker(params)
if stock.blank?
stock = Stock.new_from_lookup(params)
stock.save
end
#user_stock = UserStock.create(user: current_user, stock: stock)
flash[:success] = "Stock #{#user_stock.stock.name} was successfully added to portfolio"
end
private
attr_accessor :current_user, :params
end
With this code I have an error:
undefined local variable or method `flash'
The flash method is only available in the controller. When you want to set the flash in the service object then you need to pass the flash to the service object.
# in the controller
def create
#user_stock = UserStocksCreator.new(current_user, params, flash).call
redirect_to my_portfolio_path
end
# in the service
class UserStocksCreator
def initialize(current_user, params, flash)
#current_user = current_user
#params = params[:stock_ticker]
#flash = flash
end
def call
# unchanged...
end
private
attr_accessor :current_user, :params, :flash
end
I have a function in my model to import data from a CSV file and I'd like to have validations should there be any errors. For example, when I upload the file, I search for a User based on an ID in the file. If there is no User with that ID, I'd like to redirect_to a different page with an error.
def self.getUser(scale_id)
#user = User.find_by(scale_id: scale_id)
if #user == nil
redirect_to users_path
else
return #user
end
end
def self.bulk_upload_weigh_ins(file)
output = []
errors = []
CSV.foreach(file.path, headers: true, ) do |row|
row = row.to_hash
#scale_id = row["scale_id"]
#user = getUser(#scale_id)
row.merge!(user_id: #user_id)
WeighIn.create! row.to_hash
end
end
...and no matter what path I put there, I get the following: undefined local variable or method 'users_path' for #<Class:0x007fa06f466998> even when it is a valid path.
Is there something wrong with redirecting like this? If yes, how should I do it?
The cleanest way for custom validations is to do something like:
In your model:
User < ActiveRecord::Base
validate :get_user
def initialize(params={})
self.id = params[:id]
end
def get_user
#user = User.find_by(self.id)
if #user.nil?
errors.add(:base, "Invalid User")
return false
else
return #user
end
end
In your controller you'd then do:
def whatever_action_youre_using
#user = User.new(user_params)
unless #user.valid?
redirect_to users_path
end
end
def user_params
params.require(:user).permit(:first_name, :email, :etc, :whatever_your_atts)
end
I have a function in my model that is searching for a User based on an
ID
undefined local variable or method 'users_path' for
Class:0x007fa06f466998
As #Dave Newton mentioned in the comments, the path helpers are not available in models unless you specifically include them. Also you can't and never need to use redirect_to in a model. The application logic should be moved to the controller. Something like below should work
def get_user
#user = User.find_by(scale_id: params[:user][:scale_id])
if #user.nil?
redirect_to users_path, notice: 'Your error message here'
else
return #user
end
end
I tend to mix local variables and instance variables in Rails controllers when I don't need to use them in a view. Obviously, if I'm using in the view, I use instance variables. Is there any difference between using them in that scenario (other than class-level visibiity)?
For example:
def destroy
#micropost.find(params[:id])
#micropost.destroy
redirect_to root_url
end
or
def destroy
micropost.find(params[:id])
micropost.destroy
redirect_to root_url
end
an example of using instance variables for class level visibility would be here: https://github.com/mhartl/sample_app/blob/master/app/controllers/microposts_controller.rb ?
I think these lines of code is what your question about. Of course you don't have to instantiate that variable with # since you are not actually going to show it on your view(since it is being destroyed). The purpose of these lines of code if to first check wether #micropost exists, if it does not then redirect_to root_path else it will go to destroy method destroy the micropost and then redirect_to root_path.
Now, to answer your question, yes, there is a huge difference between #micropost and micropost. #micropost will be accessible in other methods of your controller while micropost will not(since its scope will be limited to the method you instantiate it in).
However, if you're concern about not having a # variable then you can change the code shown here to this:
class MicropostsController < ApplicationController
before_filter :signed_in_user
def create
#micropost = current_user.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to root_path
else
#feed_items = []
render 'static_pages/home'
end
end
def destroy
if micropost = current_user.microposts.find_by_id(params[:id])
micropost.destroy
end
redirect_to root_path
end
end
Beside, scope difference, you can access instance variable in another method of same controller, as
def method1
#var = "some value"
puts #var
end
def method2
puts #var
end
now, depending on the sequence u call these method, #var can have different values
So I am building an application that I am trying to never need a database as the application will just be a portal to an API. I have a sessions controller and I am trying to use a cookie based session but the setter method is never being hit. Here is what I have at this point.
sessions_controller.rb
class SessionsController < ApplicationController
def new
if current_user
redirect_to snapshots_path
end
end
def create
api = API.new
response = api.authenticate_user(params[:session][:username].downcase, params[:session][:password])
if response["Message"] == "success"
current_user = response["User"]
binding.pry
redirect_to snapshots_path, notice: "Signed in successfully."
else
flash.now[:error] = "Invalid username/password combination."
render :new
end
end
def destroy
current_user = nil
redirect_to sign_in_path
end
end
sessions_helper.rb
module SessionsHelper
def current_user=(user)
binding.pry
if user
#current_user = user
cookies[:userdata] = { :value => user, :expires => 8.hours.from_now.utc }
else
#current_user = nil
cookies.delete(:userdata)
end
end
def current_user
binding.pry
#current_user ||= (cookies[:userdata] ? cookies[:userdata] : nil)
end
end
The getter method is hit correctly every time but the setter is never getting hit. Any ideas as how to fix this thanks.
When you are assigning to current_user it's treating it as a local variable. To solve that simply assign to self.current_user instead. The getter doesn't need that because there is no local variable named that so ruby looks for a method and uses that. If you reference the getter as self.current_user that would also work.
For example change:
current_user = response["User"]
to:
self.current_user = response["User"]
Include SessionsHelper in your SessionsController in order to access SessionHelper methods within SessionsController.
Code will work fine without any modification i.e., you would be able to access current_user and current_user= directly.
class SessionsController < ApplicationController
include SessionsHelper ## Just add this
..
end
I'm trying to set a variable in my before_filter, but always get the error "undefined local variable or method 'question' for AnswersController":
class AnswersController < ApplicationController
before_filter :get_question
def create
#answer = question.answers.new(params[:answer])
#answer.user = current_user
#answer.save
flash[:notice] = 'Answer posted successfully.'
redirect_to request.referer
end
def get_question
question = Question.find(params[:question_id])
end
end
Thank you very much!
You need to make it an instance variable using the # symbol. Also, you may want to consider moving this into a private method (see below) since this most likely is not a public action.
class AnswersController < ApplicationController
before_filter :get_question
def create
#answer = #question.answers.new(params[:answer])
#answer.user = current_user
#answer.save
flash[:notice] = 'Answer posted successfully.'
redirect_to request.referer
end
private
def get_question
#question = Question.find(params[:question_id])
end
end