Alex
Alex

Reputation: 25

Rails: Duplicate functionality across controllers? A humble plea

So I'm working with authlogic, and I'm trying to duplicate the login functionality to the welcome page, so that you can log in by restful url or by just going to the main page. No, I don't know if we'll keep that feature, but I want to test it out anyway. Here's the error message:

RuntimeError in Welcome#index Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id

The code is below. Basically, what's happening is the index view (the first code snippet) is sending the information from the form to the create method of user_sessions controller. At this point, in theory, it create should just pick up, but it doesn't.

PLEASE help. Please. I've been doing this for about 8 hours. I checked Google. I checked IRC. I checked every book I could find. You don't even have to answer, I can to the grunt work if you just point me in the right direction.

===EDIT EDIT EDIT===

Sameera was good enough to provide the answer to the problem. Open question, though is what the best way to organize the app is. Is applying the object @user_sessions in a before_filter acceptable, or is there a more rails-y way of doing this?

WELCOME#INDEX

<% form_for @user_session, :url => user_sessions_path do |f| %>
    <%= f.text_field :email %><br />
<%= f.password_field :password %>
<%= submit_tag 'Login' %>
<% end %>

APPLICATION CONTROLLER

class ApplicationController < ActionController::Base
  helper :all # include all helpers, all the time
  protect_from_forgery # See ActionController::RequestForgeryProtection for details
  # Scrub sensitive parameters from your log
  # filter_parameter_logging :password
    helper_method :current_user_session, :current_user
    before_filter :new_session_object
protected
    def new_session_object
        unless current_user
        @user_session = UserSession.new(params[:user_session])
    end
end
private
def current_user_session
    return @current_user_session if defined?(@current_user_session)
    @current_user_session = UserSession.find
end
def current_user
    return @current_user if defined?(@current_user)
    @current_user = current_user_session && current_user_session.record
end
end<pre></code>

USER SESSIONS CONTROLLER

class UserSessionsController < ApplicationController
  def new
    @user_session = UserSession.new
  end

  def create
    @user_session = UserSession.new(params[:user_session])
    if @user_session.save
      flash[:notice] = "Logged in"
      redirect_to root_url
    else
      render :controller => 'user_sessions', :action => 'new'
    end
  end

  def destroy
    @user_session = UserSession.find
    @user_session.destroy
    flash[:notice] = "Logged out"
    redirect_to root_url
  end
end

A more detailed STACK TRACE

1: <h1>Welcome#index</h1>
2: <p>Find me in app/views/welcome/index.html.erb</p>
3: 
4: <% form_for @user_session, :url => user_sessions_path do |f| %>
5:  <%= f.text_field :email %><br />
6:      <%= f.password_field :password %>
7:  <%= submit_tag 'Login' %>

Application Trace | Framework Trace | Full Trace
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/actionpac -2.3.5/lib/action_controller/record_identifier.rb:76:in `dom_id'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/actionpack-2.3.5/lib/action_view/helpers/record_identification_helper.rb:16:in `dom_id'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/actionpack-2.3.5/lib/action_view/helpers/form_helper.rb:293:in `apply_form_for_options!'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/actionpack-2.3.5/lib/action_view/helpers/form_helper.rb:277:in `form_for'
/Users/alex/Desktop/anglic/app/views/welcome/index.html.erb:4:in `_run_erb_app47views47welcome47index46html46erb'

Upvotes: 1

Views: 517

Answers (3)

sameera207
sameera207

Reputation: 16619

Try this:

form_for :user_session, @user_session, :url => user_session_path(@user_session)} do |f| 

end

Upvotes: 0

Samuel Chandra
Samuel Chandra

Reputation: 1185

I think you can try this in your WelcomeController

class WelcomeController < ActionController::Base

  def index
    @user_session = UserSession.new
    .....
  end

  ..... 

end

It seems to me that you are trying to use @user_session which is not defined in the view of WelcomeController index action.

Hopefully it helps.

EDIT #1

It seems to me that the problem occur when the before_filter was called, after you submit the form in the WelcomeController#index action.

This particular section of code is strange to me

class ApplicationController < ActionController::Base

  .....
  private
  def current_user_session
    return @current_user_session if defined?(@current_user_session)
    @current_user_session = UserSession.find
  end
  .....

end

I am quite sure UserSession.find will break.

Hopefully this edit helps.

EDIT #2

Another thing that I found strange is inside the protected method new_session_object:

protected
    def new_session_object
        unless current_user
        @user_session = UserSession.new(params[:user_session])
    end
end

Any reason why the UserSession.new was created with params[:user_session] ?

The flow for your UserSessionController login as far as I understand it is:

  1. before_filter :new_session_object is called

  2. it will return false for current_user since it is a new user session

  3. it will set @user_session variable to UserSession.new(params[:user_session]) value

  4. UserSessionController#new action is called which override @user_session variable to UserSession.new

  5. new.html.erb is rendered containing the form for login

  6. before_filter :new_session_object is called again

  7. it will return false for current_user since it is still a new user session

  8. it will set @user_session variable to UserSession.new(params[:user_session]) value

  9. UserSessionController#create action is called which will again set the @user_session variable to UserSession.new(params[:user_session]) value.

  10. @user_session is saved and the user session is successfully created

BUT for the WelcomeController it is subtly different in step 4

The @user_session variable value that is used in the WelcomeController#index came from the before_filter call variable assignment of UserSession.new(params[:user_session]), at that point the params[:user_session] value is nil, so the @user_session = UserSession.new(nil) when the index.html.erb was rendered.

I am not entirely sure if this is the thing that cause your problem, but it may help you to troubleshoot this issue if you run script/server --debugger and step trough the creation of @user_session when using WelcomeController, specially when the form is posted to UserSessionController#create.

Upvotes: 0

Syed Aslam
Syed Aslam

Reputation: 8807

You should add the following method to the ApplicationController:

def require_user
    unless current_user
        flash[:notice] = "You must be logged in to access this page!"
        redirect_to :controller => "user_sessions", :action => "new"            
        return false
    end
end

Then in the WelcomeController add a before filter like this:

before_filter :require_user 

Now, anybody accessing WelcomeController will be redirected to the login page. You don't have to duplicate the functionality across controllers.

Please go through the Authlogic set-up tutorial for details: http://rdoc.info/projects/binarylogic/authlogic

Upvotes: 1

Related Questions