<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>CHANGELOG.markdown</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -35,7 +35,8 @@ module TwitterAuth
       end
 
       def identify_or_create_from_twitter_hash_and_password(twitter_hash, password)
-        if user = User.find_by_login(twitter_hash['screen_name']) 
+        if user = User.find_by_twitter_id(twitter_hash['id'].to_s)
+          user.login = twitter_hash['screen_name']
           user.assign_twitter_attributes(twitter_hash)
           user.password = password
           user.save</diff>
      <filename>app/models/twitter_auth/basic_user.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 module TwitterAuth
   class GenericUser &lt; ActiveRecord::Base
-    attr_protected :login, :remember_token, :remember_token_expires_at
+    attr_protected :twitter_id, :remember_token, :remember_token_expires_at
     
     TWITTER_ATTRIBUTES = [
       :name,
@@ -24,10 +24,11 @@ module TwitterAuth
       :utc_offset
     ]
     
-    validates_presence_of :login
+    validates_presence_of :login, :twitter_id
     validates_format_of :login, :with =&gt; /\A[a-z0-9_]+\z/i
     validates_length_of :login, :in =&gt; 1..15
     validates_uniqueness_of :login, :case_sensitive =&gt; false
+    validates_uniqueness_of :twitter_id, :message =&gt; &quot;ID has already been taken.&quot;
     validates_uniqueness_of :remember_token, :allow_blank =&gt; true
     
     def self.table_name; 'users' end
@@ -35,7 +36,10 @@ module TwitterAuth
     def self.new_from_twitter_hash(hash)
       raise ArgumentError, 'Invalid hash: must include screen_name.' unless hash.key?('screen_name')
 
+      raise ArgumentError, 'Invalid hash: must include id.' unless hash.key?('id')
+
       user = User.new
+      user.twitter_id = hash['id'].to_s
       user.login = hash['screen_name']
 
       TWITTER_ATTRIBUTES.each do |att|</diff>
      <filename>app/models/twitter_auth/generic_user.rb</filename>
    </modified>
    <modified>
      <diff>@@ -16,7 +16,8 @@ module TwitterAuth
         response = token.get(TwitterAuth.path_prefix + '/account/verify_credentials.json')
         user_info = handle_response(response)
         
-        if user = User.find_by_login(user_info['screen_name'])
+        if user = User.find_by_twitter_id(user_info['id'].to_s)
+          user.login = user_info['screen_name']
           user.assign_twitter_attributes(user_info)
           user.access_token = token.token
           user.access_secret = token.secret</diff>
      <filename>app/models/twitter_auth/oauth_user.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,7 @@
 class TwitterAuthMigration &lt; ActiveRecord::Migration
   def self.up
     create_table :users do |t|
+      t.string :twitter_id
       t.string :login
 &lt;% if options[:oauth] -%&gt;
       t.string :access_token</diff>
      <filename>generators/twitter_auth/templates/migration.rb</filename>
    </modified>
    <modified>
      <diff>@@ -81,7 +81,7 @@ describe SessionsController do
 
       describe 'with proper info' do
         before do
-          @user = Factory.create(:twitter_oauth_user)
+          @user = Factory.create(:twitter_oauth_user, :twitter_id =&gt; '123')
           @time = Time.now
           @remember_token = ActiveSupport::SecureRandom.hex(10)
           
@@ -177,7 +177,7 @@ describe SessionsController do
       
       describe '#create' do
         before do
-          @user = Factory.create(:twitter_basic_user)
+          @user = Factory.create(:twitter_basic_user, :twitter_id =&gt; '123')
         end
 
         it 'should call logout_keeping_session! to remove session info' do</diff>
      <filename>spec/controllers/sessions_controller_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,7 @@
 require 'factory_girl'
 
 Factory.define(:twitter_oauth_user, :class =&gt; User) do |u|
+  u.twitter_id { User.count + 1 }
   u.login 'twitterman'
   u.access_token 'fakeaccesstoken'
   u.access_secret 'fakeaccesstokensecret'
@@ -10,6 +11,7 @@ Factory.define(:twitter_oauth_user, :class =&gt; User) do |u|
 end
 
 Factory.define(:twitter_basic_user, :class =&gt; User) do |u|
+  u.twitter_id { User.count + 1 }
   u.login 'twitterman'
   u.password 'test'
 </diff>
      <filename>spec/fixtures/factories.rb</filename>
    </modified>
    <modified>
      <diff>@@ -13,6 +13,6 @@ FakeWeb.register_uri(:post, 'https://twitter.com:443/oauth/request_token', :stri
 
 FakeWeb.register_uri(:post, 'https://twitter.com:443/oauth/access_token', :string =&gt; 'oauth_token=fakeaccesstoken&amp;oauth_token_secret=fakeaccesstokensecret')
 
-FakeWeb.register_uri(:get, 'https://twitter.com:443/account/verify_credentials.json', :string =&gt; &quot;{\&quot;profile_image_url\&quot;:\&quot;http:\\/\\/static.twitter.com\\/images\\/default_profile_normal.png\&quot;,\&quot;description\&quot;:\&quot;Saving the world for all Twitter kind.\&quot;,\&quot;utc_offset\&quot;:null,\&quot;favourites_count\&quot;:0,\&quot;profile_sidebar_fill_color\&quot;:\&quot;e0ff92\&quot;,\&quot;screen_name\&quot;:\&quot;twitterman\&quot;,\&quot;statuses_count\&quot;:0,\&quot;profile_background_tile\&quot;:false,\&quot;profile_sidebar_border_color\&quot;:\&quot;87bc44\&quot;,\&quot;friends_count\&quot;:2,\&quot;url\&quot;:null,\&quot;name\&quot;:\&quot;Twitter Man\&quot;,\&quot;time_zone\&quot;:null,\&quot;protected\&quot;:false,\&quot;profile_background_image_url\&quot;:\&quot;http:\\/\\/static.twitter.com\\/images\\/themes\\/theme1\\/bg.gif\&quot;,\&quot;profile_background_color\&quot;:\&quot;9ae4e8\&quot;,\&quot;created_at\&quot;:\&quot;Fri Feb 06 18:10:32 +0000 2009\&quot;,\&quot;profile_text_color\&quot;:\&quot;000000\&quot;,\&quot;followers_count\&quot;:2,\&quot;location\&quot;:null,\&quot;id\&quot;:20256865,\&quot;profile_link_color\&quot;:\&quot;0000ff\&quot;}&quot;)
+FakeWeb.register_uri(:get, 'https://twitter.com:443/account/verify_credentials.json', :string =&gt; &quot;{\&quot;profile_image_url\&quot;:\&quot;http:\\/\\/static.twitter.com\\/images\\/default_profile_normal.png\&quot;,\&quot;description\&quot;:\&quot;Saving the world for all Twitter kind.\&quot;,\&quot;utc_offset\&quot;:null,\&quot;favourites_count\&quot;:0,\&quot;profile_sidebar_fill_color\&quot;:\&quot;e0ff92\&quot;,\&quot;screen_name\&quot;:\&quot;twitterman\&quot;,\&quot;statuses_count\&quot;:0,\&quot;profile_background_tile\&quot;:false,\&quot;profile_sidebar_border_color\&quot;:\&quot;87bc44\&quot;,\&quot;friends_count\&quot;:2,\&quot;url\&quot;:null,\&quot;name\&quot;:\&quot;Twitter Man\&quot;,\&quot;time_zone\&quot;:null,\&quot;protected\&quot;:false,\&quot;profile_background_image_url\&quot;:\&quot;http:\\/\\/static.twitter.com\\/images\\/themes\\/theme1\\/bg.gif\&quot;,\&quot;profile_background_color\&quot;:\&quot;9ae4e8\&quot;,\&quot;created_at\&quot;:\&quot;Fri Feb 06 18:10:32 +0000 2009\&quot;,\&quot;profile_text_color\&quot;:\&quot;000000\&quot;,\&quot;followers_count\&quot;:2,\&quot;location\&quot;:null,\&quot;id\&quot;:123,\&quot;profile_link_color\&quot;:\&quot;0000ff\&quot;}&quot;)
 
 #FakeWeb.register_uri(:get, 'https://twitter.com:443/)</diff>
      <filename>spec/fixtures/fakeweb.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,7 +7,7 @@ describe TwitterAuth::BasicUser do
 
   describe '#password=' do
     before do
-      @user = Factory.build(:twitter_basic_user)
+      @user = Factory.build(:twitter_basic_user, :twitter_id =&gt; '123')
     end
 
     it 'should change the value of crypted_password' do
@@ -58,7 +58,7 @@ describe TwitterAuth::BasicUser do
 
   describe '.authenticate' do
     before do
-      @user = Factory.create(:twitter_basic_user)
+      @user = Factory.create(:twitter_basic_user, :twitter_id =&gt; '123')
     end
 
     it 'should make a call to verify_credentials' do
@@ -72,34 +72,35 @@ describe TwitterAuth::BasicUser do
     end
 
     it 'should return the user if verify_credentials succeeds' do
-      User.stub!(:verify_credentials).and_return(JSON.parse(&quot;{\&quot;profile_image_url\&quot;:\&quot;http:\\/\\/static.twitter.com\\/images\\/default_profile_normal.png\&quot;,\&quot;description\&quot;:\&quot;Saving the world for all Twitter kind.\&quot;,\&quot;utc_offset\&quot;:null,\&quot;favourites_count\&quot;:0,\&quot;profile_sidebar_fill_color\&quot;:\&quot;e0ff92\&quot;,\&quot;screen_name\&quot;:\&quot;twitterman\&quot;,\&quot;statuses_count\&quot;:0,\&quot;profile_background_tile\&quot;:false,\&quot;profile_sidebar_border_color\&quot;:\&quot;87bc44\&quot;,\&quot;friends_count\&quot;:2,\&quot;url\&quot;:null,\&quot;name\&quot;:\&quot;Twitter Man\&quot;,\&quot;time_zone\&quot;:null,\&quot;protected\&quot;:false,\&quot;profile_background_image_url\&quot;:\&quot;http:\\/\\/static.twitter.com\\/images\\/themes\\/theme1\\/bg.gif\&quot;,\&quot;profile_background_color\&quot;:\&quot;9ae4e8\&quot;,\&quot;created_at\&quot;:\&quot;Fri Feb 06 18:10:32 +0000 2009\&quot;,\&quot;profile_text_color\&quot;:\&quot;000000\&quot;,\&quot;followers_count\&quot;:2,\&quot;location\&quot;:null,\&quot;id\&quot;:20256865,\&quot;profile_link_color\&quot;:\&quot;0000ff\&quot;}&quot;))
+      User.stub!(:verify_credentials).and_return(JSON.parse(&quot;{\&quot;profile_image_url\&quot;:\&quot;http:\\/\\/static.twitter.com\\/images\\/default_profile_normal.png\&quot;,\&quot;description\&quot;:\&quot;Saving the world for all Twitter kind.\&quot;,\&quot;utc_offset\&quot;:null,\&quot;favourites_count\&quot;:0,\&quot;profile_sidebar_fill_color\&quot;:\&quot;e0ff92\&quot;,\&quot;screen_name\&quot;:\&quot;twitterman\&quot;,\&quot;statuses_count\&quot;:0,\&quot;profile_background_tile\&quot;:false,\&quot;profile_sidebar_border_color\&quot;:\&quot;87bc44\&quot;,\&quot;friends_count\&quot;:2,\&quot;url\&quot;:null,\&quot;name\&quot;:\&quot;Twitter Man\&quot;,\&quot;time_zone\&quot;:null,\&quot;protected\&quot;:false,\&quot;profile_background_image_url\&quot;:\&quot;http:\\/\\/static.twitter.com\\/images\\/themes\\/theme1\\/bg.gif\&quot;,\&quot;profile_background_color\&quot;:\&quot;9ae4e8\&quot;,\&quot;created_at\&quot;:\&quot;Fri Feb 06 18:10:32 +0000 2009\&quot;,\&quot;profile_text_color\&quot;:\&quot;000000\&quot;,\&quot;followers_count\&quot;:2,\&quot;location\&quot;:null,\&quot;id\&quot;:123,\&quot;profile_link_color\&quot;:\&quot;0000ff\&quot;}&quot;))
       User.authenticate('twitterman','test').should == @user
     end
   end
 
   describe '.find_or_create_by_twitter_hash_and_password' do
     before do
-      @user = Factory.create(:twitter_basic_user)
+      @user = Factory.create(:twitter_basic_user, :twitter_id =&gt; '123')
     end
 
     it 'should return the existing user if there is one' do
-      User.identify_or_create_from_twitter_hash_and_password({'screen_name' =&gt; 'twitterman'},'test').should == @user
+      User.identify_or_create_from_twitter_hash_and_password({'id' =&gt; '123', 'screen_name' =&gt; 'twitterman'},'test').should == @user
     end
 
     it 'should update the attributes from the hash' do
-      User.identify_or_create_from_twitter_hash_and_password({'screen_name' =&gt; 'twitterman', 'name' =&gt; 'New Name'}, 'test').name.should == 'New Name'
+      User.identify_or_create_from_twitter_hash_and_password({'id' =&gt; 123, 'screen_name' =&gt; 'twitterman', 'name' =&gt; 'New Name'}, 'test').name.should == 'New Name'
     end
 
     it 'should update the password from the argument' do
-      User.identify_or_create_from_twitter_hash_and_password({'screen_name' =&gt; 'twitterman', 'name' =&gt; 'New Name'}, 'test2').password.should == 'test2'
+      User.identify_or_create_from_twitter_hash_and_password({'id' =&gt; '123', 'screen_name' =&gt; 'twitterman', 'name' =&gt; 'New Name'}, 'test2').password.should == 'test2'
     end
 
     it 'should create a user if one does not exist' do
-      lambda{User.identify_or_create_from_twitter_hash_and_password({'screen_name' =&gt; 'dude', 'name' =&gt; &quot;Lebowski&quot;}, 'test')}.should change(User, :count).by(1)
+      lambda{User.identify_or_create_from_twitter_hash_and_password({'id' =&gt; 124, 'screen_name' =&gt; 'dude', 'name' =&gt; &quot;Lebowski&quot;}, 'test')}.should change(User, :count).by(1)
     end
 
     it 'should assign the attributes from the hash to a created user' do
-      user = User.identify_or_create_from_twitter_hash_and_password({'screen_name' =&gt; 'dude', 'name' =&gt; &quot;Lebowski&quot;}, 'test')
+      user = User.identify_or_create_from_twitter_hash_and_password({'id' =&gt; 124, 'screen_name' =&gt; 'dude', 'name' =&gt; &quot;Lebowski&quot;}, 'test')
+      user.twitter_id.should == '124'
       user.login.should == 'dude'
       user.name.should == 'Lebowski'
       user.password.should == 'test'
@@ -108,7 +109,7 @@ describe TwitterAuth::BasicUser do
 
   describe '#twitter' do
     before do
-      @user = Factory.create(:twitter_basic_user)
+      @user = Factory.create(:twitter_basic_user, :twitter_id =&gt; '123')
     end
 
     it 'should be an instance of TwitterAuth::Dispatcher::Basic' do
@@ -119,4 +120,19 @@ describe TwitterAuth::BasicUser do
       @user.twitter.user.should == @user
     end
   end
+
+  describe 'changing usernames' do
+    before do
+      @user = Factory.create(:twitter_basic_user, :twitter_id =&gt; '123')
+    end
+
+    it 'should not create a new record when a screen_name has changed' do
+      lambda{User.identify_or_create_from_twitter_hash_and_password({'id' =&gt; '123', 'screen_name' =&gt; 'dude'},'awesome')}.should_not change(User,:count)
+    end
+
+    it 'should update the record with the new screen name' do
+      User.identify_or_create_from_twitter_hash_and_password({'id' =&gt; '123', 'screen_name' =&gt; 'dude'},'awesome').should == @user.reload
+      @user.login.should == 'dude'
+    end
+  end
 end</diff>
      <filename>spec/models/twitter_auth/basic_user_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,7 @@
 require File.dirname(__FILE__) + '/../../spec_helper'
 
 describe TwitterAuth::GenericUser do
-  should_validate_presence_of :login
+  should_validate_presence_of :login, :twitter_id
   should_validate_format_of :login, 'some_guy', 'awesome', 'cool_man'
   should_not_validate_format_of :login, 'with-dashes', 'with.periods', 'with spaces'
   should_validate_length_of :login, :in =&gt; 1..15
@@ -27,19 +27,23 @@ describe TwitterAuth::GenericUser do
 
   describe '.new_from_twitter_hash' do
     it 'should raise an argument error if the hash does not have a screen_name attribute' do
-      lambda{User.new_from_twitter_hash({})}.should raise_error(ArgumentError, 'Invalid hash: must include screen_name.')
+      lambda{User.new_from_twitter_hash({'id' =&gt; '123'})}.should raise_error(ArgumentError, 'Invalid hash: must include screen_name.')
+    end
+
+    it 'should raise an argument error if the hash does not have an id attribute' do
+      lambda{User.new_from_twitter_hash({'screen_name' =&gt; 'abc123'})}.should raise_error(ArgumentError, 'Invalid hash: must include id.')
     end
 
     it 'should return a user' do
-      User.new_from_twitter_hash({'screen_name' =&gt; 'twitterman'}).should be_a(User)
+      User.new_from_twitter_hash({'id' =&gt; '123', 'screen_name' =&gt; 'twitterman'}).should be_a(User)
     end
 
     it 'should assign login to the screen_name' do
-      User.new_from_twitter_hash({'screen_name' =&gt; 'twitterman'}).login.should == 'twitterman'
+      User.new_from_twitter_hash({'id' =&gt; '123', 'screen_name' =&gt; 'twitterman'}).login.should == 'twitterman'
     end
 
     it 'should assign twitter attributes that are provided' do
-      u = User.new_from_twitter_hash({'screen_name' =&gt; 'twitterman', 'name' =&gt; 'Twitter Man', 'description' =&gt; 'Saving the world for all Tweet kind.'})
+      u = User.new_from_twitter_hash({'id' =&gt; '4566', 'screen_name' =&gt; 'twitterman', 'name' =&gt; 'Twitter Man', 'description' =&gt; 'Saving the world for all Tweet kind.'})
       u.name.should == 'Twitter Man'
       u.description.should == 'Saving the world for all Tweet kind.'
     end</diff>
      <filename>spec/models/twitter_auth/generic_user_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -14,6 +14,12 @@ describe TwitterAuth::OauthUser do
       lambda{ User.identify_or_create_from_access_token(@token) }.should_not raise_error(ArgumentError)
     end
 
+    it 'should change the login when the screen_name changes' do
+      @user = Factory(:twitter_oauth_user, :twitter_id =&gt; '123')
+      User.stub!(:handle_response).and_return({'id' =&gt; 123, 'screen_name' =&gt; 'dude'})
+      User.identify_or_create_from_access_token(@token).should == @user.reload
+    end
+
     it 'should accept two strings' do
       lambda{ User.identify_or_create_from_access_token('faketoken', 'fakesecret') }.should_not raise_error(ArgumentError)
     end
@@ -27,19 +33,19 @@ describe TwitterAuth::OauthUser do
       User.identify_or_create_from_access_token(@token)
     end
 
-    it 'should try to find the user with that login' do
-      User.should_receive(:find_by_login).once.with('twitterman')
+    it 'should try to find the user with that id' do
+      User.should_receive(:find_by_twitter_id).once.with('123')
       User.identify_or_create_from_access_token(@token)
     end
 
     it 'should return the user if he/she exists' do
-      user = Factory.create(:twitter_oauth_user, :login =&gt; 'twitterman')
+      user = Factory.create(:twitter_oauth_user, :twitter_id =&gt; '123', :login =&gt; 'twitterman')
       user.reload
       User.identify_or_create_from_access_token(@token).should == user
     end
 
     it 'should update the access_token and access_secret for the user if he/she exists' do
-      user = Factory.create(:twitter_oauth_user, :login =&gt; 'twitterman', :access_token =&gt; 'someothertoken', :access_secret =&gt; 'someothersecret')
+      user = Factory.create(:twitter_oauth_user, :twitter_id =&gt; '123', :login =&gt; 'twitterman', :access_token =&gt; 'someothertoken', :access_secret =&gt; 'someothersecret')
       User.identify_or_create_from_access_token(@token)
       user.reload
       user.access_token.should == @token.token</diff>
      <filename>spec/models/twitter_auth/oauth_user_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,6 @@
 ActiveRecord::Schema.define :version =&gt; 0 do
   create_table :twitter_auth_users, :force =&gt; true do |t|
+    t.string :twitter_id
     t.string :login
     
     # OAuth fields</diff>
      <filename>spec/schema.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>e9f01feabce8922f3e9ca5567e4ba6225f4e9bff</id>
    </parent>
  </parents>
  <author>
    <name>Michael Bleigh</name>
    <email>michael@intridea.com</email>
  </author>
  <url>http://github.com/mbleigh/twitter-auth/commit/43738b56ba53e75c89832df0c75fd20a3152dfca</url>
  <id>43738b56ba53e75c89832df0c75fd20a3152dfca</id>
  <committed-date>2009-04-22T21:19:28-07:00</committed-date>
  <authored-date>2009-04-22T21:19:28-07:00</authored-date>
  <message>Closes GH-5 - Now uses Twitter ID as the lookup mechanism for OAuth and HTTP Basic. Requires twitter_id string column in the users table.</message>
  <tree>54a7a25dd9d4f6f6657bb3eb2bdfcee8e9f60ee5</tree>
  <committer>
    <name>Michael Bleigh</name>
    <email>michael@intridea.com</email>
  </committer>
</commit>
