How checking value from DB table? - ruby-on-rails

I want create a simple checking value from database. Here is my code:
def check_user_name(name, email)
db_name = Customers.find_by_name(name).to_s
db_email = Customers.find_by_email(email).to_s
if name == db_name && email == db_email
return 'yes'
else
return 'no'
end
end
But I have allways 'no' variant....why ?

Because you are calling to_s on your Customers model and not actually getting the name. The two fetch lines you have should be:
Customers.find_by_name(name).name.to_s # to_s probably not necessary if you know this field is a string
Customers.find_by_email(email).email
But, you're making two separate requests to the database. I don't know what the purpose of this is (as you could be selecting two different Customers) but you could do:
if Customers.where(name: name, email: email).exists?
"yes"
else
"no"
end
Since you are, however, selecting by name and email - I would highly recommend that you make sure those fields are indexed because large tables with those requests will bog the server and make that route rather slow (I would actually recommend that you pursue other routes that are more viable, but I wanted to mention this).

When you give Customers.find_by_name(name), you will not get name of a customer. Actually it will return activerecord object, so from this object you need to get the name and email of a customer, like below,
name = Customers.find_by_name(name).name
email = Customers.find_by_email(email).email
Now you will get the exact name and email of matched data from DB.

Related

Rails 4 how to know if update has been executed

I have this code in Rails 4:
mmkBase = Mmk::Base.find_or_initialize_by(id: id)
mmkBase.update(name: name, city: city, country: country, address: address, region_ids: regionIds, latitude: latitude,
longitude: longitude)
The code is working ok, but now I would need to gather some statistics, like:
Number of bases.
Number of new bases.
Number of loaded (updated) bases.
Number of bases is not a problem. Number of new bases, I think I could check if the object mmkBase returned by find_or_initialize comes with more attributes than just the id, for example with a name.
However, I don´t know how can I check if the update operation is run.
Is there any way to know this? Is there a better way to achieve this? Maybe, I should use a more traditional way to do it, with a select first?
However, I don´t know how can I check if the update operation is run
You can simply use...
if mmkbase.update mkbase_params
...
else
...
This will invoke the update method, generating a response determined by whether it was successful or not (true = success; false = validation failure)
Number of new bases
As mentioned, the new_record? method is what you need here...
if mmkbase.new_record? #-> mmkbase hasn't been saved to db yet
I'd do this:
mmkBase.find_or_create_by(mmkbase_params) do |base|
#do something if new base
end
private
def mmkbase_params
params.require(:mmkbase).permit(:name, :city, :country, :address, :region_ids, :latitude, :longitude)
end
If you're having to pull this type of data by its primary key (find_or_initialize_by id:), you're in trouble... how do you know if the primary key will remain constant?
Of course, this is not always the case. Changing user details would require you to reference the user id - however that data is rigid. IE a user registered (with name and email), and you're now looking for that user (it can't be created on the fly).
I don't know your schema; anything which can be created like this should be referenced by its core data (name, city, etc)...
mmkbase.find_by(name: ....)
In relational databases, you need to be dealing with the data, not the primary keys. In other types of db, you can deal with indexes, but with this type of setup, it's much better to work with the pure data.
You want to distinguish between an already existing record and a newly created record. On a new record which was only initialized by find_or_initialize_by, new_record? returns true. So for example you could write:
mmkBase = Mmk::Base.find_or_initialize_by(id: id)
new_base = mmkBase.new_record?
if mmkBase.update(name: name, city: city, country: country, address: address, region_ids: regionIds, latitude: latitude,
longitude: longitude)
# update was successful
if new_base
new_bases += 1
else
updated_bases += 1
end
else
# error handling
end
Note that it is probably not a good idea to use find_or_initialize_by with the ID of the record. See Rich Peck's answer.
You could probably check the updated_at timestamp on the object to see if it has been updated recently but the Rails 4 update method doesn't tell you whether an object was updated or not.
http://apidock.com/rails/ActiveRecord/Base/update/class

Table performance when create a unique token by user

In my User model i have the following method :
def confirmation_token
self.confirmation = loop do
random_token = SecureRandom.urlsafe_base64(16, false)
break random_token unless User.exists?(confirmation: random_token)
end
end
this method will just create a random token to confirm user's email...
as you can see it loop while User.exists?(confirmation: random_token), which means it verify if there is no similar token already in user table.
my question is : if i have for example a lot of rows in "user table", i need to add index in this (confirmation) column for more performance ?
note (this method is executed just once per user ... the first time when user is sign up)
Yes. If you're doing many searches on any particular column (in this case confirmation), you should index that column.
If You are just searching by that value, then short answer was given to You.
Though there are some things to consider. First of all You probably would want that index to be unique, this will improve the performance quite a lot as table grows.
add_index :users, :confirmation_token, unique: true
Also You probably want a unique value instead of just random value. Though it's unlikely that it would generate a duplicate, it's still random, not unique value. One of the options would be generating and SHA using Digest class from some user column that You know is unique for this table, like this:
Digest::SHA1.hexdigest(user.email)
UPD: Asker is concerned about cases if someone would know that he uses email as a key and would use it to generate token.
This usually is solved by appending some unique key to the email before encrypting. You can generate such key with secure random and store it inside environment variable.
In your .bashrc/.profile/.bash_profile or any other You use, do this:
export EMAIL_TOKEN_SECRET="M9SyIuuOPhakX0b6gjvcRnsRHY="
Then do like this:
Digest::SHA1.hexdigest("#{user.email}-#{ENV['EMAIL_TOKEN_SECRET'}")

Rails cross model validation

I have two tables one for members and the other for employees, both have an attribute called id_number this attribute is not required and can be null.
Is it possible to run a validation to ensure the uniqueness of the id_number, so that if an employee is added with the same id_number as an member or vice versa that it will give an error.
I am thinking of writing my own validation but hitting the db for each instance will be very slow as some companies upload 10's of thousands of employees at a time.
Yes that's possible with your own validation. I think you have to hit the database, otherwise you never could check if it exists already.
def your_validation
employee_ids = Employee.all.map(&:id_number)
member_ids = Member.all.map(&:id_number)
id = self.id_number
if employee_ids.include?(id) || member_ids.include?(id)
errors.add(:id_number, "is already taken")
end
end
I think adding an index to your id_number will be good.
UPDATE: The above method could be changed to following to improve the performance:
def your_validation
employee_ids = Employee.all.map(&:id_number)
if employee_ids.include?(self.id_number)
errors.add(:id_number, "is already taken")
else
member_ids = Member.all.map(&:id_number)
if member_ids.include?(self.id_number)
errors.add(:id_number, "is already taken")
end
end
end
The first one is cleaner, the second one should be faster. But check this out with a lot of db entries and a benchmark tool.
I think you'll want something like this:
def your_validation
if self.id_number.present?
if Employee.exists?(:id_number=>self.id_number) || Member.exists(:id_number=>self.id_number)
errors.add(:id_number, "is already taken")
end
end
end
if you have indices on the id_number columns this check should run very quickly and is the same check that validates_uniqueness_of would use within a single table. Solutions that involves fetching all ids into rails will start running into problems when the tables get large.
Another thing to note is that if your app runs multiple web server instances at a time these kinds of rails side checks can't 100% guarantee uniqueness as they are subject to races between threads. The only way to ensure uniqueness in such situations would be to use facilities built into your database or generate the id_numbers yourself from a source that precludes duplicates (such as a database sequence).

How to store find_by_sql_results in session variable

Here is my controller code to check login details of a user
def validateLogin
#email = params[:userEmail1]
#pass = params[:userPassword1]
if params[:userEmail1] != nil
valid_user = Userprofile.find_by_sql(["select * from userprofiles where userEmail=? and userPassword=?", #email, #pass])
if valid_user.count > 0
session[:email] = #email
session[:uid] = valid_user.id
session[:userType] = valid_user.userType # usertype is a column in userprofiles table
# But here i am not receiving the usertype it gives error that undefined variable usertype.
redirect_to "/userhomes/"
else
flash[:message] = "Either email or password is incorrect"
redirect_to '/'
end
else
flash[:message]="Fields can not be blank"
render :action=>'defaults'
end
Please help
session[:userType] = valid_user.userType
# Error: (usertype is a column in userprofiles table)
But here i am not receiving the usertype it gives error that undefined variable usertype.
You are seeing this error because you receive an array of objects from find_by_sql. You even check the size of the array in your if clause.
From your code I think you expect only one returned object. But you still need to get it from the array like so:
profiles = Userprofile.find_by_sql(["select * from userprofiles where userEmail=? and userPassword=?", #email, #pass])
if profiles.count > 0
user_profile = profiles[0]
#... your other stuff
end
Another variant which also much better uses Rails idioms and especially ActiveRecord as is was inteded to be used is to let it construct the SQL by itself which is generally safer, less prone to errors and cacheble.
You didn't write which version of Rails you are using, but for Rails 2.3.x, it looks like this
user_profile = Userprofile.first(:conditions => {:userEmail => #email, :userPassword => #pass})
For Rails 3.x, it looks like this:
user_profile = Userprofile.where(:userEmail => #email, :userPassword => #pass).first
Both variants expect that you have a model called Userprofile, which you generally require to effectively work with database objects in Rails. What both queries do is to create a new model instance from the first row returned from your query (that's what the first does).
Generally, you should get a book or some guide on the internet and learn how to properly use ActivRecord. Note that the API has seriously changed between Rails 2.3 and Rails 3 so make sure to use a guide for your actual Rails version.
And as a final advice, you shouldn't store actual ActiveRecord objects in the session. They would need to be serialized on store and de-serialized on access. What makes it hard (or impossible to track object references.
Also, Rails uses the cookie session store by default, which means that the whole session data is stored in a cookie on the client. The data therein in fully readyabkle to anyone with access to the cookie as it is only signed to restrict tampering with the data, but it is not encrypted. Thus, in your case anyone would be able to ready the (unecrypted) password.
Instead of storing the model object, you should store it's id instead and get the actual (and up-to-date) object from the database instead on each request. This is much easier, saves you from cache inconsistencies (what happens if the user changes her password) and is probably faster than to transfer a huge session cookie from the client on each request.

What's the best way to validate multiple emails and handle errors in Rails?

In the current app I'm building I've got a textarea where a user will enter a comma-delimited list of email addresses.
I'm currently splitting the list into an array and then saving one by one. But if, say, I have this input...
blah#example.com, test#example, foo#example.com
... then blah#example.com will be saved, but saving test#example will fail. So I then need to remove blah#example.com from the comma-delimited string of values that I pass back to the textarea when I show the error that test#example isn't a valid email address.
Is there a better way to validate these on the server side and handle errors without getting fancy / ugly in the controller?
Thanks in Advance!
Assuming this is a model that has_many emails, and the email model uses :validate_email, you could do something like the following:
class Foo < ActiveRecord::Base
validate :must_not_have_invalid_addresses
...
def emails=(addresses)
#invalid_addresses = []
addresses.split(",").each do |address|
#invalid_addresses.push(address) unless emails.create({:address => address})
end
end
def must_not_have_invalid_addresses
errors.add_to_base("Some email addresses were invalid") unless #invalid_addresses.empty?
end
end
This provides a validation error + an array of the invalid email addresses which you can make accessible to your view if you like.
ruby has a split function (.each) described here and supports regular expressions as described here
as such, you'd split the string (using "," as your separator) and then use the regular expression to validate each e-mail.
You can put saving emails in transaction. Then if any save will fail, then all previos saves are canceled. In such case, validations can be done only on model layer.
I think it would be clear code, but for sure it isn't the fastest possible way (but using Ruby means you are not doing it in even fast way ;) )
If you have them in a variable called emails, perhaps something like this may work:
if valid_emails?(emails)
# then have your normal logic here
if #user.save
flash[:notice] .....
end
end
private
def valid_emails?(emails)
not emails.find {|email| email =~ /[\w\.%\+\-]+#(?:[A-Z0-9\-]+\.)+(?:[A-Z]{2,}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|jobs|museum)/i }.nil?
end
EDIT: actually you may just want to use this regular expression. It was taken from the restful-authentication plugin.

Resources