Rails 3: обновление пользовательских атрибутов при создании аутентификации

Я следил за Railscasts #235 и #236, чтобы настроить создание аутентификации пользователей с помощью omniauth. http://railscasts.com/episodes/235-omniauth-part-1 http://railscasts.com/episodes/236-omniauth-part-2

У меня есть 2 логических атрибута в пользовательской модели, которые называются :facebok_share и :twitter_share, и я хочу установить значение true при создании новой аутентификации.

У меня это работает, когда я создаю нового пользователя, но если существующий пользователь добавляет аутентификацию, я не могу заставить логическое значение обновиться до true.

Когда вызывается apply_omniauth(omniauth), он устанавливает self.facebook_share = true или self.twitter_share = true в моей пользовательской модели.

Я попытался добавить новый метод с именем apply_share, который изменяет логические значения в зависимости от провайдера, и я пытаюсь вызвать current_user.apply_share(omniauth), но в базе данных ничего не происходит.

Что я делаю неправильно? Спасибо!

## контроллер аутентификации

class AuthenticationsController < ApplicationController

  def index
    @title = "Authentications"
    @authentications = current_user.authentications if current_user
  end

  def create
    # creates omniauth hash and looks for an previously established authentication
    omniauth = request.env["omniauth.auth"]
    authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
    # if previous authentication found, sign in user
    if authentication
      flash[:notice] = "Signed in successfully"
      sign_in_and_redirect(:user, authentication.user)
    #  for users already signed in (current_user), create a new authentication for the user
    elsif current_user
      current_user.apply_share(omniauth)
      current_user.authentications.create(:provider => omniauth['provider'], :uid => omniauth['uid'], :token => (omniauth['credentials']['token'] rescue nil),
                                           :secret => (omniauth['credentials']['secret'] rescue nil))
      flash[:notice] = "authentications successful"
      redirect_to authentications_url
    # new user is created and authentications are built through apply_omniauth(omniauth)
    else
      user = User.new
      user.apply_omniauth(omniauth)
      if user.save
        flash[:notice] = "Signed in successfully"
        sign_in_and_redirect(:user, user)
      # if validations fail to save user, redirects to new user registration page 
      # new twitter authentications redirect so user can enter their password
      else
        session[:omniauth] = omniauth
        redirect_to new_user_registration_url
      end
     end
   end

  def destroy
    @authentication = current_user.authentications.find(params[:id])
    @authentication.destroy
    flash[:notice] = "Successfully destroyed authentication."
    redirect_to authentications_url
  end

end

## user model

 # set share booleans to true depending on 'provider' type
  def apply_share(omniauth)
    case omniauth['provider']
      when 'facebook'
        self.facebook_share = true
      when 'twitter'
        self.twitter_share = true
     end
   end

 # from authentications controller, new user split into type of provider
 def apply_omniauth(omniauth)
   case omniauth['provider']
   when 'facebook'
     self.apply_facebook(omniauth)
   when 'twitter'
     self.apply_twitter(omniauth)
   end
   # builds authentication with provider, uid, token, and secret
   authentications.build(hash_from_omniauth(omniauth))
  end

 protected

 # sets new user attributes from facebook
 def apply_facebook(omniauth)
   self.name = omniauth['user_info']['name']
   self.email = omniauth['user_info']['email'] if email.blank?
   self.facebook_share = true
 end

 # sets new user attributes from twitter 
 def apply_twitter(omniauth)
   if (extra = omniauth['extra']['user_hash'] rescue false)
     # Example fetching extra data. Needs migration to User model:
     # self.firstname = (extra['name'] rescue '')
     self.name = (extra['name'] rescue '')
     self.bio = (extra['description'] rescue '') 
   end
   self.twitter_share = true

 end

 # set authentication attributes to those from 'omniauth' hash
 def hash_from_omniauth(omniauth)
   {
     :provider => omniauth['provider'],
     :uid => omniauth['uid'],
     :token => (omniauth['credentials']['token'] rescue nil),
     :secret => (omniauth['credentials']['secret'] rescue nil)
   }
 end
end


## new methid with :before add => :apply_share
def apply_share(authentication) 
  case authentication['provider'] 
    when 'facebook' 
      self.facebook_share = true 
    when 'twitter'
      self.twitter_share = true 
    end 
  self.save
end

person trying_hal9000    schedule 25.06.2011    source источник


Ответы (2)


Я считаю, что вы никогда не сохраняли current_user. Итак, вы устанавливаете для своих атрибутов значение true, а затем перенаправляете. Ассоциация хранится в модели аутентификации, поэтому Rails, пытаясь быть полезным, не обновляет current_user, а только новый экземпляр аутентификации.

пытаться:

current_user.apply_share(omniauth)
current_user.save

и посмотрите, исправит ли это это. Теперь, если это произойдет, я настоятельно рекомендую вместо этого использовать обратный вызов. Посмотрите здесь:

http://guides.rubyonrails.org/association_basics.html

Раздел 4.5 об обратных вызовах ассоциации. Вы можете выполнить обратный вызов before_add в вашей ассоциации аутентификации has_many, чтобы удалить этот код из вашего контроллера, поскольку он и так довольно раздут.

   class User < ActiveRecord::Base
     has_many :authentications, :before_add => :apply_share

     def apply_share(authentication)
      #update attributes
      #save model
     end
   end
person Kelend    schedule 25.06.2011
comment
Вызов current_user.save действительно работает, но я попытался настроить вашу идею с помощью вызова before_add, и я не могу заставить его работать. Мне нужно различать, является ли поставщик аутентификации «facebook» или «twitter», поэтому я попробовал это, но это не работает, есть идеи? (также вставка выше с синтаксисом) def apply_share(authentication) case authentication['provider'] when 'facebook' self.facebook_share = true when 'twitter' self.twitter_share = true end self.save end - person trying_hal9000; 25.06.2011
comment
попробуйте использовать authentication.provider вместо authentication['provider'] - person Kelend; 26.06.2011

Вам нужно вызвать #save для объекта User после установки атрибутов *_share.

Добавление новых элементов в коллекцию has_many автоматически сохраняет элемент коллекции, но не запускает операцию сохранения родительского элемента (belongs_to).

person krohrbaugh    schedule 25.06.2011