Detect duplicate records using Sequel and PostgreSQL - ruby

I want to check to see if the email address that a new person uses when registering is a duplicate of an existing record.
Here is the code:
post '/signup' do
email_address = params['email_address']
username = params['username']
password = params['password']
#duplicate = DB[:users].select(:email_address).where('email_address = ?', email_address)
if email_address = #duplicate
redirect "/?msg=Email address already in use. Try again"
return
end
end
This blocks all attempts even when the email address is not a duplicate and redirects with the error message.
If I substitute #duplicate with duplicate (without the # mark) then same result, all attempts blocked. If I use == instead of = then duplicate email addresses are ignored and no attempts are blocked.
I know the query:
DB[:users].select(:email_address).where('email_address = ?', email_address)
is correct because I have tested it. So I assume the problem lies with the construction of the if clause.

You need add .first at the end of you query otherwise you will get an array.
After that you can test if #duplicate != nil

If you write email_address = #duplicate the code inside the if will be always excuted since the condition for the if expression is always true. Anything in Ruby except nil and false evaluates as true in boolean context.
If you write email_address == #duplicate the code inside the if will be never executed since you are trying to compare
a string (email_address) with an object of class Sequel::Postgres::Dataset (#duplicate).
A better approach could be
post '/signup' do
# same code as yours...
#duplicate = DB[:users].where(email_address: email_address).count
unless #duplicate.zero?
# redirect and return
end
end

Related

How do I access submitted params in a rails mailer?

I had a previous question that helped me loop through all users where a certain question is met.
However, I'm realizing I can't hard code that condition. I need to somehow get that data from the submitted form, which doesn't seem to be possible in the mailer.
In other words, I'm trying to loop through all users where the user's state is equal to the home_state of the candidate being entered. Basically when the candidate is created, I want to get the home_state of that candidate, and then loop through all users, and for each user that has same state as that candidate, I want to send them the email via this mailer.
Here's my candidate_mailer.rb file
class CandidateMailer < ApplicationMailer
default from: 'wesleycreations#gmail.com'
def self.send_request(row)
#candidate = Candidate.new(candidate_params) # if I can access this here, how to I create the
# following array?
emails = []
User.where(state: #candidate.home_state).each do |u|
emails << u.email # To insert the user email into the array
end
emails.each do |email|
new_request(email,row).deliver_now
end
end
def new_request(email, row)
#candidate = row
mail(to: email, subject: 'New request')
end
end
But the
#candidate = Candidate.new(candidate_params)
obviously doesn't work because the params aren't available in the mailer.
Here in the candidates_controller.rb I have this
def create
#candidate = Candidate.new(candidate_params) #of course here I can access params
if #candidate.save
row = #candidate
CandidateMailer.send_request(row)
else
render('new')
end
end
SO the question is, how do I access params in rails mailer? And if I can't, then how do I refactor my code so that the lines that check if the user meets certain condition is done in the controller?
I was able to figure this out by doing this. after I saved the candidate, I saved the candidate to a global variable. and THEN I send the mailer.
def create
#candidate = Candidate.new(candidate_params)
if #candidate.save
row = #candidate
$candidate = #candidate
end
CandidateMailer.send_request(row)
else
end
end
This way the mailer had access to the new candidate that been created, and I was able to check my condition in there.
So in my mailer, when I use $candidate.home_state, it returned the correct state, mail went out, and made me very happy :)
emails = []
User.where(state: $candidate.home_state).each do |u|
emails << u.email # To insert the user email into the array
end

Sinatra Route returns nothing

I have a very simple example where sinatra simply returns no output.
The program enters the if clause but the block is not finished and therefore nothing is sent to rack, nothing goes to the browser... not a single character.
require 'sinatra'
get '/' do
var='confirmed'
if var == 'confirmed'
'Confirmed'
end
if var == 'declined'
'Declined'
end
end
The question is now: Is adding a "return" or "next" the way this is usually done? With it, its running... But I never found an example in the net that had to use a next statement...
So, is the "if logic" usually somewhere else and there is only a single erb :xyz at the end of a route?
I am confused...
You have the answer mostly. You always need to send something to rack to get a response.
You probably have a view to show the status on then you add at the end something like this (You can have multiple erb blocks just add for each route a erb call):
get '/' do
var='confirmed'
if var == 'confirmed'
st = 'Confirmed'
end
if var == 'declined'
st = 'Declined'
end
erb :myViewName, :locals => {:status => st}
end
Or just use return like this, if your response is just a string. Be aware that everything after this return isn't executed:
if var == 'confirmed'
return 'Confirmed'
end
It's nothing to do with the way Sinatra works, really. It's more of a Ruby matter. According to Sinatra readme:
The return value of a route block determines at least the response body passed on to the HTTP client, or at least the next middleware in the Rack stack. Most commonly, this is a string, as in the above examples. But other values are also accepted.
The problem in your code is that your last if is a statement itself. If your var variable isn't "declined", then the if block evaluates to nil, and as it is the last value in your route block, this is what gets returned by Sinatra.
When you use explicit return, you don't get to the second if and don't have this issue, which is why it works with explicit return.
You would not need an explicit return with a if/elsif block like this:
# This is one single statement that would return either confirmed or declined
# Request will always return a non-nil value
get '/' do
...
if var == 'confirmed'
'Confirmed'
elsif var == 'declined'
'Declined'
end
end
Or a case/when block:
# This is one single statement that would return either confirmed or declined
# Request will always return a non-nil value
get '/' do
...
case var
when 'confirmed' then 'Confirmed'
when 'declined' then 'Declined'
end
end

Checking if a record exists in Sinatra/DataMapper

I currently generate a user's profile page using their serial ID, like so:
get '/users/:id' do
#user = User.get(params[:id])
end
This works great, until a number is entered that doesn't exist in the database.
I'm aware I can change User.get to User.get! to return an ObjectNotFoundError error if the record isn't found, but I'm not sure how I can use this to my aid.
I used to use .exists? when using RoR.
Any suggestions?
Thanks in advance!
Edit: I'm going to leave the question unanswered, as I haven't actually found a solution to what I asked in the title; however, I did manage to solve my own problem by checking to see if the :id entered is higher than the amount of users that exist in the database, like so:
if params[:id].to_i > User.count
"This user does not exist."
else
#users_id = User.get(params[:id])
erb(:'users/id')
end
You already have the correct code:
#user = User.get(params[:id])
If no user exists with the given id, then #get will return nil. Then you can just do a conditional:
#user = User.get params[:id]
if #user
# user exists
else
# no user exists
end
This is a very common pattern in Ruby which takes advantage of the "truthiness" of anything other than false or nil. i.e. you can say if 0 or if [] and the condition will evaluate to true
You can recreate a .exists? method:
class User
def self.exists?(id_or_conditions)
if id_or_conditions.is_a? Integer
!! User.get id_or_conditions
else
!! User.first id_or_conditions
end
end
end
#get is similar to #find in rails, except it doesn't raise an error if the record is not found. #first is similar to #find_by in rails.

Ruby BCrypt hash comparison not working

I am a new comer to Ruby, so apologies if this question has already been answered. I have read the other questions and still cannot figure out what I am doing wrong.
I am creating hashed passwords for storing in a db like this:
new_user.password = BCrypt::Password.create(unhashed_password)
# Write the user to database
new_user.store_user
I then retrieve the user from the db by checking against the inputed user name, and then check the password like this:
# Get user from the database
def self.get_user(check_user_name)
db = User.open_db
user = User.new
user_arr = db.execute("SELECT * FROM user_data WHERE user_name = ?", check_user_name).first
db.close
# if the user exists check the password
if user_arr.size != 0
print "Enter your password : "
# Get password from user
user_input_password_attempt = gets.chomp
end
# Parse the db user into a user class if password guess is correct
stored_password = BCrypt::Password.new(user_arr[2])
if user_input_password_attempt == stored_password
##users_logged_in += 1
user.user_id = user_arr[0]
user.user_name = user_arr[1]
user.password = user_arr[2]
return user
end
:no_user
end
My problem is that the var stored_password is returning a hash and != user_input_password_attempt
I have read the Ruby-Doc and googled this extensively
When you use == you are actually calling the == method defined on the object on the left hand side, passing the right hand side as argument:
a == b
is equivalent to
a.==(b)
Depending on the object you call the == method you might receive a different result. In other words:
a == b
might or might not return a different result than
b == a
While personally I think this is nonsense and equality operators should be transitive, symetric and reflexive the BCrypt people have decided to implement it in another way:
def ==(secret)
super(BCrypt::Engine.hash_secret(secret, #salt))
end
(taken from http://bcrypt-ruby.rubyforge.org/classes/BCrypt/Password.html#M000009)
This means that you have to write:
stored_password = BCrypt::Password.new(user_arr[2])
if stored_password == user_input_password_attempt
...
end
in order to call the == method on the Password instance.

Why won't Sequel write my database table?

controller/makenew.rb
class MakeController < Controller
map '/makenew'
#require 'model/debate'
def debate
if request.post? #this line is potentially dangerous!
#---> 1/3 fetch postdata
data = request.subset(:question, :type, :category, :assertion)
data['user_id'] = user.id #id = request.params['id']
#---> 2/3 check permissions
if user.points < 40
flash[:error] = 'You don\'t have enough points to make a debate.'
redirect_referrer
else
debate = Debate.new
end
#---> 3/3 modify database
begin
debate.save(data)
flash[:success] = success
flash[:form_data] = debate
redirect 'debates'
rescue => e
Ramaze::Log.error(e)
#flash[:form_errors] = debate.errors
#flash[:error] = data
flash[:error] = e
#flash[:error] = 'Failure whilst saving. Contact technical support!'
redirect 'debates' #redirect_referrer
end
#|
end #closes posting conditional
end #closes makesave
end
The error I get is.
SQLite3::ConstraintException: debates.question may not be NULL
I have checked the postdata for data.question and it is not null.
What is going on?
You need to pass 'data' to #update. Thus:
debate.save(data)
is wrong, you have to do:
debate.update(data)
debate.save
If you don't do this, your debate object has no member assigned and thus its question member is nil, violating your DB constraints.
See the differences between #save and #update here:
Update : http://sequel.rubyforge.org/rdoc/classes/Sequel/Model/InstanceMethods.html#method-i-update
Save : http://sequel.rubyforge.org/rdoc/classes/Sequel/Model/InstanceMethods.html#method-i-save
In a nutshell: #save will save the current model instance to the database, while #update will change a bunch of instance attributes in one operation.
But you have to remember that changing a model instance's attributes DOES NOT write them to the database. You always have to call #save explicitly.
Are you sure that your model accepts mass assignment of primary keys?
Try calling Debate.unrestrict_primary_key
You can check the rules in the Sequel documentation.

Resources