Rails: Open a login modal from a controller - ruby-on-rails

I'm building an ecommerce app, and have a pretty standard user model and order model. For a user to place an order they must first be logged in. I'm using a Bootstrap modal for the standard login\signup forms.
I'm trying to find a way for when a signed out visitor clicks the Order Now button (links to /orders/new) to have the login\signup modal appear, and then after they log in they'll be redirected to /orders/new path.
Here's my current Orders#new method:
def new
#order = Order.new
if current_user
#order.user = current_user
#order.docs.build
else
redirect_to new_order_path
end
end
Here's my current Users#new and Users#create methods. I used this guide to setup the modals.
def new
#user = User.new
respond_modal_with #user
end
def create
#user = User.new(user_params)
respond_modal_with #user, location: root_path
respond_to do |format|
if #user.save
format.html {
login(params[:user][:email], params[:user][:password])
UserMailer.send_signup_email(#user).deliver_later
redirect_to root_path, notice: "You have successfully signed up"
}
format.json { render :show, status: :created, location: #user }
else
format.html { render :new, alert: "Registration failed" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
Sorry if I'm missing important info here. It's my first time posting and I'm a newbie. Thanks in advance for your help!
Edit:
Comments were recommending I use Devise for authorization, and I should have mentioned that I'm already using Sorcery for my authentication\authorization. So that part of the problem isn't an issue. The trouble I'm having is just how to trigger the modal from the Orders controller. Thanks!

Hi if you want to add functionality such as login/signup and perform all those operations, I would recommend you to use 'devise' gem. It is very easy and handy to use. You can find its documentation here. It has several methods like user_signed_in? which will help your problem signed out user to show login page and then continue your modal operation.
Thanks

To do this in a DRY way using Devise you can use the StoreLocation module. see the code: https://github.com/plataformatec/devise/blob/7d3d6fb3f04caea95e343956a88654f753b45af4/lib/devise/controllers/store_location.rb
To use that, you can follow the instructions from their WIKI: https://github.com/plataformatec/devise/wiki/How-To:-redirect-to-a-specific-page-on-successful-sign-in
Pretty much, you will have to:
1.Create a filter in your application_controller to redirect to sign-in page and save the current path:
def ensure_logged_in_or_redirect
unless logged_in?
store_location_for(:user, request.path)
redirect_to new_user_session_path
end
end
2.Create the after_sign_in_path_for method also in the application_controller, as shown on the Wiki.
3.Call that ensure_logged_in_or_redirect filter before the actions you want to protect, like the 'New Order' one.
Hope that helps.

Related

Rails 5, rendering the same view in a consecutive action doesn't refresh the browser

Quick question, I've been trying for the last couple hours to discern what is causing the following behavior but it's just beyond my grasp.
I have this two actions on my 'UsersController':
def new
#user = User.new
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
flash[:success] = 'Wellcome, %s! You have successfully
registered.' % [#user.name]
format.html { redirect_to login_path }
format.json { render :show, status: :created, location: #user }
else
flash.now[:error] = 'Hmm... There seems to be some errors.'
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
Basically, we render a clean 'new' view, try to register a new user, and if the creation of a user fails, the application should flash a message for the current action (the create action), and render the 'new' view, updating the previous one with the flash information and the errors of the #user variable.
The problem is that, although the server processes the response just fine, the browser does not update the page, never re-renders the page, it keeps the stale 'new' view. I've looked the response with chrome's web tools and it bears the updated view, but for some reason the browser just won't render it.
I think it has something to do with caching, but really I'm out of my element here. If instead of rendering I just redirect to the new action, the flash works fine (removing the .now(), that is), but this way I lose the #user, which I would like to keep with it's full functionality.
Any ideas why this behaves like this, or at least how to solve it?
If you redirect_to the new action, when the user submits, it will still post to the create action and the user_params would still take effect. Simplying rendering :new on the already new page will not perform a fresh request/response. To initiate a fresh request you will have to use redirect_to.

Rails: double render error in actions

I'm new in Rails world, but this Double render error a little bit interesting. I have never met this problem in PHP or in ASP.NET.
So I have two actions in one controller which is extended via Devise's Register controller.
I want to achieve that If the user logged in the two new_with_school and create_with_school actions will be redirected by the redirect_signed_in_user function.
class RegistrationsController < Devise::RegistrationsController
def new_with_school
redirect_signed_in_user
build_resource({})
resource.build_school
respond_with self.resource
end
def create_with_school
redirect_signed_in_user
build_resource(sign_up_params_with_school)
resource.school = School.new(sign_up_params_with_school[:school_attributes])
resource.school.user = resource
resource.role = 1
resource.save
respond_to do |format|
if resource.save
format.html { redirect_to after_sign_up_path_for(resource) }
format.json { render :show, status: :created, location: resource }
else
clean_up_passwords resource
set_minimum_password_length
format.html { render :new_with_school }
format.json { render json: resource.errors, status: :unprocessable_entity }
end
end
end
protected
def redirect_signed_in_user
redirect_to '/' if user_signed_in?
end
end
I gave this error message:
Render and/or redirect were called multiple times in this action.
Please note that you may only call render OR redirect, and at most
once per action. Also note that neither redirect nor render terminate
execution of the action, so if you want to exit an action after
redirecting, you need to do something like "redirect_to(...) and
return".
So it seems in Rails I can use only one redirect function in action.
But in this case, how can I achieve that if the user logged in, those 2 actions cannot reachable by the logged in user. The most beautiful solution will be, it the webapp could somehow redirect users to the root path (without throw an exception), so I want the most user friendly solution.
How can I do this? (I have read the devise code, but I cannot figure out how the root redirection works in)
Use this:
def redirect_signed_in_user
redirect_to '/' && return if user_signed_in?
end

How do I define a custom URL for a form confirmation page?

I am creating a basic product landing page with Rails in which users can enter their email address to be notified when the product launches. (Yes, there are services/gems etc that could do this for me, but I am new to programming and want to build it myself to learn rails.)
On successful submit of the form, I would like to redirect to a custom '/thanks' page in which I thank users for their interest in the product (and also encourage them to complete a short survey.)
Currently, successful submits are displayed at "/invites/:id/" eg "invites/3" which I do not want since it exposes the number of invites that have been submitted. I would like to instead redirect all successful submits to a "/thanks" page.
I have attempted to research "rails custom URLs" but have not been able to find anything that works. The closest I was able to find was this Stackoverflow post on how to redirect with custom routes but did not fully understand the solution being recommended. I have also tried reading the Rails Guide on Routes but am new to this and did not see anything that I understood to allow for creating a custom URL.
I have placed my thanks message which I would like displayed on successful form submit in "views/invites/show.html.haml"
My Routes file
resources :invites
root :to => 'invites#new'
I tried inserting in routes.rb:
post "/:thanks" => "invites#show", :as => :thanks
But I don't know if this would work or how I would tell the controller to redirect to :thanks
My controller (basically vanilla rails, only relevant actions included here):
def show
#invite = Invite.find(params[:id])
show_path = "/thanks"
respond_to do |format|
format.html # show.html.erb
format.json { render json: #invite }
end
end
# GET /invites/new
# GET /invites/new.json
def new
#invite = Invite.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #invite }
end
end
# POST /invites
# POST /invites.json
def create
#invite = Invite.new(params[:invite])
respond_to do |format|
if #invite.save
format.html { redirect_to #invite }
#format.js { render :action => 'create_success' }
format.json { render json: #invite, status: :created, location: #invite }
else
format.html { render action: "new" }
#format.js { render :action => 'create_fail' }
format.json { render json: #invite.errors, status: :unprocessable_entity }
end
end
end
It would seem as if creating a standard URL for displaying a confirmation would be relatively straightforward. Any advice on how to achieve this would be appreciated.
I guess you want to redirect after your create action, which is executed when the form is submitted.
Just add redirect_to in the following way:
def create
#invite = Invite.new(params[:invite])
if #invite.save
...
redirect_to '/thanks'
else
...
redirect_to new_invite_path # if you want to return to the form submission page on error
end
end
I omitted some of the code for brevity.
In your routes add:
get '/thanks', to: "invites#thanks"
Add the thanks action to your invites controller:
def thanks
# something here if needed
end
And create a thanks.html.erb page in app/views/invites.
I would do get "/thanks" => "invites#thanks" in routes.rb and then add this in your controller:
def thanks
end
Then add a file app/views/invites/thanks.html.erb with your thank-you content.
You could create a route like this:
resources :invites do
collection do
get 'thanks'
end
end
This will also create a path helper called thanks_invites_path.
It will be at the invites/thanks path, but if you want it to be on/thanks, you could just do as Jason mentioned:
get "/thanks" => "invites#thanks", :as => :thanks
The as part will generate a helper to access that page: thanks_path.
You would need a extra action in the controller called thanks, and put whatever info you need inside, and also you will need a additional view called thanks.html.erb
Since you want everybody to go to that page after a successful submit, in your create action you would have:
format.html { redirect_to thanks_invites_path} (or thanks_path), what ever you choose, when you name the route you can check it with rake routes if it's okay, and whatever rake routes says, just add _path at the end.

Curl POST to Rails application when controller looks like this

I'm trying to curl POST my Rails application in order to create a new Entry object. The problem is my entries_controller Create action looks like this:
def create
#user = current_user
#entry = #user.entries.build(params[:entry])
respond_to do |format|
if #entry.save
format.html { redirect_to landing_page_url, notice: 'Entry was successfully created.' }
format.json { render json: #entry, status: :created, location: #entry }
else
format.html { render action: "new" }
format.json { render json: #entry.errors, status: :unprocessable_entity }
end
end
end
Calling #user.entries.build just returns an exception because current_user doesn't exist. The thing is the Create action works well when I use the browser to create an Entry (as I login and create the current_user variable) but I do not know if it's possible to curl POST and create an Entry without changing the controller logic. And if it's not possible, could someone help reach the right direction towards building the controller logic (compatible with curl POST)?
I'm sorry if this is a dumb question, I'm fairly new to all this.
PS: I'm using Rails 3.2.3, if that's of any help.
Not sure how you create current_user (what it's based off), but this must exist for your method to work.
If you can pass a param to your action that specifies the user and set current_user as current_user ||= params[:user]... beware of the security implications of this.
You really should have a before_filter on your action to set current_user (via login, I'm presuming).

New to jQuery/Ajax how can I add a user relation to creating a new review?

I have been trying to get to grips with jQuery and been following a railscast on adding an Ajax add review form, which works fine but I would now like to add into it the ability for a review to belong to a user as well as a venue.
Reviews controller
def create
#review = Review.create!(params[:review])
#review.venue = #venue
if #review.save
flash[:notice] = 'Thank you for reviewing this venue!'
respond_to do |format|
format.html { redirect_to venue_path(#venue) }
format.js
end
else
render :action => :new
end
end
views\reviews\create.js.erb
$("#new_review").before('<div id="flash_notice"><%= escape_javascript(flash.delete(:notice)) %></div>');
$("#reviews_count").html("<%= pluralize(#review.venue.reviews.count, 'Review') %>");
$("#reviews").append("<%= escape_javascript(render(:partial => #review)) %>");
$("#new_review")[0].reset();
I have tried changing the controller to:
def create
#review = #current_user.reviews.create!(params[:review])
#review.venue = #venue
if #review.save
flash[:notice] = 'Thank you for reviewing this venue!'
respond_to do |format|
format.html { redirect_to venue_path(#venue) }
format.js
end
else
render :action => :new
end
end
but it just wont submit, with no errors.
I think I have the models set correctly with belongs_to and has_many, I think this is a controller issue I'll add other code bits if needed.
Development log
NoMethodError (undefined method `reviews' for nil:NilClass):
app/controllers/reviews_controller.rb:14:in `create'
Thanks for any help!
It appears that your error is residing with #current_user. According to your development log, #current_user is nil when you call #current_user.reviews on it. I would say track down where this #current_user instance variable is being set and find out why it is nil. Now, what kind of authentication are you using? Most authentication plugins, especially those used by Ryan Bates of the Railscasts you mentioned, use a local variable, say just current_user, as the means to access the currently signed in user. I know I do in all my code.
So, rewrite the line as
#review = current_user.reviews.create!(params[:review])
and see if that works. If it doesn't, change it back and then track down where this #current_user is being set. Chances are good it is being set in a before_filter :method_name at the beginning of your controller.
Calling create! (with exclamation mark) will throw an exception and thus abort your create action if saving fails. Check your log/development.log for these exceptions.
Use build instead of create and lose the exclamation mark.
def create
#review = #current_user.reviews.build(params[:review])
#review.venue = #venue
if #review.save
flash[:notice] = 'Thank you for reviewing this venue!'
respond_to do |format|
format.html { redirect_to venue_path(#venue) }
format.js
end
else
render :action => :new
end
end

Resources