I'm building an events platform with Devise gem & Rails 6.1. I'm having trouble with a small RSVP functionality.
When a non-login user click "Attend" button, it redirect the user to sign-in and once this is done the user is redirect back to the previous page. HOWEVER user has to click again "Attend" button.
I want the user redirect back and ALSO complete the create action ( POST action )
I managed to do the redirect work fine, but the user would have to click again on the "Attend" button which I do not want. I've searched the entire internet but none of the solutions worked. The closet solution I've found is from this blog https://blog.justinthiele.com/retaining-form-data-through-a-login-process-a/ which I tried to apply on my app but without success.
I would think that something is missing in the after_sign_in_path_for
method.
Any help would be appreciated.
UPDATE!
After debugging, I changed a bit the way I'm overriding Devise. I feel I'm very close to the solution but can't figure it out what I'm missing.
attendee_controller.rb
def create
if !current_user
# Store the data in the session so we can retrieve it after login
session[:attendee] = params
# Redirect the user to register/login
redirect_to user_session_path
else
@attendee = current_user.attendees.build({event_id: params[:id]})
respond_to do |format|
AttendeeMailer.with(event: @attendee).notify_event_creator.deliver_now
if @attendee.save
format.html { redirect_to @attendee.event, notice: "You have successfully registered for the event!" }
format.json { render :show, status: :created, location: @attendee }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @attendee.errors, status: :unprocessable_entity }
end
end
end
end
app\controllers\users\sessions_controller.rb (user_session_path)
protected
def after_sign_in_path_for(resource_or_scope)
if session[:attendee].present?
new_attributes = session[:attendee]
previous_url = session[:previous_url] #defined in the show method from the events controller.
@attendee = current_user.attendees.create({event_id: new_attributes[:id]})
#clear session
session[:attendee] = nil
session[:previous_url] = nil
previous_url
else
#if there is not temp attendee in the session proceed as normal
super
end
end
events_controller.rb
def show
session[:previous_url] = request.fullpath
end
routes.rb
devise_for :users, controllers: { sessions: "users/sessions" }
resources :events do
post :attendee, to: 'attendees#create', on: :member
post :unattend, to: 'attendees#destroy', on: :member
end
In Views, views\events\show.html.erb
<%= button_to 'Attend Now', attendee_event_path(@event), class:"btn btn-success btn-lg" %>
Found the solution !
There is actually a gem called repost
, that helps you redirect to a POST action. I implemented it in my solution.
Here is the whole technique I applied. To redirect after sign-in and then complete the POST action when the login was successful:
First, I create a session session[:previous_url]
for later on to be used to redirect back here.
def show
session[:previous_url] = request.fullpath #url for user to come back after signing in
...
attendee_controller.rb : we create session[:attendee]
def create
if !current_user
session[:attendee] = true
redirect_to user_session_path
else
...
app\controllers\users\sessions_controller.rb (user_session_path)
protected
def after_sign_in_path_for(resource_or_scope)
if session[:attendee]
previous_url = session[:previous_url]
session[:previous_url] = nil #clear session
previous_url #going back to event page
else
super
end
end
More info into after_sign_in_path_for
method here in the Devise How-To Wiki.
previous_url
will bring us back to show
view from the events.
app\controllers\events_controller.rb
before_action :attendee?, only: :show
private
def attendee?
if session[:attendee]
session[:attendee] = nil
repost(attendee_event_path, options: {authenticity_token: :auto})
end
end
This is the gem template :
repost('url' , params: {}, options: {})
Here is the gem link: https://github.com/vergilet/repost