Monday, October 12, 2009

Add new user - Reading Tracks (4)

今日の課題

ユーザ登録はどのようにやっているのか?具体的には,


192.168.1.102 - - [08/Oct/2009:22:52:51 JST] "POST /users HTTP/1.1" 302 92
http://192.168.1.101:3000/users/new -> /users
ここで,どんな処理をしているのか?
早速,user_controller.rb の該当するコードを参照してみる。

  # Example usage: curl -H 'Accept: application/xml' -H 'Content-Type: application/xml'
  #               -u admin:up2n0g00d
  #               -d '<request><login>username</login><password>abc123</password></request>'
  #               http://our.tracks.host/users
  #
  # POST /users
  # POST /users.xml
  def create
    if params['exception']
      render_failure "Expected post format is valid xml like so: <request><login>username</login><password>abc123</password></request>."
      return
    end
    respond_to do |format|
      format.html do
        unless User.no_users_yet? || (@user && @user.is_admin?)
          @page_title = "No signups"
          @admin_email = User.find_admin.preference.admin_email
          render :action => "nosignup", :layout => "login"
          return
        end
        
        user = User.new(params['user'])
        unless user.valid?
          session['new_user'] = user
          redirect_to :action => 'new'
          return
        end

        first_user_signing_up = User.no_users_yet?
        user.is_admin = true if first_user_signing_up
        if user.save
          @user = User.authenticate(user.login, params['user']['password'])
          @user.create_preference
          @user.save
          session['user_id'] = @user.id if first_user_signing_up
          notify :notice, "Signup successful for user #{@user.login}."
          redirect_back_or_home
        end
        return
      end
      format.xml do
        unless User.find_by_id_and_is_admin(session['user_id'], true)
          render :text => "401 Unauthorized: Only admin users are allowed access to this function.", :status => 401
          return
        end
        unless check_create_user_params
          render_failure "Expected post format is valid xml like so: <request><login>username</login><password>abc123</password></request>."
          return
        end
        user = User.new(params[:request])
        user.password_confirmation = params[:request][:password]
        if user.save
          render :text => "User created.", :status => 200
        else
          render_failure user.errors.to_xml
        end
        return
      end
    end
  end  
まず最初の例外処理を見てみる。

    if params['exception']
      render_failure "Expected post format is valid xml like so: <request><login>username</login><password>abc123</password></request>."
      return
    end
想定外のXML文書が来たかどうかだと思う。想定外かどうか判断するには,モデルuserを参照するのだろう。validates_presence_of には,:login と :password があり,:login は 3文字以上80文字以下などなどと制限を設けている。そして失敗したら,render_failure で失敗理由を明示し,元に戻っている。
実際のテストコードでも,このような内容のテストが行なわれている。test/functional/users_controller_test.rb の以下のテストだ。

  def test_create_with_invalid_password_does_not_add_a_new_user
    login_as :admin_user
    assert_no_difference(User, :count) do
      post :create, :user => {:login => 'newbie', :password => '', :password_confirmation => ''}
    end
  end
  
  def test_create_with_invalid_password_redirects_to_new_user_page
    login_as :admin_user
    post :create, :user => {:login => 'newbie', :password => '', :password_confirmation => ''}
    assert_redirected_to :controller => 'users', :action => 'new'    
  end
  
  def test_create_with_invalid_login_does_not_add_a_new_user
    login_as :admin_user
    post :create, :user => {:login => 'n', :password => 'newbiepass', :password_confirmation => 'newbiepass'}
    assert_redirected_to :controller => 'users', :action => 'new'    
  end
  
  def test_create_with_invalid_login_redirects_to_new_user_page
    login_as :admin_user
    post :create, :user => {:login => 'n', :password => 'newbiepass', :password_confirmation => 'newbiepass'}
    assert_redirected_to :controller => 'users', :action => 'new'    
  end
なるほど,最初のテストではinvalidなpasswordを持つuserをUserに追加させていない。他の3つは,invalidな:login,:password を持つuserの作成に対するrequestに対して, /users/new に redirect されているかどうかをテストしている。(警告メッセージが出ているのかどうかテストするにはどうすればいいのだろうか。)
次の行からは,
respond_to do |format|
と始まっているように,responseを作成している箇所であることを確認して,ここまでとする。早速以下の処理内容を理解できないので,じっくりと把握していきたい。

        unless User.no_users_yet? || (@user && @user.is_admin?)
          @page_title = "No signups"
          @admin_email = User.find_admin.preference.admin_email
          render :action => "nosignup", :layout => "login"
          return
        end

No comments: