Rails Advanced Sign Up Implementation How To? - ruby-on-rails

I am a three week old Rails newbie and I have something that I want to implement but have no idea how to go about it. I'm making an app:
I want a user to enter some sign-up info on the new users view page, then, when they click submit, instead of the user being created and saved in the database right away, instead I want them to be taken to a second webpage where they will be asked for some final verification before they can create their account. Then when they click 'verify' and the verification passes, the account is finally created and saved to the database.
This is hard for me because I only know how to make basic forms, where you enter info, hit submit, and you have a new entry in the database. I don't know how to defer the "user creation" for another webpage, but a friend has mentioned something about http requests, but I still don't know anything. I'll post some of my code so far:
My users_controller new definition:
def new
#user = User.new
#user.websites.build
end
My users_controller create method:
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Welcome!"
redirect_to #user
else
render 'new'
end
end
My users/new.html.erb sign up form:
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<%= render 'shared/error_messages' %>
<%= form_for(#user) do |f| %>
<%= f.label :email %>
<%= f.text_field :email %><br/>
<%= f.label :password %>
<%= f.password_field :password %><br/>
<%= f.label :password_confirmation, 'Confirm Password' %>
<%= f.password_field :password_confirmation %><br/>
<%= f.fields_for :websites do |builder| %>
<%= builder.label :url, 'Website URL' %>
<%= builder.text_field :url %>
<% end %>
<%= f.submit "Sign up", :id => 'submit' %>
<% end %>
I've never asked a question that's just asking for advice like this before, so I'm hoping this is the right place to ask. Any and all help is appreciated.

I think you are trying to create a "multistep" form...
There is a very good railscast about it: http://railscasts.com/episodes/217-multistep-forms?view=asciicast
However, you might face some validation problems, as you need to validate each step individually. So, take a look also here: http://guides.rubyonrails.org/active_record_validations_callbacks.html#conditional-validation
I hope it helps...

You'd better use Devise for user management.
https://github.com/plataformatec/devise
Here's a railscast tutorial.
railscasts.com/episodes/209-introducing-devise

If you're doing multistep, you can always pass information from one page to another. You can store it in a session array, repost it to the other page, or even make a cookie.
Remember to also do every step of this under https, because account creation requires sensitive info.
If Rails is your first I would actually recommend something lower level like PHP so you can understand how everything works before you start doing stuff with a high level framework like rails. MVC is usually not the best first step in web development, even though it is powerful and easier than doing everything from scratch.

Related

How does hidden_field_tag works in an reset password form

It's being really hard to understand how a form work with hidden_field_tag in a specific situation. I wish someone would explain me what happens.
It's the form from reset password from railstutorial.
My view is:
<% provide(:title, 'Reset password') %>
<h1>Reset password</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#user, url: password_reset_path(params[:id])) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= hidden_field_tag :email, #user.email %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Update password", class: "btn btn-primary" %>
<% end %>
</div>
</div>
My update action and the strong params method:
def update
if params[:user][:password].empty?
#user.errors.add(:password, "can't be empty")
render 'edit'
elsif #user.update_attributes(user_params)
log_in #user
#user.update_attribute(:reset_digest, nil)
flash[:success] = "Password has been reset."
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:password, :password_confirmation)
end
When access the edit action, the user enter an email an the app send a link to this email, with an activation token, like this path:
/password_resets/9Ij91DFChTeWTitNDVJfYw/edit?email=example%40railstutorial.org
The user open this link and change the password. That is perfectly functional. My question is: if I already have the user from the edit action and doesn't use params[:email] (provided with the hidden_field_tag) explicitly in my update action, why do I need the hidden tag?
Actions are separate from each other. When you first render your password reset form in scope of the edit action, your #user instance variable, being somehow initialized before, is used to supply a value for the hidden email field.
When later user submits the form, its data gets processed by the update action in a completely separate request. This request does not know what happened before, specifically, that the form was originally rendered by the edit action. It also doesn't have access to any of the objects that were there in scope of the edit action.
In order to process the request, your update action code needs to initialize all the objects it needs anew, and that is primarily the #user object.
If current action doesn't know what happened before, how it would know which user record should go to the #user variable? It can find the user by email, this is why you supplied it in the params. Your edit action code made it available for the next request.
From the look of your edit action, there should already be some method in your controller which initializes the #user object before the action code gets executed. Look for before_action callback.
The method probably looks up the user by the email passed in the params. When action itself gets executes, the #user object is already there so it might seem Rails somehow knows how to get it. It doesn't, your controller code makes this happen.
Therefore you need hidden tag to pass context between actions. There are other ways to do that, like using session or cookies, but this way is probably the simplest.

Two different login pages for one model with devise on rails

I am working on an rails app in which there is one table Users, which has two roles Admin and member. Until now I am using single login page.
But now, I need to make two different login pages with completely different styling for admin and member.
Moreover Admin don't have sign_up option but member has.
I am totally confused that is it possible or not?
If possible, how to achieve this.
Devise sign in form is just a form with action matching to devise controller.
You can write this form code anywhere you like,
<%= form_for(:user, :url => session_path(:user)) do |f| %>
<%= f.text_field :email %>
<%= f.password_field :password %>
<%= f.check_box :remember_me %>
<%= f.label :remember_me %>
<%= f.submit 'Sign in' %>
<%= link_to "Forgot your password?", new_password_path(:user) %>
<% end %>
Adding to #maximus answer, you could use the Pundit gem to define your logged in :user action through a UserPolicy controller. Within this controller, you should be able to define which direction a passed in User navigates to defined by their logged in role.
https://github.com/elabs/pundit

Edit user from another view?

I need to edit a user's data from another view. Example, when a user makes a new post, they can change their name, phone number, address, etc. I am using the Devise gem.
Does anyone have any ideas?
You can edit any model/migration from any view. Rails doesn't restrict you on that, though it does try to lead you down the conventional way. In the view you want to edit your user, try something like this:
<%= form_for #user do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
...more code here...
<% end %>
Just make sure that #user is a user object.

What is the use of hidden_field method on Page 491 in Michael Hartl's Rails tutorial book?

(First of all, I want to note that this question might only be answered if you have read the book or even got the book in your hand so that you can read the relevant part on Page 491, since I can only extract a small section and paste it here. I apologize for that.)
This is the _follow.html.erb partial used in the book on page 491:
<%= form_for current_user.relationships.
build(:followed_id => #user.id) do |f| %>
<div><%= f.hidden_field :followed_id %></div>
<div class="actions"><%= f.submit "Follow" %></div>
<% end %>
It is for creating a "follow" button to follow other users.
And then in his explanation of the line dealt with the "f.hidden_field", he said,
Finally, you’ll note that the form doesn’t have any content other than the button, but it still needs to send the followed_id, which we accomplish with hidden_field;
My questions are:
Why do we need a hidden_field here to send the followed_id? As far as I see it, the line
<%= form_for current_user.relationships.
build(:followed_id => #user.id) do |f| %>
has already assigned the :followed_id attribute right?
I think those two part of code are different.
<%= form_for current_user.relationships.build(:followed_id => #user.id) do |f| %>
This line creates a new record for the form.
<%= f.hidden_field :followed_id %>
This code create an actual params of your form.
--> You might want to read that to learn more about form_for http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for

Getting check_box to work in a Rails form

My objective is to use a check box on the sign-in form to allow the user to choose to stay signed in after browser close. I'm using this code in the sign-in form for the check box (views > sessions > new.html.erb):
<div class="field">
<%= f.check_box :stay_signed_in %> Stay signed in?
</div>
:stay_signed_in is therefore 1 if checked, and 0 if not. I then (attempt to) set a session variable :staysignedin to either true or false depending on the value of :stay_signed_in (in the sessions controller):
def create
session[:staysignedin] = (params[:session][:stay_signed_in] == "1") ? true : false
...
end
There is something wrong with this code. Even when the box is checked and :stay_signed_in is definitely 1, session[:staysignedin] is never set to true. Where am I going wrong?
Edit:
The rest of the sign-in form looks like this:
<%= form_for(:session, :url => sessions_path) do |f| %>
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.check_box :stay_signed_in %>
Stay signed in on this computer?
</div>
<div class="actions">
<%= f.submit "Sign in" %>
</div>
<% end %>
Edit: correction: it IS setting session[:staysignedin] to be true or false correctly, so the fault must be elsewhere. The problem is that with 'stay signed in' checked, when I close the browser and reopen it, the user is not still signed in. When I remove the 'stay signed in?' check box on the sign in form and all the relevant code, leaving only the permanent sign-in code, it works fine.
def sign_in(user)
if session[:staysignedin]
cookies.permanent.signed[:remember_token] = [user.id, user.salt]
else
session[:userid] = user.id
end
self.current_user = user
end
def sign_out
if session[:staysignedin]
cookies.delete(:remember_token)
else
session[:userid] = nil
session[:staysignedin] = nil
end
self.current_user = nil
end
What does the rest of your form code look like?
My guess is that :stay_signed_in is not storing to params[:session][:stay_signed_in], but rather params[:something_else][:stay_signed_in] or just params[:stay_signed_in]
If you're running on local WEBrick you can see the parameters that are posted with each request, that will help you make sure you've got the right parameter key.
-EDIT-
Based on your last comment, I would recommend using Devise to handle your authentication logic. It is super-easy to implement, fully Rails 3 compatible, and includes built in code for handling sessions and remembering to stay logged in.
It's probably not worth your time to get stuck for this long on a simple sessions remembering issue when there's such a good plugin available that is lightweight, rock-solid, well-documented etc.
For an intro, see this Railscast.

Resources