Suppose this is my users controller:-
class UsersController < ApplicationController
def show
#user = session[:user]
end
def prepare
session[:user]= User.find(:first)
redirect_to :action => 'show'
end
def update
#user = session[:user]
#user.name = 'rai'
redirect_to :action => 'show'
end
end
View for show.html.erb
<%= #user.name %>
Show page
<%= link_to 'Update', :action=> 'update' %>
Now Explaining the issue:---
Suppose first time user opens the browser with
http://localhost:3000/users/prepare
o/p will be:---
Mohit Show page Update // supposing user table has values mohit as name
Now when he click on update he will get as output like this:--
rai Show page Update
But this should not happen cause
firstly when are at prepare action where value is fecthced from db and its mohit. and then he is redirected to show ie displying the values from session. ie mohit
Now when user click on the update he is redirected to update when value from session is stored to a user instance and the name attribute of that user instance has been modified to rai. and finally redirected to show page.
Now in this page when user's name is displayed its showing rai.. thats the QUESTION why??
cause session should store the same mohit value cause we havnt made any change in session..
When you are doing
#user = session[:user]
#user variabe is assigned reference to the object session[:user], not the copy of it.
So when you are modifying #user, session[:user] is also modified, as they are essentially the same object.
I'm not sure, but I think it is something with hashes and classes and about copying them. So when you do:
#user = session[:user]
You are not making a copy of object but it is something likre reference in C++, both #user and session[:user] are reffering to the same object, so when you modify one, you get both modified.
Example from console:
a = {}
a[:user] = User.first
a[:user].firstname # => "Mohit"
b = a[:user]
b.firstname = 'rai'
a[:user].firstname # => 'rai'
a[:user] = User.first
a[:user].firstname # => 'Mohit'
Related
I'm trying to implement a form with the following behavior:
1) Input some semantic data about a user (i.e. username).
2) Do a User.find_by(:username = username).
3) If such a user exists, direct to show page for that user.
4) If such a user does not exist, create a new user with the provided username, then redirect to the show page for that user.
This should be simple but I cannot figure out how to format the form_for helper and my show and create actions in my user_controller to implement this behavior.
I currently have:
form_with :url => 'users/:id', :method => :get do
...
end
because I'm ultimately trying to invoke the "show" method of the controller. However, my form does not take in a user's id as a parameter, and when the user does not yet exist there is no :id parameter to access at the time of the form's submission.
How can I set up my form to redirect to show in each case, while still adhering to the logic explained above?
You can do something like this in your User's Controller create action
def create
usr_name = params[:username]
#user = User.where(username: usr_name).first_or_initialize
if #user.persisted?
redirect_to user_path(#user) # or whatever your user show path is
elsif #user.save
redirect_to user_path(#user)
else
render :new
end
end
You would just need to make sure that you are validating the uniqueness of usernames.
Also, first_or_initialize(and its counterpart first_or_create) can take in a block. So, you can assign other attributes to the new User like this...
User.where(username: usr_name).first_or_initialize do |usr|
usr.some_attribute = some_value
end
you can use find_or_initialize_by(unique_key) in your create method.unique_key can be any key which you are using to identify your user such as email,phone etc.
Just installed Geocoder and trying to get it to show signs of life. I want a user to log in and have the profile landing page to show data on their ip. Since I'm in the dev environment, here is my attempt at a temporary solution:
class SessionsController
def create
user = User.authenticate(params[:username_or_email], params[:password])
if user
session[:user_id] = user.id
redirect_to profile_path, :notice => "Logged in!"
#request = Rack::Request.new({'REMOTE_ADDR' => '107.128.188.218'})
#location = #request.location
#blah blah blah
end
in the view:
<%= #location.data %>
the error I get is undefined method 'data' for nil:NilClass
in the intializers/geocoder.rb
Geocoder.configure(
:timeout => 30
)
You are assigning ivars #request and #location in the create action of your SessionsController, but the lifetime of these variables is just that request. After the controller action finishes, you are redirecting the client's browser to your ProfilesController, so you need to move those two assignments into ProfilesController#show, then it should work.
EDIT: Since you say you are using the SessionsController show action for the user's profile, moving those two statements to that action method should do the trick. :-)
I am working on a reservation project and after login I want to pass the current user information from the Sessions Controller to a Reservations Controller via a home page. How can I do that? I have been following Michael Hartl's Rails Tutorial for the login. Here's what I have tried in the create section of the Sessions Controller
render home_path :local_variables[:id => user_id]
and in the home_path(users#home)
<%= link_to new_reservation_path(:user_id => :id) %>
but it shows me an undefined method error. My Reservation model has a user_id column.I am confused regarding this matter. What should I do?
render home_path :local_variables[:id => user_id]
Seems weird to me to pass locals that way (don't even know if it's possible, never seen locals used outside of rendering views/partials).
I think the best way is to redirect instead and set the user in the sessions once they have been logged in successfully, so in your login action:
user = User.find_by_email(params[:user][:email]) # or however you are finding the person trying to login
session[:user] = user
redirect_to home_path
then in users#home
#user = session[:user]
and finally, in the view:
<%= link_to new_reservation_path(:user_id => #user.id) %>
EDIT
Actually, probably not a good idea to store an object in the session, instead of
session[:user] = user
You could try:
session[:user_id] = user.id
and then to find the user:
#user = User.find(session[:user_id])
If you still get an undefined error then it's probably because the user is nil (unless the User model actually has no id attribute, which would be strange) so there might be an issue with the login part, it's hard to say without knowing what that looks like.
If you need the logged in user on every page, you could abstract the functionality out into the application controller:
before_filter :check_user
def check_user
#user = User.find(session[:user_id]) if session[:user_id]
end
Then, you can use the #user instance variable anywhere in your app.
I have a small problem:
I am trying to send a variable from controller 1 to controller 2 and then send it back to controller 1. Here is the logic:
In order to start making a new model, the user has to sign in.
Guest users must also have access to the path of the new model.
If not logged in, guest users have to be redirected to sign in.
Once logged in, users have to go back to the path they previously tried to access.
I decided to pass the type of the model to the log in screen and then pass it back to the new model action.
The variable type usually comes from the route, so upon redirecting to the login screen, I just pass it over.
businesses_controller.rb:
before_filter :require_login
def require_login
unless current_user
redirect_to signin_path( :type => params[:type])
end
end
When I get redirected, the variable gets passed into my url - so far, so good:
/signin?type=ccompany
The problem comes when I try to grab the variable from the URL and use its value to redirect the successfully logged in user back to where he tried to go to:
sessions_controller.rb:
class SessionsController < ApplicationController
before_filter :initialize_type , :only => [:new , :create]
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
sign_in user
if defined?(#type)
redirect_to send("new_#{#type}_path")
else
redirect_back_or user
end
else
flash.now[:error] = t('sign.invalid')
render 'new'
end
end
def destroy
sign_out
redirect_to root_url
end
private
def initialize_type
#type = params[:type]
end
end
When I put the #type variable into my view, its value renders without problems. But when I try to log in, this is the error I get:
undefined method `new__path' for #<SessionsController:0x007f76189d42f8>
I also tried
redirect_to :controller => "businesses" , :action => "new" , :type => params[:type]
But the type symbol passes with no key. I also tried putting a variable there, still doesn't work.
So far, it has only worked when I just write down a string that matches the path, but that is not what I need. I need to pass the value of the params[:type] hash. I've put the value of the variable in the view and it's all good, but when I try to pass it into a function, its value somehow 'disappears'.
How can I make this work?
Problem is:
if defined?(:type)
it always returns true, as a symbol is always defined. What you want is:
if defined?(#type)
however this might add 'unintentional feature' to your code, as nil is well defined in ruby. You should do:
if #type
NOTE:
I need to pass the value of the :type symbol. - symbol has no value. Variables do have values, symbol is a symbol and it is its own value.
After understanding the question:
The above still holds.
You cannot pass instance variable from one action to another. For each request rails instantiate new controller instance and all instance variables are lost. There are number of ways to pass it though.
Create hidden field in your form to hold the value. You need to keep in mind that its value can be overwritten by any internet user with a minimal knowledge of how internet works.
In your new action simple write the value into a session and read it in you create action. Since session is either stored server side or stored in encrypted cookie, there is very small chance anyone can temper with it.
Ok, I found a workaround.It's a bit clumsy and may cause security issues, but it works. I forced the variable to pass as a session parameter.
I put the received variable as a hidden field in the new session form:
<%= form_for(:session , url: sessions_path ) do |f| %>
<%= f.label :email , t('session.email') %>
<%= f.text_field :email %>
<%= f.label :password , t('session.password') %>
<%= f.password_field :password%>
<%= f.hidden_field :val , value: #type %>
##type is set in the before filter as a variable passed from another controller
Then, upon submit, I just catched the parameter and put it into its place:
if user && user.authenticate(params[:session][:password])
sign_in user
if defined?(#type)
params.require(:session).permit(:val)
#type = params[:session][:val]
redirect_to send("new_#{#type}_path")
else
redirect_back_or user
end
else
flash.now[:error] = t('sign.invalid')
render 'new'
end
end
Now the function works properly. Unfortunately, I have to use the view as a "conduit" that passes a variable. I would be really happy if somebody writes a helper method for this or a better workaround.
Cheers.
In my current app, i use Geocoder gem to get the city and the country of the visitor. I use hidden fields in my view to get these details. When the login form is submitted, these details will be sent to the controller and the controller will save them to the database. When I try to get these details directly from the controller by using
request.location.city
It will assigning a blank value to the database. If I use hidden fields in the view, some one can temper with them right? So, how can I fix this?
You should store visitor information before you render any content:
class UsersController
def new
# I suspect that, for fast insert, you should probably use a NoSQL database
# to perform `store!` or even just write it to a log file
Visitor.store!(:city => request.location.city, :ip => request.ip)
end
def create
#user = User.build(params[:user].merge(:city => request.location.city))
if #user.valid?
#user.save
flash[:notice] = "You've been registered!"
redirect_to user_dashboard_path
else
flash[:notice] = "Couldn't register your account"
render action: "new"
end
end
end