2016-01-08 22 views
8

Próbuję utworzyć aplikację w Rails 4. Próbowałem przez ostatnie 3 lata (z wyjątkiem 10 dni), aby przygotować się do pracy.Szyny 4 - Wymyśl Omniautha (wiele strategii)

Staram się śledzić ten tutorial: http://sourcey.com/rails-4-omniauth-using-devise-with-twitter-facebook-and-linkedin/

Proszę Niepolecane inną dokumentację tutoriale/GEM. Próbowałem co najmniej 30 innych samouczków, a dokumentacja z klejnotami zawiera mnóstwo błędów i składników, których nie rozumiem.

Mój obecny problem polega na tym, że po przejściu do etapu rejestracji w tym samouczku formularz poprosi mnie o podanie mojego adresu e-mail.

Kontroler użytkowników ma mety metody zapisywania jako:

def finish_signup 
    # authorize! :update, @user 
    if request.patch? && params[:user] #&& params[:user][:email] 
     if @user.update(user_params) 
     @user.skip_reconfirmation! 
     # sign_in(@user, :bypass => true) 
     # redirect_to root_path, notice: 'Your profile was successfully updated.' 
     # redirect_to [@user, @user.profile || @user.build_profile] 
     sign_in_and_redirect(@user, :bypass => true) 
     else 
     @show_errors = true 
     end 
    end 
    end 

Kiedy próbuję to, otrzymuję ten błąd:

undefined method `match' for {:host=>"localhost", :port=>3000}:Hash 

punktów błędach w tej linii:

 <div class="intpol3"><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></div> 

Moje środowisko programistyczne jest skonfigurowane tak, aby zawierało wszystkie szczegóły konfiguracji dla mojego nadawcy wiadomości e-mail.

Kiedy próbuję ten sam krok w trybie produkcyjnym, otrzymuję ten błąd:

ActionView::Template::Error (No route matches {:action=>"show", :controller=>"profiles", :id=>nil} missing required keys: [:id]): 

on szuka identyfikatora profilu bo mam after_create działania w moim modelu użytkownika jako:

after_create :gen_profile 

    def gen_profile 
    Profile.create(user: self) # Associations must be defined correctly for this syntax, avoids using ID's directly. 
    # Profile.save 
    end 

Innym problemem związanym z tym samouczkiem jest to, że pola w tabeli tożsamości nie są wypełniane.

Chciałbym znaleźć kogoś, kto pomyślnie wdrożył ten samouczek lub może zobaczyć, jak to zrobić.

Mój kod to:

gemfile

gem 'devise', '3.4.1' 
gem 'devise_zxcvbn' 
gem 'omniauth' 
gem 'omniauth-oauth2', '1.3.1' 
gem 'omniauth-google-oauth2' 
gem 'omniauth-facebook' 
gem 'omniauth-twitter' 
gem 'omniauth-linkedin-oauth2' 
gem 'google-api-client', require: 'google/api_client' 

trasy

devise_for :users, #class_name: 'FormUser', 
      :controllers => { 
       :registrations => "users/registrations", 
       # :omniauth_callbacks => "users/authentications" 
       :omniauth_callbacks => 'users/omniauth_callbacks' 
      } 

    # get '/auth/:provider/callback' => 'users/authentications#create' 
    # get '/authentications/sign_out', :to => 'users/authentications#destroy' 

    # PER SOURCEY TUTORIAL ---------- 
    match '/users/:id/finish_signup' => 'users#finish_signup', via: [:get, :patch], :as => :finish_signup 

resources :users do 
    resources :profiles, only: [:new, :create] 
    end 

user.rb

klasa User < ActiveRecord :: Base

TEMP_EMAIL_PREFIX = '[email protected]' 
    TEMP_EMAIL_REGEX = /\[email protected]/ 

    devise :database_authenticatable, :registerable, 
     :recoverable, :rememberable, :trackable, 
      :confirmable, :lockable, 
     # :zxcvbnable, 
     :omniauthable, :omniauth_providers => [:facebook, :linkedin, :twitter, :google_oauth2 ] 





    # --------------- associations 

    has_many :authentications, :dependent => :delete_all 

    has_one :profile 

    has_many :identities 


    # --------------- scopes 

    # --------------- validations 

    # validates_presence_of :first_name, :last_name 
    validates_uniqueness_of :email 

    # per sourcey tutorial - how do i confirm email registrations are unique? 
    # this is generating an error about the options in the without function -- cant figure out the solution 
    validates_format_of :email, :without => TEMP_EMAIL_REGEX, on: :update 


    # --------------- class methods 


# sourcey tutorial 

def self.find_for_oauth(auth, signed_in_resource = nil) 
    # Get the identity and user if they exist 
    identity = Identity.find_for_oauth(auth) 

    # If a signed_in_resource is provided it always overrides the existing user 
    # to prevent the identity being locked with accidentally created accounts. 
    # Note that this may leave zombie accounts (with no associated identity) which 
    # can be cleaned up at a later date. 
    user = signed_in_resource ? signed_in_resource : identity.user 

    # p '11111' 

    # Create the user if needed 
    if user.nil? 
     # p 22222 
     # Get the existing user by email if the provider gives us a verified email. 
     # If no verified email was provided we assign a temporary email and ask the 
     # user to verify it on the next step via UsersController.finish_signup 
     email_is_verified = auth.info.email && (auth.info.verified || auth.info.verified_email) 
     email = auth.info.email if email_is_verified # take out this if stmt for chin yi's solution 
     user = User.where(:email => email).first if email 

     # Create the user if it's a new registration 
     if user.nil? 
     # p 33333 
     user = User.new(
      # at least one problem with this is that each provider uses different terms to desribe first name/last name/email. See notes on linkedin above 
      first_name: auth.info.first_name, 
      last_name: auth.info.last_name, 
      email: email ? email : "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com", 
      #username: auth.info.nickname || auth.uid, 
      password: Devise.friendly_token[0,20]) 
# fallback for name fields - add nickname to user table 
     # debugger 

     # if email_is_verified 
      user.skip_confirmation! 
     # end 
     # user.skip_confirmation! 

     user.save! 
     end 
    end 

    # Associate the identity with the user if needed 
    if identity.user != user 
     identity.user = user 
     identity.save! 
    end 
    user 
    end 

    def email_verified? 
    self.email && TEMP_EMAIL_REGEX !~ self.email 
    end 

użytkowników kontroler

class UsersController < ApplicationController 

before_action :set_user, only: [:index, :show, :edit, :update, :finish_signup, :destroy] 

# i added finish_signup to the set_user action (not shown in tutorial) 

    def index 
    # if params[:approved] == "false" 
    # @users = User.find_all_by_approved(false) 
    # else 
     @users = User.all 
    # end 

    end 

    # GET /users/:id.:format 
    def show 
    # authorize! :read, @user 
    end 

    # GET /users/:id/edit 
    def edit 
    # authorize! :update, @user 
    end 

    # PATCH/PUT /users/:id.:format 
    def update 
    # authorize! :update, @user 
    respond_to do |format| 
     if @user.update(user_params) 
     sign_in(@user == current_user ? @user : current_user, :bypass => true) 
     format.html { redirect_to @user, notice: 'Your profile was successfully updated.' } 
     format.json { head :no_content } 
     else 
     format.html { render action: 'edit' } 
     format.json { render json: @user.errors, status: :unprocessable_entity } 
     end 
    end 
    end 

    # GET/PATCH /users/:id/finish_signup 
    def finish_signup 
    # authorize! :update, @user 
    if request.patch? && params[:user] #&& params[:user][:email] 
     if @user.update(user_params) 
     @user.skip_reconfirmation! 
     # sign_in(@user, :bypass => true) 
     # redirect_to root_path, notice: 'Your profile was successfully updated.' 
     # redirect_to [@user, @user.profile || @user.build_profile] 
     sign_in_and_redirect(@user, :bypass => true) 
     else 
     @show_errors = true 
     end 
    end 
    end 

    # DELETE /users/:id.:format 
    def destroy 
    # authorize! :delete, @user 
    @user.destroy 
    respond_to do |format| 
     format.html { redirect_to root_url } 
     format.json { head :no_content } 
    end 
    end 

    private 
    def set_user 
     @user = User.find(params[:id]) 
    end 

    def user_params 
     # params.require(:user).permit(policy(@user).permitted_attributes) 
     accessible = [ :first_name, :last_name, :email, :avatar ] # extend with your own params 
     accessible << [ :password, :password_confirmation ] unless params[:user][:password].blank? 
     # accessible << [:approved] if user.admin 
     params.require(:user).permit(accessible) 
    end 

end 

omniauth callbacks Kontroler

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController 

    def self.provides_callback_for(provider) 
    class_eval %Q{ 
     def #{provider} 
     @user = User.find_for_oauth(env["omniauth.auth"], current_user) 

     if @user.persisted? 
      sign_in_and_redirect @user, event: :authentication 

      set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format? 
     else 
      session["devise.#{provider}_data"] = env["omniauth.auth"] 
      redirect_to new_user_registration_url 
     end 
     end 
    } 
    end 

    # , current_user has been deleted from the end of line 51 
    #come back to put current_user into fidn by oauth so i can link other accounts - i have added this back for the purpose of solving the current problem 

      # puts current_user.inspect 
      # sign_in_and_redirect [@user, @user.profile || @user.build_profile] 

      # sign_in_and_redirect_user(:user, event: :authentication) 


    [:twitter, :facebook, :linkedin, :google_oauth2].each do |provider| 
    provides_callback_for provider 
    end 

    def after_sign_in_path_for(resource) 
    if resource.email_verified? 
     super resource 
    else 
     finish_signup_path(resource) 
    end 
    end 

end 

rejestracje

class Users::RegistrationsController < Devise::RegistrationsController 




     protected 

     def after_sign_up_path_for(resource) 
     profile_path(resource) 
    end 


    private 
    def user_params 
      params.require(:user).permit(:first_name, :last_name, :email, :password) 
    end 

    end 

tożsamość.rb

kontroler
class Identity < ActiveRecord::Base 


    belongs_to :user 
    validates_presence_of :uid, :provider 
    validates_uniqueness_of :uid, :scope => :provider 



    def self.find_for_oauth(auth) 
    find_or_create_by(uid: auth.uid, provider: auth.provider) 
    end 


end 

Tożsamości

class IdentitiesController < ApplicationController 
    before_action :set_identity, only: [:show, :edit, :update, :destroy] 
    before_action :authenticate_user! 

    # GET /identities 
    # GET /identities.json 
    def index 
    @identities = Identity.all 
    end 

    # GET /identities/1 
    # GET /identities/1.json 
    def show 
    end 

    # GET /identities/new 
    def new 
    @identity = Identity.new 
    end 

    # GET /identities/1/edit 
    def edit 
    end 

    # POST /identities 
    # POST /identities.json 
    def create 
    @identity = Identity.new(identity_params) 

    respond_to do |format| 
     if @identity.save 
     format.html { redirect_to @identity, notice: 'Identity was successfully created.' } 
     format.json { render :show, status: :created, location: @identity } 
     else 
     format.html { render :new } 
     format.json { render json: @identity.errors, status: :unprocessable_entity } 
     end 
    end 
    end 

    # PATCH/PUT /identities/1 
    # PATCH/PUT /identities/1.json 
    def update 
    respond_to do |format| 
     if @identity.update(identity_params) 
     format.html { redirect_to @identity, notice: 'Identity was successfully updated.' } 
     format.json { render :show, status: :ok, location: @identity } 
     else 
     format.html { render :edit } 
     format.json { render json: @identity.errors, status: :unprocessable_entity } 
     end 
    end 
    end 

    # DELETE /identities/1 
    # DELETE /identities/1.json 
    def destroy 
    @identity.destroy 
    respond_to do |format| 
     format.html { redirect_to identities_url, notice: 'Identity was successfully destroyed.' } 
     format.json { head :no_content } 
    end 
    end 

    private 
    # Use callbacks to share common setup or constraints between actions. 
    def set_identity 
     @identity = Identity.find(params[:id]) 
    end 

    # Never trust parameters from the scary internet, only allow the white list through. 
    def identity_params 
     params[:identity] 
    end 
end 

Devise Mailer - potwierdzenie

 <div class="intpol3"><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></div> 

STRESZCZENIE Aktualne problemy:

  1. W trybie rozwoju: Jest problem z link do confi token rmacyjny. Nie mogę znaleźć żadnych materiałów, które wskazywałyby, dlaczego tak się mogło stać. Błąd: (ActionView :: Szablon :: Błąd (niezdefiniowana metoda "dopasuj" dla {: host => "localhost",: port => 3000}: Hash):

  2. W trybie produkcyjnym istnieje błąd z użytkownikiem szukającym identyfikatora profilu. Komunikat o błędzie: ActionView :: Szablon :: Błąd (brak trasy odpowiada {: action => "show",: controller => "profiles",: id => nil} brakujące wymagane klucze [id]):

profilach trasy są:

resources :profiles, only: [:show, :edit, :update, :destroy] 
resources :users do 
    resources :profiles, only: [:new, :create] 
    end 
  1. Żaden z pola w model tożsamości jest wypełniany. Wszystkie są wyświetlane jako zero.

rzeczy zrobić inaczej niż jak pokazano w tutorialu:

  1. pozwalam też e-mail zapisz się

  2. dodam „finish_sign_up” do set_user zanim akcji w kontrolerze użytkowników

  3. Dodaję strategię g + (co oznacza, że ​​moje klejnoty są nieco inne)

  4. Moja nowa metoda użytkownika nie używa surowych informacji. Używa informacji przetworzonych oauth.

  5. Moje przekierowanie w metodzie rejestracji końcowej jest nieco inne, chociaż skomentowałem to i wróciłem do sposobu, w jaki zostało to określone w samouczku, aby spróbować działać (chociaż powyższe problemy się powtarzają).

Szaleję, próbując rozwiązać te problemy. Powiedziałbym, że 3 lata to o wiele za długo, aby utknąć w tym problemie. Jeśli ktokolwiek może pomóc, zapłaciłbym 10 razy, a potem kilka. Dziękuję Ci.

+0

Zrobiłem to z powodzeniem. Czy potrafisz zweryfikować z poziomu konsoli szyn, czy rzeczywiście tworzone są twoje profile? Czy masz bardziej szczegółowe pytanie? – errata

+0

Tak - profil jest tworzony dla użytkownika. Moje konkretne pytanie brzmi: jak mogę uzyskać tę konfigurację samouczka? Obecny błąd jest natychmiastowym wyzwaniem, więc jeśli istnieje sposób na rozwiązanie problemu, który może być przyczyną tego problemu, chciałbym zacząć od rozwiązania tego problemu. – Mel

+0

Hi Errata - czy dodałeś jakiś przycisk gdzieś w kodzie, aby pozwolić użytkownikom (raz w sesji) na dodanie innych tożsamości użytkowników? Gdzie to zrobiłeś? Myślę, że gdy wszystkie bieżące problemy zostaną rozwiązane, prawdopodobnie ma to sens w metodzie rejestracji końcowej. Jak sobie z tym poradziłeś? – Mel

Odpowiedz

1
<div class="intpol3"><%= link_to 'Confirm my account', 
    confirmation_url(@resource, confirmation_token: @token) %></div> 

Spróbuj resource zamiast @resource. AFAIK to tylko helper_method, a NIE zmienna instancji.

Myślę, że rozwiąże to Twój problem w pełni w produkcji. Ponieważ @resource to nie to samo co resource, nie zostało ono ustawione i zasadniczo wywołujesz confirmation_url(nil, confirmation_token: @token), a nil przechodzi do komunikatu o błędzie.

W opracowaniu pojawia się dodatkowy problem, który najprawdopodobniej ma związek ze skonfigurowaniem config.action_mailer.default_url_options w config/environments/development.rb i najprawdopodobniej powoduje zgłoszenie wyjątku w ActionDispatch::Http::Url.build_host_url. Podejrzewam, że masz coś takiego:

config.action_mailer.default_url_options[:host] = { host: 'localhost', port: 9000 } 

zmienić na:

config.action_mailer.default_url_options[:host] = 'localhost:9000' 

i sprawdzić, czy to rozwiązuje wszystko. Jeśli się mylę co do tego, jak skonfigurowano config.action_mailer.default_url_options, wklej swój numer config/environments/development.rb ORAZ cały ślad stosu z błędu programowania, abyśmy mogli Ci pomóc.