class User < ApplicationRecord has_secure_password belongs_to :invited_by, class_name: "User", optional: true has_many :invited_users, class_name: "User", foreign_key: :invited_by_id, dependent: :nullify has_many :created_entries, class_name: "Entry", foreign_key: :created_by_id, dependent: :nullify has_many :updated_entries, class_name: "Entry", foreign_key: :updated_by_id, dependent: :nullify has_many :requested_entries, class_name: "Entry", foreign_key: :requested_by_id, dependent: :nullify has_many :submitted_suggested_meanings, class_name: "SuggestedMeaning", foreign_key: :submitted_by_id, dependent: :nullify has_many :reviewed_suggested_meanings, class_name: "SuggestedMeaning", foreign_key: :reviewed_by_id, dependent: :nullify has_many :comments, dependent: :nullify enum :role, %i[contributor reviewer admin] validates :email, presence: true, uniqueness: { case_sensitive: false } validates :password, length: { minimum: 12 }, if: -> { password.present? } before_validation :normalize_email scope :by_role, ->(role) { where(role: role) if role.present? } scope :search_email, ->(q) { where("email LIKE ?", "%#{sanitize_sql_like(q)}%") if q.present? } # Invitation token expires after 14 days INVITATION_TOKEN_EXPIRY = 14.days # Remember me token expires after 2 weeks REMEMBER_TOKEN_EXPIRY = 2.weeks def invitation_expired? return false if invitation_sent_at.nil? invitation_sent_at < INVITATION_TOKEN_EXPIRY.ago end def invitation_pending? invitation_token.present? && invitation_accepted_at.nil? && !invitation_expired? end def invite_by(invitee) self.invited_by = invitee if invitee && invited_by.nil? self.invitation_token = SecureRandom.urlsafe_base64(32) self.invitation_sent_at = Time.current end def invite_by!(invitee = nil) invite_by(invitee) save! end def self.find_by_valid_invitation_token(token) where(invitation_token: token) .where(invitation_accepted_at: nil) .where("invitation_sent_at > ?", INVITATION_TOKEN_EXPIRY.ago) .first end def remember_me self.remember_token = SecureRandom.urlsafe_base64(32) self.remember_created_at = Time.current save(validate: false) remember_token end def forget_me update_columns(remember_token: nil, remember_created_at: nil) end def remember_token_expired? return true if remember_created_at.nil? remember_created_at < REMEMBER_TOKEN_EXPIRY.ago end def self.find_by_valid_remember_token(token) user = find_by(remember_token: token) return nil if user.nil? || user.remember_token_expired? user end private def normalize_email self.email = email.downcase.strip if email.present? end end