2017-08-05 95 views
0

我的意圖是實施STI兩種類型:工作人員和臨牀醫師。我以前的實現是使用枚舉的角色,並在盡我所能按照類似問題的答案後,拿出測試等中的所有引用來枚舉角色並替換爲類型的引用,當我獲得以下錯誤的許多版本運行我的測試套件:單表繼承錯誤 - ActiveRecord :: SubclassNotFound

ERROR["test_valid_signup_information_with_account_activation", UsersSignupTest, 1.01794000000001] 
test_valid_signup_information_with_account_activation#UsersSignupTest (1.02s) 
ActiveRecord::SubclassNotFound:   ActiveRecord::SubclassNotFound: The single-table inheritance mechanism failed to locate the subclass: 'Staff'. This error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this column if you didn't intend it to be used for storing the inheritance class or overwrite User.inheritance_column to use another column for that information. 
      app/controllers/users_controller.rb:19:in `create' 
      test/integration/users_signup_test.rb:27:in `block (2 levels) in <class:UsersSignupTest>' 
      test/integration/users_signup_test.rb:26:in `block in <class:UsersSignupTest>' 

這裏是我的困惑有可能被隱藏的問題幾個方面:

在我的用戶模型user.rb,我想我正確地確定該子類(工作人員和臨牀醫師),但我不確定是否正確包裝了一切。所有其他代碼是否必須包含在其中一個類中?我濫用「結束」了嗎?

class User < ApplicationRecord 
end 

class Staff < User 
end 

class Clinician < User 
end 

belongs_to :university 
has_many :referral_requests 




    attr_accessor :remember_token, :activation_token, :reset_token 
    before_save :downcase_email 
    before_create :create_activation_digest 
    validates :name, presence: true, length: { maximum: 50 } 
    VALID_EMAIL_REGEX = /\A[\w+\-.][email protected][a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i 
    validates :email, presence: true, length: { maximum: 255 }, 
        format: { with: VALID_EMAIL_REGEX }, 
        uniqueness: { case_sensitive: false } 
    validates :type, presence: true 
    validates :university_id, presence: true, if: lambda { self.type == 'Staff' } 

    has_secure_password 
    validates :password, presence: true, length: { minimum: 6 }, allow_nil: true 






    # Returns the hash digest of the given string. 
    def User.digest(string) 
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : 
                BCrypt::Engine.cost 
    BCrypt::Password.create(string, cost: cost) 
    end 

    # Returns a random token. 
    def User.new_token 
    SecureRandom.urlsafe_base64 
    end 

    # Remembers a user in the database for use in persistent sessions. 
    def remember 
    self.remember_token = User.new_token 
    update_attribute(:remember_digest, User.digest(remember_token)) 
    end 

    # Returns true if the given token matches the digest. 
    def authenticated?(remember_token) 
     return false if remember_digest.nil? 
    BCrypt::Password.new(remember_digest).is_password?(remember_token) 
    end 


    # Forgets a user. 
    def forget 
    update_attribute(:remember_digest, nil) 
    end 

    # Returns true if the given token matches the digest. 
    def authenticated?(attribute, token) 
    digest = send("#{attribute}_digest") 
    return false if digest.nil? 
    BCrypt::Password.new(digest).is_password?(token) 
    end 

    # Activates an account. 
    def activate 
    update_attribute(:activated, true) 
    update_attribute(:activated_at, Time.zone.now) 
    end 

    # Sends activation email. 
    def send_activation_email 
    UserMailer.account_activation(self).deliver_now 
    end 

# Sets the password reset attributes. 
    def create_reset_digest 
    self.reset_token = User.new_token 
    update_attribute(:reset_digest, User.digest(reset_token)) 
    update_attribute(:reset_sent_at, Time.zone.now) 
    end 

    # Sends password reset email. 
    def send_password_reset_email 
    UserMailer.password_reset(self).deliver_now 
    end 

    # Returns true if a password reset has expired. 
    def password_reset_expired? 
    reset_sent_at < 2.hours.ago 
    end 

def feed 
    ReferralRequest.where("user_id = ?", id) 
    end 


private 

    # Converts email to all lower-case. 
    def downcase_email 
    self.email = email.downcase 
    end 

    # Creates and assigns the activation token and digest. 
    def create_activation_digest 
     self.activation_token = User.new_token 
     self.activation_digest = User.digest(activation_token) 
    end 
end 

下面是發生故障的具體測試代碼(許多被失敗的測試套件之一 - 所有的用戶參數也同樣,雖然定義)。我是否適當地傳遞員工參數?

test "valid signup information with account activation" do 
    get signup_path 
    assert_difference 'User.count', 1 do 
     post users_path, params: { user: { name: "Example User", 
             email: "[email protected]", 
             university_id: 1 , 
             type: "Staff", 
             password:    "password", 
             password_confirmation: "password" } } 

這裏是我的用戶表的模式:

create_table "users", force: :cascade do |t| 
    t.string "name" 
    t.string "email" 
    t.datetime "created_at",      null: false 
    t.datetime "updated_at",      null: false 
    t.string "password_digest" 
    t.string "remember_digest" 
    t.string "activation_digest" 
    t.boolean "activated",   default: false 
    t.datetime "activated_at" 
    t.string "reset_digest" 
    t.datetime "reset_sent_at" 
    t.integer "university_id" 
    t.integer "role" 
    t.string "type" 
    t.index ["email"], name: "index_users_on_email", unique: true 
    end 

非常感謝您的任何想法!我在這裏問了很多問題,但只是在嘗試通過相似的答案很長一段時間之後。

回答

1

假設上面的代碼示例是準確的,您會看到此錯誤,因爲user.rb文件無效且無法解析。你也應該看到一個關於解釋器的錯誤。

class User < ApplicationRecord 
    belongs_to :university 
    has_many :referral_requests 

    attr_accessor :remember_token, :activation_token, :reset_token 
    before_save :downcase_email 
    before_create :create_activation_digest 
    validates :name, presence: true, length: { maximum: 50 } 
    VALID_EMAIL_REGEX = /\A[\w+\-.][email protected][a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i 
    validates :email, presence: true, length: { maximum: 255 }, 
        format: { with: VALID_EMAIL_REGEX }, 
        uniqueness: { case_sensitive: false } 
    validates :type, presence: true 

    has_secure_password 
    validates :password, presence: true, length: { minimum: 6 }, allow_nil: true 
    # etc... 
end 

class Staff < User 
    validates :university_id, presence: true 
end 

class Clinician < User 
end 

標準類的繼承應用做法,所以如果有代碼在那裏,這只是適合於特定的子類它應該移動那裏(例如university_id驗證移動到Staff)。

# Returns the hash digest of the given string. 
def User.digest(string) 
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : 
               BCrypt::Engine.cost 
    BCrypt::Password.create(string, cost: cost) 
end 

# Returns a random token. 
def User.new_token 
    SecureRandom.urlsafe_base64 
end 

這些應該寫成

def self.digest(string) 
    # ... 
end 

def self.new_token 
    # ... 
end 

,或者

class << self 
    def digest(string) 
    # ... 
    end 

    def new_token 
    # ... 
    end 
end 
+0

的代碼示例是正確的,只要是我怎麼把它寫在。根據您的有用反饋,這顯然是錯誤的。我會執行。謝謝! – mike9182

+0

一個問題:爲什麼我需要將切換到STI的方法(如摘要)更改爲新的語法?我相信你,我可以這樣寫,但我不知道它爲什麼會改變。 – mike9182

+0

@ mike9182與STI無關,這就是Rubyists如何寫的。我認爲你的工作有效,但我給出的例子是其他Ruby用戶期望看到的。 –