Search code examples
ruby-on-railsrubytwitteromniauth-twitter

Rails: Twitter Omniauth - update user attributes (user.image)


I use the gem 'omniauth-twitter' to let my user login to my app. Then I can use their name and profile image from twitter on my app.

The problem I am facing is that when a user update its name or image on twitter, it doesn't update on my app.

Here is my config:

app/controllers/omniauth_callbacks_controller.rb

class OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def all
    user = User.from_omniauth(request.env["omniauth.auth"])

    if user.persisted?
      flash.notice = "Signed in!"
      sign_in_and_redirect user     
    else
      session["devise.user_attributes"] = user.attributes
      redirect_to new_user_registration_url
    end

  end

  alias_method :twitter, :all
end

app/models/user.rb

  def self.from_omniauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
      user.provider = auth.provider
      user.uid = auth.uid
      user.name = auth.info.nickname
      user.image = auth["info"]["image"].sub("_normal", "")
    end
  end

  def self.new_with_session(params, session)
    if session["devise.user_attributes"]
      new(session["devise.user_attributes"]) do |user|
        user.attributes = params
        user.valid?
      end
    else
      super
    end
  end

end

I did not suceed to update user attributes. Any ideas following my configuration to do this ? thanks.


Solution

  • I believe you may be mistaken about the semantics of #first_or_create. The correct semantics is:

    A) try finding a record with the provided query scope (your where clause -> provider: auth.provider, uid: auth.uid)

    B.1) If found any, return the first one not executing the passed in block.

    B.2) If not found any, initialize a new one and pass it to the block passed as argument (which in your case is what sets the name and image)

    So, on the first login, the program flow follows A > B.2, which creates a new user with the correct name and image. On the successive logins, it follows A > B.1, which just return the user already on the database.

    You can solve this issue ensuring the code that sets name and picture always runs:

    def self.from_omniauth(auth)
      user = find_or_initialize_by(provider: auth.provider, uid: auth.uid)
      user.name  = auth.info.nickname
      user.image = auth["info"]["image"].sub("_normal", "")
    
      user.save
      user
    end
    

    Cheers!