2014-12-06 58 views
2

我有一個使用Devise(最新)的Rails 4應用程序,並試圖爲每個用戶(如ID,但更長等)創建一個隨機令牌。使用this回答我能拿出後續代碼:在Rails帳戶創建時創建隨機,唯一的令牌

# app/models/model_name.rb 
class ModelName < ActiveRecord::Base 
    include Tokenable 
end 

# app/models/concerns/tokenable.rb 
module Tokenable 
    extend ActiveSupport::Concern 

    included do 
    before_create :generate_token 
    end 

    protected 

    def generate_token 
    self.token = loop do 
     random_token = SecureRandom.urlsafe_base64(nil, false) 
     break random_token unless self.class.exists?(token: random_token) 
    end 
    end 
end 

此代碼的工作充滿了想象的是對於任何給定模型的獨特標記。即所有用戶將擁有唯一的令牌,並且所有的管理員都將擁有唯一的令牌。但是管理員可能與用戶具有相同的標記 - 此行爲是不需要的。

是否有一種優雅的方式,即將標記抽象爲它自己的模型並使用「has_one」關係來確保標記不存在於它所屬的所有模型中?

(我想我可以硬編碼unless (User.exists? ... or Admin.exists? ...)進入,除非條款,雖然這看起來笨重。)

任何想法或建議表示讚賞!謝謝!

回答

2

我會創建一個方法,列出每個類包括我的關注,然後測試每個令牌。事情是這樣的:

# app/models/concerns/tokenable.rb 
module Tokenable 
    extend ActiveSupport::Concern 

    included do 
    before_create :generate_token 
    end 

    protected 

    def included_classes 
    ActiveRecord::Base.descendants.select do |c| 
     c.included_modules.include(Concerns::Tokenable)}.map(&:name) 
    end 
    end 

    def generate_token 
    self.token = loop do 
     random_token = SecureRandom.urlsafe_base64(nil, false) 
     break random_token unless included_classes.map {|c| c.constantize.exists?(token: random_token) }.include?(true) 
    end 
    end 
end 

所以include_classes是要返回名稱的數組作爲每一個包括Tokenable關注類的字符串。然後在generate_token中的循環中檢查每個類是否生成真或假數組,然後我們只需檢查include?(true)是否爲真。

Here is were I found how to get included classes(第一回答)。

編輯

在Rails 5 included_classes看起來是這樣的(注意ApplicationRecord並且不需要顧慮:: Tokenable):

def included_classes 
    ApplicationRecord.descendants.select do |c| 
     c.included_modules.include?(Tokenable) 
    end 
    end 
+0

我遇到了「c.included_modules.include(Concerns :: Tokenable)'的循環依賴檢測錯誤'。設計是否會造成這個圈子? – Vasseurth 2014-12-09 06:59:42

+1

我可以通過將include(Concerns :: Tokenable)更改爲include(Tokenable)來解決這個錯誤。 – Vasseurth 2014-12-09 07:28:48

0

Rails的5配備了一個新的feaeture has_secure_token是很容易使用方法:

# Schema: User(token:string, auth_token:string) 
class User < ActiveRecord::Base 
    has_secure_token :auth_token 
end 

user = User.new 
user.save 
user.auth_token # => "pX27zsMN2ViQKta1bGfLmVJE" 
user.regenerate_auth_token # => true 

由於Rails的5尚未發佈,您可以使用反向移植has_secure_token寶石