I have an invite method that sends users emails to be part of a team. I have a modal that displays some users that already have an account and a textfield to input more emails and sends an invite for the person to join the platform.
But if I type an email that already exists in the database I get:
Validation failed: Email has already been taken
But want to send an email even if the person already has an account.
Here's what I have:
def invite
invite = params.dig(:invite) || {}
candidate_ids = invite.dig(:candidate_ids) || []
extra_emails = invite.dig(:extra_emails) || ""
emails = extra_emails
.split(/,|;/)
.map(&:strip)
new_users = emails.map { |email| team_email_invite(email) }
candidate_ids.concat new_users.map(&:id)
invite_data = candidate_ids.map { |uid| {
:user_id => uid,
:team_id => #team.id,
} }
TeamInvite.create(invite_data)
.map!(&:email)
respond_to do |format|
format.html { redirect_to overviews_url, notice: 'Invites sent.' }
format.json { head :no_content }
end
end
def team_email_invite(email)
user = User.new({
:email => email,
:password => SecureRandom.hex,
:role => :shadow,
})
user.add_role :recruiter
user.skip_confirmation_notification!
user.save!
end
candidate_ids - It's the users that I display on the list(all good)
extra_emails - emails in the textfield
So when I write a user that already has an account in the textfield, team_email_invite tries to create a new user and crashes.
I don't want to do something like
begin
new_users = emails.map { |email| team_email_invite(email) }
rescue
new_users=[]
end
because then it doesn't send the invite.
Any idea how to solve this?
You could use first_or_initialize. The block only gets run if the User does not already exist. Here's an example...
user = User.where(email: email).first_or_initialize do |usr|
usr.email = email
usr.password = SecureRandom.hex
usr.role = :shadow
usr.skip_confirmation_notification!
end
user.add_role :recruiter
user.save!
Related
Post has_many :comments
Comment belongs_to :post
I want to bulk update (approve) all comments of posts that are not cancelled. If there is a cancelled comment selected, display the notification else approve comments.
def make_comments_approved
comment_ids = params[:comment_ids]
if comment_ids
posts = Post.includes(:comments).
where(comments: { id: comment_ids.keys })
comments = posts.comments
cancelled_comments = posts.
where(comments: { status: :cancelled }).comments
comments_to_update = comments - cancelled_comments
if cancelled_comments.any?
flash[:error] = "Cannot approve cancelled comments: #{cancelled_comments.pluck(:number)}"
else
approve_comments(comments_to_update)
flash[:notice] =
"#{pluralize(comments.count, 'comment')} successfully approved"
end
else
flash[:error] = 'Select comments to update'
end
redirect_to :back
end
private
def approve_comments(comments)
# approve here
end
Use update_all for bulk update. For example:
comments.update_all(approved: true)
However, be cautious as it does not trigger validations or callbacks.
If you have comment_ids with you do following
cancelled_comment = Comment.where(id: comment_ids.keys, status: :cancelled)
if cancelled_comment.count > 0
flash[:error] = "Cannot approve cancelled comments: #{cancelled_comments.pluck(:number)}"
else
comments = Comment.where(id: comment_ids.keys).update_all()
flash[:notice] = "#{pluralize(comments.count, 'comment')} successfully approved"
end
If you are using the search-logic gem, you can do the following (Syntax is of Rails 2)
Comment.status_ne("cancelled").update_all(:approved => true)
In my application, I have a twitter and facebook login, however I need to prompt a password and email after they first register with twitter or facebook. I am using omniauth gems and my controller/user model looks like this:
//socials_controller.rb
def create
#render text: request.env['omniauth.auth'].to_yaml and return
#user = User.from_omniauth(request.env['omniauth.auth'])
if(#user.email == nil)
redirect_to patient_login_entry_url(#user)
elsif #user.confirmed
log_in #user
redirect_to #user
else
flash[:danger] = "Bir hata olustu."
redirect_to root_url
end
end
def login_entry
#patient = Patient.find(params[:id])
end
def update_social
#patient = Patient.find(params[:id])
if #patient.update_attributes(user_params)
SendVerificationEmailJob.perform_later #patient
flash[:success] = "Aktivasyon için #{#patient.email} adresinizi kontrol ediniz."
redirect_to root_url
else
flash[:danger] = "Bilgilerinizi kontrol edip tekrar deneyiniz."
redirect_to patient_login_entry_url(#patient)
end
end
and my from_omniauth method is:
//user.rb
has_secure_password
class << self
def from_omniauth(auth_hash)
if exists?(uid: auth_hash['uid'])
user = find_by(uid: auth_hash['uid'])
else
user = find_or_create_by(uid: auth_hash['uid'], provider: auth_hash['provider'], type: 'Patient')
user.location = get_social_location_for user.provider, auth_hash['info']['location']
if auth_hash.provider == 'facebook'
user.avatar = User.process_uri(auth_hash['info']['image'])
user.name = auth_hash['extra']['raw_info']['first_name']
user.surname = auth_hash['extra']['raw_info']['last_name']
user.email = auth_hash['extra']['raw_info']['email']
user.gender = auth_hash['extra']['raw_info']['gender']
elsif auth_hash.provider == 'twitter'
user.avatar = auth_hash['info']['image']
user.name = auth_hash['info']['name']
end
user.url = get_social_url_for user.provider, auth_hash['info']['urls']
user.save!
end
user
end
At the login_entry page, I simply prompt the email and password, and POSTing them to the update_social method.
However, as expected, my app throws the error "Password can't be blank", because has_secure_password validates its presence by default. So, I need to persist it between the requests since I can not save it without a password. How can I achieve this?
I tried to store the created object in session by using to_json method, and turning it into a hash between requests, however this time the profile picture I got from twitter/facebook did not persist (I'm using AWS S3 + Paperclip, the URL persists but there is no such image when I check it from the S3 console) so I think that solution was not good.
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions).first
end
end
Above is the method for authenticate and that code is from Model.
I unable to understand how to pass warden_conditions as parameter in this method for testing.
Can you please help me how to pass warden_condition as parameter for Rspec (Unit) Testiong?
Thanks
That seems to be a class method, and warden_conditions seems to be just a hash, so you can use something like this
let(:email) { "foo#bar.com" }
let(:warden_conditions) { { login: email } }
it "finds user by email" do
user = User.create(email: email)
authenticated = User.find_for_database_authentication(opts)
expect(authenticated).to eql user
end
it "finds user by username" do
user = User.create(username: email)
authenticated = User.find_for_database_authentication(opts)
expect(authenticated).to eql user
end
When a user registers with twitter I'm trying to add his name, location, etc. to his user record. I think I want to do something like user.build
Here is the controller. This is what happens:
user = User.new
user.apply_omniauth(omni)
if user.save
flash[:notice] = "Logged In!"
sign_in_and_redirect(:user, user)
else
session[:omniauth] = omni.except('extra')
redirect_to new_user_registration_path
end
When a user doesn't exist with twitter, the user is redirected to the registration path where they finish registering. I want to add the extra stuff from twitter to their yet to be saved user account. I can't do it in the user.apply_omniauth(omni) method because that saves to the authentications table.
Any ideas?
Thanks!
You can create a flag in apply_omniauth method to make a decision to save or not.
app/models/user.rb
# def apply_omniauth(omniauth) => def apply_omniauth(omniauth, save_it)
# apply_omniauth with save it flag
def apply_omniauth(omniauth, save_it = false)
case omniauth['provider']
when 'facebook'
self.apply_facebook(omniauth)
end
self.email = omniauth['user_info']['email']
if email.blank ? build_authentications(omniauth, save_it)
end
#build authentications
def build_authentications(omniauth, save_it = false)
auth_params = {: provider = > omniauth['provider'],
: uid = > omniauth['uid'],
: token = > (omniauth['credentials']['token'] rescue nil)
}
if save_it authentications.create!(auth_params)
else authentications.build(auth_params)
end
end
#force to save
def apply_omniauth!(omniauth)
apply_omniauth(omniauth, true)
end
We implemented a simple login method for our Ruby on Rails project, we have a button on the page to submit for the login information that the user will enter, but instead of pointing to our login method, the submit button runs our create method and tries to create another user.
def login
username = params[:username]
password = params[:password]
if(username == NIL || password == NIL)
loginfailed
else
comparisonUser = User.where("username = ?", username)
if(comparisonUser == NIL)
loginfailed
else
comparisonPassword = comparisonUser.password
if(comparisonPassword == password)
flash[:notice] = "Login Successful"
#user = comparisonUser
else
loginfailed
end
end
end
end
Here is the Create method:
def create
#user = User.create!(params[:user])
flash[:notice] = "User, #{#user.username} was successfully created."
redirect_to questions_path
end
and the code for the login button on the web page:
= form_tag users_path do
= label :userlogin, :username, 'Username'
= text_field :userlogin, :username
%br/
= label :userlogin, :password, 'Password'
= text_field :userlogin, :password
%br/
= submit_tag 'Login', :id => 'Login'
I probably need to add some more information to the submit_tag, but im not sure what to do.
Thanks for the help!
You're posting to the users_path, which is typically the create action (unless you've modified the route).
Instead of form_tag users_path you need to reference the appropriate route.