def create
chef = Chef.find_by(email: params[:email])
if chef && chef.authenticate(params[:password])
**session[:chef_id] = chef.id**
flash[:success] = "You logged In"
redirect_to recipes_path
else
flash.now[:danger] = "Check your email or password"
render 'new'
end
end
What does
session[:chef_id] = chef.id
do? Is that session[:chef_id] a kind of variable or something? To which the id of a chef is assigned? Can I use some other name there?
That's storing the chef_id in the session, which is a way of persisting data accross multiple requests. It's not specific to Rails, as the session is also available in all web applications. You could read more on the Rails session here and more on web sessions here
session[:chef_id] = :foo stores chef_id key in session with value of :foo.
So you can fetch assigned :foo value later by calling session[:chef_id].
In your code it is assigned to id of chef who has email equals to params[:email].
You can name :chef_id whatever your like but I think it is pretty normal name.
Related
How to send a variable/parameter from an action without using the URL?
I need to create a user in multiple steps, and the user is created on the 2nd step. I need to receive the email of the user from the first step.
One first step, I receive the email in params, and use this line of code: redirect_to new_user_registration_path(email: params[:email]) to send it out to the next page/action.
For some reasons, I have been told that I can't use emails in URLs, so now I need to send the email under the hood which surely is possible through the POST method, but redirect_to doesn't support POSTs requests.
There could be a suggestion of using render instead of redirect_to, but I'm using Devise, so I would like to hand over the functionality to Devise, instead of manually doing it all by myself.
There is another idea of using cookies to store the email address, but I'm interested in more of a Rails way.
There can be another way too, one way is to using session
On the first step of form submission store email in session variable and use it on the further step and after that clear that session variable.
Eg -
def first_step
#user = User.new
end
def second_step
# Assuming after first step form is being submitted to second step
session[:email] = params[:email]
end
def next_step
user_email = session[:email]
end
Hereby session[:email] will be available everywhere except model layer unless it is set to blank (session[:email] = nil), that should be set to blank after the user is created.
You can use the flash for this
flash[:email] = params[:email]
redirect_to new_user_registration_path
in your view, something like this
<%= hidden_field_tag :email, flash[:email]
You will need to add this line
class ApplicationController < ActionController::Base
add_flash_types :email
I'm just posting this as a possible solution, I realize this is not some best-practice solution for every case
In my controllers I often have functionality like this:
#account = Account.new(account_params)
if #account.save
if #account.guest?
...
else
AccountMailer.activation(#account).deliver_later
#account.update_column(:activation_sent_at, Time.zone.now)
flash[:success] = "We've sent you an email."
redirect_to root_path
end
end
What's the best way to send an email and update the activation_sent_at attribute without having to save the record twice? Calling update_column doesn't feel right to me here because AFAIK it creates an extra SQL query (correct me if I'm wrong).
Your code is fine. I wouldn't change it.
For example, you might be tempted to do something like this:
#account = Account.new(account_params)
#account.activation_sent_at = Time.zone.now unless #account.guest?
if #account.save
if #account.guest?
...
else
AccountMailer.activation(#account).deliver_later
flash[:success] = "We've sent you an email."
redirect_to root_path
end
end
Aside from the small issue that there's now repeated logic around #account.guest?, what happens if AccountMailer.activation(#account).deliver_later fails? (When I say "fails", I mean - for example - AccountMailer has been renamed, so the controller returns a 500 error.)
In that case, you'd end up with a bunch of account records which have an activation_sent_at but were never sent an email; and you'd have no easy way to distinguish them.
Therefore, this code warrants running two database calls anyway: One to create the record, and then another to confirm that an email was sent. If you refactor the code to only perform a single database call, then you'll become vulnerable to either:
Sending an email to a non-created user, or
Marking a user with activation_sent_at despite o email being sent.
The controller should be doing two transactions, not one. Which is why I said: Don't change it.
I think my question title is bit confusing. But what I am meaning to ask is I am creating my own authentication system using mobile. Just like devise comes with current_user to create a session, I want to know how can I achieve same on a different model.
I have a model called Commuter. It also has a id with it.
A record of commuter looks like this.
Commuter.last
<Commuter id: 867, phone_number: "9483942090">
I am trying to create a session after verfying the mobile number with my controller method as follows:
def verify
#commuter = Commuter.where(phone_number: params[:phone_number]).first
if (#commuter && #commuter.authenticate_otp(params[:otp],drift:300))
#commuter.auth_active = true
if #commuter.save
#Removed from session after verified it
session[:phone_number] = nil
session[:is_verified] = nil
#signed in commuter after verified it
sign_in(:commuter, #commuter)
flash[:notice] = "Your mobile no is verified."
end
else
flash[:alert] = "You have entered wrong otp.Please check again."
end
puts "#{current_commuter.phone_number}"
redirect_to root_path
end
I just a puts there to debug. So right now I am getting current_commuter as undefined local variable for obvious reasons I guess. So I wanted to know how can achieve this session based current commuter ?
You can save the Commuter id in the session as session[:cid] = 1 and create a method on your base controller like this
def current_commuter
#commuter ||= Commuter.find session[:cid]
end
helper_method :current_commuter
My book says to run "rails g controller sessions" and edit it as
class SessionController < ApplicationController
def create
user = User.find_or_create_from_auth_hash(request.env['omniauth.auth'])
session[:user_id] = user.id
redirect_to root_path
end
end
What is session[:user_id]? If X is controller, Y and Z are some string, can I define as X[:Y] = Z?
The session[:user_id] is a special variable, that can be accessed like a hash,, storing all information you store into your application's session.
You can read more about the session in Rails here: http://guides.rubyonrails.org/action_controller_overview.html#session
It's basically a Hash that's shared between requests to store information and re-use it in sub-sequent requests.
session[:user_id] is a variable, that you earlier need to store and then reuse it throughout your session.
F.e. You have authentication and then you have to jump through pages, that needs user_id. So you can use session, to store it there. You can find more info there http://guides.rubyonrails.org/security.html.
No. session is just an object which respond to [](key) method. You cannot define something like controller[:foo] = 'bar'
I'm trying to build a registration module where user can only register if their e-mail is already in an existing database.
Models:
User
OldUser
The condition on User will be
if OldUser.find_by_email(params[:UserName]) exists, allow user registration.
If not, then indicate error message.
This is really simple to do in PHP where I can just run a function to execute a mysql query. However, I couldn't figure out how to do it on Rails. It looks like I have to create a custom validator function but seems to be overkilled for a such simple condition.
It should be pretty simple to do. What have I missed?
Any pointer?
Edit 1:
This solution by dku.rajkumar works with a slight modification:
validate :check_email_existence
def check_email_existence
errors.add(:base, "Your email does not exist in our database") if OldUser.find_by_email(self.UserName).nil?
end
For cases like this, is it better to do validation in the model or at the controller?
you can do it as
if OldUser.find_by_email(params[:UserName])
User.create(params) // something like this i guess
else
flash[:error] = "Your email id does not exist in our database."
redirect_to appropriate_url
end
UPDATE: validation in model, so the validation will be done while calling User.create
class User < ActiveRecord::Base
validates :check_mail_id_presence
// other code
// other code
private
def check_mail_id_presence
errors.add("Your email id does not exist in our database.") if OldUser.find_by_email(self.UserName).nil?
end
end
I'd recommend starting with Devise.
See https://github.com/plataformatec/devise
Even if you have unusual needs like these, you can normally adapt it. Once you get to know it, it's extremely powerful, solid and debugged, and you can do all sorts of things with it.
Bellow is just an initial implementation .../app/controller/UsersController for User registration related actions.
def new
#user = User.new
end
def create
#user = User.new(params[:user])
#old_user = User.find_by_email(user.email)
if #old_user
if #user.save
# Handle successful save
else
render 'new' # and render some error message telling why registration was not succeed
end
else
# render some page with some sort of error message of 'new' new users
end
end
Update:
Check out the following resources for more info:
Ruby on Rails Tutorial
Rails: User/Password Authentication from Scratch, Part I/II