faced with an issue where #user.reset_token returns nil.
app/views/user_mailer/password_reset.html.erb
<%= link_to "Reset password", edit_password_reset_url(#user.reset_token, email: #user.email) %>
Reset_token is declared in User model, whereby this problem happens when I try to use a sidekiq worker. Refer to code below.
app/models/user.rb
class User < ActiveRecord::Base
attr_accessor :reset_token
def User.new_token
SecureRandom.urlsafe_base64
end
def send_password_reset_email
PasswordResetWorker.perform_async(self.id)
end
private
def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_token))
update_attribute(:reset_sent_at, Time.zone.now)
end
app/workers/password_reset_worker.rb
class PasswordResetWorker
include Sidekiq::Worker
sidekiq_options retry: false
def perform(user_id)
user = User.find(user_id)
UserMailer.password_reset(user).deliver
end
end
app/mailers/user_mailer.rb
class UserMailer < ActionMailer::Base
default from: "noreply#example.com"
def password_reset(user)
#user = user
mail to: user.email, subject: "Password Reset"
end
end
This problem DOES NOT happen when I do not use workers
app/models/user.rb
def send_password_reset_email
UserMailer.password_reset(self).deliver
end
Would like to know what can I replace "#user.reset_token" with? Let me know if you need more info. Thanks in advance.
You're not storing the reset_token in the database - you're storing the reset_digest.
When you don't use workers, you're storing the reset_token in the User instance, then passing that same User instance to your mailer - hence the reset_token is still available.
When you use workers, your worker only has the User's ID, so it's reloading the User instance from the database. Because the reset_token isn't being stored in the database, it's coming back nil.
Either you should be saving the reset_token in the database, or your password email should be using reset_digest in the URL.
Related
I have around 30 mailer methods where I'm passing the user as an argument. Since I need access to the #user variable in the view, I'm having to set this instance variable in every mailer method, for example, send_x_email(user).
Normally this would be done in an initialize method, but I've read that mailers act a bit differently. Additionally, some of the methods take a different number of arguments (one just takes user, the other takes user and message).
I've investigated before_action callbacks and looked at this post
Setting instance variables in Action Mailer?
...but I'm still stuck.
I would appreciate any thoughts on how to simplify things and remove #user = user from the 30 or so methods in the mailer class. Cheers!
class ReminderSender < ActionMailer::Base
def send_commands_email(user)
#user = user
mail(to: #user.email,
subject: "All Commands",
from: "<commands##{ENV['DOMAIN']}>")
end
def send_attachment_warning(user, message)
#user = user
#message = message
mail(to: #user.email,
subject: "Attachment Warning",
from: "<attachments##{ENV['DOMAIN']}>")
end
end
Try defining a 'mail' method in your class and declaring an instance variable there e.g.
class YouMailer
def send_email(user, message)
subject = 'something'
body = message
mail(user, {subject: subject, body: body}})
end
def mail(user, options={})
#user = user
mail_options = {to: #user.email}.merge(options)
super(mail_options)
end
end
But you might need to specify the 'template_path' and 'template_name' options with that strategy.
My suggestion would be to keep things as they are. Having "#user = user" in all of your mailer methods out of necessity isn't bad.
I am working on a application where Users can list their in-game items to trade with other Users. A user's profile url would be something like this:
/users/1/index
And their user listings profile would be something like
/users/1/listings/1
All other resources nested under users would be the same as the latter.
I am trying to implement a method that is called by a before_filter callback that checks to see if a user has blocked or is blocked by the user who owns the profile and respective nested resources such as ability to message them, view their listings etc. If either has blocked each other, then they redirected to the root page of the application. This is the method that I use for the before_filter:
def blocked_relationships
if blocked?
redirect_to :root
end
end
I used another method that checks the state of the relationships between the two users.
This is the method I found and worked on after some research courtesy of the Rails Recipes book:
def blocked?
Relationship.exists?(user_id: current_user.id, other_user_id: params[:user_id], status: "blocked") ||
Relationship.exists?(user_id: params[:user_id], other_user_id: current_user.id, status: "blocked")
end
The problem I have is that this method only works, for example, when User 1 is looking at User 2's items, messages, listings etc. because the url:
/users/2/listings [or items or etc]
will contain a params that makes reference to the user as params[:user_id]. params[:id] in this case and context will refer to the listings id.
BUT, if I am User 1 and I have blocked User 2 and visit User 2's profile, this method will not work because the url /users/2/index will use params[:id] to instead of params[:user_id].
I've been thinking about how to implement this in a DRY way but I can't seem to solve my problem other than doing something like this:
def blocked?
if params[:user_id].blank?
Relationship.exists?(user_id: current_user.id, other_user_id: params[:id], status: "blocked") ||
Relationship.exists?(user_id: params[:id], other_user_id: current_user.id, status: "blocked")
else
Relationship.exists?(user_id: current_user.id, other_user_id: params[:user_id], status: "blocked") ||
Relationship.exists?(user_id: params[:user_id], other_user_id: current_user.id, status: "blocked")
end
end
I also considered the possibility that I'm not even implementing my blocking feature correctly, but before I address that issue, I was wondering if anyone had any ideas on how to solve this problem. Any help or feedback would be greatly appreciated and I would be happy to add anymore information for clarification. Thanks!
Why not other_id = params[:user_id] || params[:id]? This is a way to override :id when :user_id is present.
About your blocking feature though, to me I'd like to see a user even if I've blocked them. I'd create a blocked_by_user_id field on the Relationship to see who did the blocking and only disallow the blocked party from seeing the user's profile.
You'd probably want to checkout authorization gems for rails like cancan or related (it's not my favorite but the most popular). However, you could handle it like this:
class User
has_many :relationships,
scope :accessible_by,
->(user) { where.not id: user.relationships.where(status: :blocked).pluck(:other_user_id) }
end
Then use the relationship User.accessible_by(current_user) on your controller instead of plainly User to retrieve resources. For example:
class UsersController < ApplicationController
def index
#users = User.accessible_by(current_user)
# bleh
end
def show
#user = User.accessible_by(current_user).find(params[:id])
# etc
end
end
When the resource is nested under a user you could do this:
class Users::PicturesController < UsersController
def index
#pictures = User.accessible_by(current_user)
.find(params[:user_id]).pictures
end
def show
#picture = User.accessible_by(current_user)
.find(params[:user_id]).pictures.find(params[:id])
end
end
When a user tries to access a resource that can't view, ActiveRecord::RecordNotFound will be raised, so you should handle it:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNorFound, with: :rescue_not_found
private
def rescue_not_found
redirect_to root_path,
notice: 'You can\'t access that with your current priveleges. '
end
end
I'm unable to assign a model instance to a reference property of another model. Relevant code is below:
module Blog::Models
class Post < Base; belongs_to :user, dependent: :destroy end
class User < Base; has_many :posts end
...
class BasicFields < V 1.0
def self.up
create_table User.table_name do |t|
...
end
create_table Post.table_name do |t|
...
t.references :user
end
end
...
end
end
module Blog::Controllers
...
class PostEditN
...
def post(post_num)
#post = Post.find(post_num)
#user = User.find(#input.user)
...
#post.user = #user # Error thrown: NameError at /post/edit/1 uninitialized constant User
# #post.user_id = #user.id << This is my currently working solution
#post.save
redirect PostN, post_num
end
end
...
end
...
When I assign something to #post.user using Camping in console mode, it is successful, but I can't seem to accomplish the same behavior in the controller otherwise. I made do by simply assigning the #user.id to the user_id property of the Post instance. However, I would like to figure out why the alternate method works in the Camping console and not when I'm simply running the webserver.
My best guess is that this is a problem with namespaces. In the code you show Useris actually Blog::Models::User. In your controller the context is Blog::Controllers. Have you tried changing the code in the controller to?
#post = Blog::Models::Post.find(post_num)
#user = Blog::Models::User.find(#input.user)
...
I was able to resolve my issue. Seems when I was creating new Post records, I was not initializing the User. Thus, when assigning #post.user it would complain that the user property was uninitialized. The only problem I see is that an operation was attempted to be made on an oprhan Post record, which is invalid data according to the relationship with User.
I have no idea how this works in rails but I set up routes like this:
resources :users do
resources :api_keys
end
(User has_many: api_keys, api_key belongs_to: user)
So I then (since I only care about API Keys), created the following controller:
class ApiKeysController < ApplicationController
before_action :authenticate_user!
def index
#user = User.find(params[:user_id])
#api_key = User.apikeys
end
def create
#user = User.find(params[:user_id])
#api_key = ApiKey.new(create_new_api_key)
create_api_key(#api_key, #user)
end
def destroy
destroy_api_key
end
private
def create_new_api_key
params.require(:api_key).permit(user_attributes: [:id], :api_key)
end
end
Which states, authenticate user before every action, index fetches all api keys based on a user id. create is suppose to create an api key based on a user id, (note: create_api_key(#api_key, #user) just an abstracted method that states - if we saved, redirect to user_path with a message, if we failed, back to user path with a error message)
And destroy, well that just finds an api key, destroys it and redirects (again with the abstraction).
Whats the issue?
the create_new_api_key method. Its freaking out and saying:
syntax error, unexpected ')', expecting => (SyntaxError)
I thought this is how I pass in the user id ??
You need to change the order of the arguments passed in to permit to fix the syntax error:
def create_new_api_key
params.require(:api_key).permit(:api_key, user_attributes: [:id])
end
I'm kind of new to Rails 3.1. and I'm facing an issue only in my production env with my Signup form (actually, it's more about the controller).
Here is the code in User
class UsersController < ApplicationController
[...]
def create
#user = User.new(params[:user])
logger.info "value of login in param : #{params[:user][:login]}" #-> log the actual login
logger.info "value of login : #{#user.login}" #-> log empty
#user.admin = false
if #user.save
flash[:notice] = t('flash.notice.user.create.valid')
redirect_back_or_default root_path
else
flash[:notice] = t('flash.notice.user.create.invalid')
render :action => :new
end
end
end
Also, the controller logs show that the params hash is good
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"QwOqmp0CT/d4mmC1yiLT4uZjP9bNDhbUXHanCQy5ZrA=",
"user"=>{"login"=>"myLogin",
"email"=>"t.r#gmail.com",
"password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]"}}
My login form works as expected (already created users are able to sign in)
Again, this only happens in production.
EDIT: Here is my User Model
class User < ActiveRecord::Base
acts_as_authentic
#== Callbacks
before_create :set_defaults
attr_accessible :avatar ##### EDIT: TO FIX THE ISSUE, ADD THE OTHER FIELDS AS WELL
protected
def set_defaults
self.total_1 = self.total_2 = self.total_3 = 0
end
end
Just to memorialize the answer from the comments above:
Normally you can use mass assignment to set fields on a model, but when you use attr_accessible, you are then limited to only mass assigning those fields. So stuff like User.new(params[:user]) won't work; instead, you'd have to do:
#user = User.new
#user.login = params[:user][:login]
# ...etc.
#user.save
Simple add your fields to the attr_accessible list and you can go back to mass assignment.