Search code examples
ruby-on-railsrubydeviseruby-on-rails-6ruby-on-rails-6.1

Devise redirect after sign_in and perfom POST action


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" %>


Solution

  • 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.

    Here is where the gem will come save the day :

    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