2016-01-08 17 views
8

Tôi đang cố gắng tạo một ứng dụng trong Rails 4. Tôi đã cố gắng trong 3 năm qua (ngoại trừ 10 ngày), để bắt đầu làm việc.Rails 4 - Devise Omniauth (nhiều chiến lược)

Tôi đang cố gắng để làm theo hướng dẫn này: http://sourcey.com/rails-4-omniauth-using-devise-with-twitter-facebook-and-linkedin/

Xin đừng khuyên tài liệu hướng dẫn/đá quý khác. Tôi đã thử ít nhất 30 hướng dẫn khác và tài liệu về đá quý có đầy đủ các lỗi và thành phần mà tôi không hiểu.

Vấn đề hiện tại của tôi là khi tôi đến bước đăng ký kết thúc trong hướng dẫn này, biểu mẫu yêu cầu tôi cung cấp địa chỉ email của tôi.

Bộ điều khiển người dùng có một phương thức đăng ký kết thúc như:

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 

Khi tôi cố gắng này, tôi nhận được lỗi này:

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

Những điểm lỗi tại dòng này:

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

Môi trường phát triển của tôi được thiết lập để bao gồm tất cả chi tiết cấu hình cho người gửi email của tôi.

Khi tôi thử các bước tương tự trong chế độ sản xuất, tôi nhận được lỗi này:

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

Nó tìm kiếm một hồ sơ id bởi vì tôi có một hành động after_create trong mô hình sử dụng của tôi là:

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 

Vấn đề khác của tôi với hướng dẫn này là các trường trong bảng nhận dạng không được phổ biến.

Tôi rất muốn tìm một người đã triển khai thành công hướng dẫn này hoặc có thể xem cách thực hiện công việc này.

Mã của tôi là:

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' 

tuyến

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

lớp người dùng < 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 

người dùng điều khiển

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 

callbacks omniauth khiển

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 

đăng ký điều khiển

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 

Identity.rb

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 

Identities khiển

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 bưu phẩm - Xác nhận

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

TÓM TẮT CÁC VẤN ĐỀ HIỆN:

  1. Trong chế độ phát triển: Có một vấn đề với liên kết đến confi mã thông báo rmation. Tôi không thể tìm thấy bất kỳ tài liệu nào cho biết lý do tại sao điều này có thể phát sinh. Lỗi này là: (ActionView :: Template :: Error (phương thức undefined `match 'cho {: host =>" localhost ",: port => 3000}: Hash):

  2. Trong chế độ sản xuất, có một Thông báo lỗi là: ActionView :: Template :: Error (Không khớp với đường dẫn {: action => "show",: controller => "profiles",: id => nil} thiếu các khóa cần thiết : [: id]):

hồ sơ của tôi đường bay:

resources :profiles, only: [:show, :edit, :update, :destroy] 
resources :users do 
    resources :profiles, only: [:new, :create] 
    end 
  1. Không ai trong số các trường trong mô hình nhận dạng được phổ biến. Tất cả chúng đều hiển thị là không.

Things Done khác biệt so với như trong Tutorial:

  1. Tôi cũng cho phép email đăng ký

  2. tôi thêm 'finish_sign_up' đến set_user trước khi hành động trong bộ điều khiển người dùng

  3. Tôi thêm chiến lược g + (có nghĩa là đá quý của tôi hơi khác nhau)

  4. Phương thức người dùng mới của tôi không sử dụng thông tin thô. Nó sử dụng thông tin được xử lý oauth.

  5. Chuyển hướng của tôi trong phương thức đăng ký kết thúc hơi khác một chút, mặc dù tôi đã nhận xét điều đó và quay lại cách thức được nêu trong hướng dẫn để làm việc này (mặc dù các vấn đề trên đang lặp lại).

Tôi đang điên cố gắng giải quyết những vấn đề này. Tôi muốn nói 3 năm là quá lâu để bị mắc kẹt về vấn đề này. Nếu bất cứ ai có thể giúp, tôi sẽ trả tiền trước đó 10x và sau đó một số. Cảm ơn bạn.

+0

Tôi đã làm điều này thành công. Bạn có thể xác minh từ bảng điều khiển đường ray mà hồ sơ của bạn thực sự đang được tạo không? Bạn có câu hỏi cụ thể hơn không? – errata

+0

Có - một hồ sơ được tạo cho người dùng. Câu hỏi cụ thể của tôi là làm thế nào để tôi có được thiết lập hướng dẫn này? Lỗi hiện tại là thách thức ngay lập tức, vì vậy nếu có cách giải quyết bất cứ điều gì có thể gây ra vấn đề đó, tôi muốn bắt đầu bằng cách giải quyết điều đó. – Mel

+0

Hi Errata - bạn đã thêm một số loại nút nào đó trong mã của bạn để cho phép người dùng (một lần trong một phiên) thêm danh tính người dùng khác? Bạn đã làm điều đó ở đâu? Tôi đang nghĩ, khi tất cả các vấn đề hiện tại được giải quyết, có thể có nghĩa là thêm nó vào phương thức đăng ký kết thúc. Bạn đã xử lý như thế nào? – Mel

Trả lời

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

Hãy thử resource thay vì @resource. AFAIK nó chỉ là một helper_method, KHÔNG phải là một biến cá thể.

Tôi nghĩ rằng sẽ giải quyết được vấn đề của bạn hoàn toàn trong sản xuất. Vì @resource không giống như resource, nó chưa được đặt và về cơ bản bạn gọi số confirmation_url(nil, confirmation_token: @token) và rằng nil sẽ được chuyển đến thông báo lỗi.

Trong quá trình phát triển, có vẻ như có vấn đề khác, điều này có thể liên quan đến cách bạn đã định cấu hình config.action_mailer.default_url_options trong config/environments/development.rb và rất có thể là ngoại lệ trong ActionDispatch::Http::Url.build_host_url. Tôi nghi ngờ bạn có cái gì đó như:

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

Thay đổi đó để:

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

Và xem nếu có thể giải quyết tất cả mọi thứ. Nếu tôi sai về cách config.action_mailer.default_url_options được định cấu hình, vui lòng dán config/environments/development.rb VÀ theo dõi ngăn xếp đầy đủ của bạn từ lỗi phát triển để chúng tôi có thể trợ giúp thêm cho bạn.

Các vấn đề liên quan