how to check if email exists from object errors array Rails - ruby-on-rails-3

I am creating users from emails.
If an email exists I want to skip creating that user.
Here is my model method:
def invite_users(emails, board)
invalid_emails =[]
emails.each do |email|
user = User.new(:email => email)
user.save ? Participant.make(user, board) : invalid_emails << email unless user.errors.each {|key, msg| msg == "has already been taken"}
end
invalid_emails if invalid_emails.any?
end
I want to check if the error generated from user.save is a duplicate email error. If so I don't want to put that email in the invalid_emails array.
How can I do this?

validates_uniqueness_of is useless for such purposes.
The canonical thing to do is to add a unique index on email to the users table, and use code like:
begin
-- do stuff that creates records --
rescue ActiveRecord::StatementInvalid => e
raise unless /Mysql::Error: Duplicate entry/.match(e)
end
if you want to emulate create or find, you do something like
result = nil
begin
result = User.create(:email => xxxx)
rescue ActiveRecord::StatementInvalid => e
if /Mysql::Error: Duplicate entry/.match(e)
user = User.find_by_email(xxx)
else
raise
end
end

just change
user = User.new(:email => email)
user.save ? Participant.make(user, board) : invalid_emails << email unless user.errors.each {|key, msg| msg == "has already been taken"}
to
user = User.new(:email => email)
if User.find_by_email( email ).blank?
Participant.make(user, board)
else
invalid_emails << email
end

Since errors is basically an array you could just change each to any to get a boolean value for your condition.

Related

How to return the correct fields in Rails API?

I have these two tables - User, Accounts.
User contains an authentication key,
Accounts contains the list of accounts.
I am trying to get the list of accounts for the user if the authentication key is correct.
So in the controller I have -
def show
#user = User.where(authentication_token: params[:authentication_token])
render json: #user.as_json(
only: [:email, :id, :authentication_token]
),
status: :created
end
This would just return the user details. How can i edit it so it first checks if the user exists with that Authentication_token, and then uses the UserID, in the accounts table to get the list of accounts ?
Your question is a little unclear: What is the desired behaviour if the authentication_token is not correct? Raise an exception? Redirect somewhere? Display a flash message? ...
You could, for example, do something like this:
def show
if authenticated_user
render json: authenticated_user.accounts.as_json(
only: [:id, :foo, :bar]
),
status: :ok
else
render json: { errors: { authentication_token: 'Invalid' } },
status: :unauthorized
end
end
private
def authenticated_user
#authenticated_user ||= User.find_by(
authentication_token: params[:authentication_token]
)
end

Rails 3 Actionmailer - How to send only if email exists

I have a Rails 3 app sending emails to multiple (2) recipients. The first recipient is required, but the second is not required. I get an error if the second recipient is null. How do I bypass the secondary email if null without getting an error?
def email_approvers(invoice)
#subject = 'Invoice Approval Request'
#body["invoice"] = invoice
#recipients = invoice.approver_email, invoice.alternate_approver_email
#from = "..."
#sent_on = Time.now
#headers = {}
end
You should see http://edgeguides.rubyonrails.org/action_mailer_basics.html#complete-list-of-action-mailer-methods (2.3.4 Sending Email To Multiple Recipients). You can make an array with the emails (maybe in the class Invoice you can create a method that returns an array of valid emails).
In the mailer:
default :from => "..."
def email_approvers(invoice)
#data = invoice
mail :to => #data.recipients, :subject => 'Invoice Approval Request'
end
In the Invoice model:
def recipients
return alternate_approver_email.nil? ? [approver_email] : [approver_email, alternate_approver_email]
end
--
For your case (it will set the property to nil if there is no value, so the previous code will work):
def alternate_approver_email
self.alternate_approver_email = !self.alternate_approver_username.nil? ? self.alternate_approver_username + "#email.com" : nil
end

Rails 3 validates_uniquess_of virtual field with scope

I am working on a Rails 3 app that is needing to run a validation on a virtual field to see if the record already exists... here is my model code:
#mass-assignment
attr_accessible :first_name, :last_name, :dob, :gender
#validations
validates_presence_of :first_name, :last_name, :gender, :dob
validates :fullname, :uniqueness => { :scope => [:dob] }
def fullname
"#{first_name} #{last_name}"
end
I am not getting any errors, its passing the validation.
I don't think you can do something like that with standard validates. Reason is, it usually is a "simple" sql look-up for uniqueness. But to check uniqueness of what's sent back from your method, it'd have to :
Get all entries of your scope
For each, execute the fullname method.
Add every result in an array while testing for uniqueness
Simple when the data set is small, but then it'd just take ages to do as soon as you reach 10K plus matches in your scope.
I'd personally do this with a standard before_save using the dob scope
before_save :check_full_name_uniqueness
def check_full_name_uniqueness
query_id = id || 0
return !Patient.where("first_name = ? and last_name = ? and dob = ? and id != ?", first_name, last_name, dob, query_id).exists?
end
Add error message (before the return):
errors.add(:fullname, 'Your error')
Back in the controller, get the error :
your_object.errors.each do |k, v| #k is the key of the hash, here fullname, and v the error message
#Do whatever you have to do
end

Where to set session default value?

guys!
Prior to asking i should mention, that i`m working without ActiveRecord or any self-hosted-database. So thats why i have to store some values in the session.
From the very begining i desided to set session value of the users city in the layout. - i supposed it would be loaded before anything else. So i`ve done something like this:
<% session[:city] ||= {:name => 'City-Name', :lat => '40', :lng => '40'}%>
But when i`m loading directly to inner page it occurs that session[:city is nil *(
How should i set the session properely, so that it wouldn`t be nil???
I had similar needs in one of the applications I worked on. It needed the users data to be loaded on sign-in and stored in the session. So, wrote a module called session_helpers.rb with the following:
module SessionHelpers
def get_value(key)
session[key.to_sym]
end
protected
def store_data(*objects)
objects.each do |object|
if object.is_a?(Hash)
object.each do |key, value|
session[key.to_sym] = value
end
end
end
end
def remove_data(*objects)
objects.each do |object|
if object.is_a?(String)
key = to_id(object)
else
key = to_id(object.class.name)
end
session[key] = nil
end
end
def update_data(key, value)
session[key.to_sym] = value
end
private
def to_id(name)
"#{name.parameterize('_').foreign_key}".to_sym
end
end
You can make any or all the methods available to views as well:
# application_controller.rb
helper_method :get_value
From the model I would retrieve a hash of the data that needs to be put up in the session about the user:
def common_data
#data = Hash.new
#data.merge!( { 'news' => self.news.count } )
...
#data
end
As I wanted to do this after sign-in I overrode the devise method to do this:
def after_sign_in_path_for(resource_or_scope)
store_data( '_count', current_user.common_data )
dashboard_path
end
This way I was able to load important data about the user on sign-in and store it in the session and retrieve whenever I wanted. Hope this helps.

Rails3 - Validate a unique pair of indexes

I created an index in my migrations:
add_index "enrollments", ["student_id", "lecture_id"], :name => "index_enrollments_on_student_id_and_lecture_id", :unique => true
How is it possible to validate this pair of keys in Rails? I tried:
validates_uniqueness_of :enrollment_id, :scope => [:student_id, :lecture_id]
But it doesn't work properly.
Also, I need to find out in a view, if this key already exists, or if it is possible to create a new entry.
class Enrollment < ActiveRecord::Base
validates_uniqueness_of :student_id, :scope => :lecture_id
end
If you want to determine in the view before submitting new enrollment that this pair exists then you can use ajax request (I prefer RJS with JQuery) and check it with:
class Enrollment < ActiveRecord::Base
def self.pair_exists?(student_id, lecture_id)
Enrollment.find_all_by_student_id_and_lecture_id(student_id, lecture_id).any?
end
end
Hope this will help you.
Sorry for opening an old thread but since it's 2011 and I still couldn't find a proper validator, I created one myself:
class UniqueSetValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
setup record
record.errors[attribute] << "- collection of fields [" + #fields + "] is not unique" if record.class.count(:conditions => #conditions) > 0
end
def check_validity!
raise ArgumentError, "Must contain an array of field names' symbols" unless options[:in] && options[:in].respond_to?(:each)
end
private
def setup record
conditions = []
fields = []
options[:in].each do |field|
conditions |= [ field.to_s + " = '" + record[field].to_s + "'" ]
fields |= [ field.to_s ]
end
#conditions = conditions.join(" AND ")
#fields = fields.join(", ")
end
end
It seems to work to me. To use it paste the code into:
your_rails_app/lib/unique_set_validator.rb
and enable it in:
your_rails_app/config/application.rb
by adding this line:
config.autoload_paths += %W( #{config.root}/lib )
Then you can simply use it in your model:
validates :field, :unique_set => [ :field, :field2 ]
It will validate the uniqueness of pair [ :field, :field2 ], and any error would be returned to :field. I haven't tried but it should work for more that 2 fields.
I hope that I didn't messed up anything, and that this will help someone. :)
Try this!
validates_uniqueness_of :enrollment_id, student_id, :scope => [:student_id, :lecture_id], :lecture_id], :message => "combination of enrollment, student and lecture should be unique."
If combination of student and lecture is not unique than you will get message in your error messages.
Update:
validates_uniqueness_of :student_id, :scope => [:lecture_id], :message => "combination of student and lecture should be unique."
The way voldy define in answer that's the correct way.