84 lines
2.0 KiB
Ruby
84 lines
2.0 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class User < ApplicationRecord
|
|
include Discard::Model
|
|
|
|
def destroy(*)
|
|
soft_delete_guard
|
|
discard
|
|
end
|
|
|
|
def destroy!(*)
|
|
soft_delete_guard
|
|
discard
|
|
end
|
|
|
|
def really_destroy!
|
|
ActiveRecord::Base.instance_method(:destroy).bind(self).call
|
|
end
|
|
|
|
belongs_to :classroom, optional: true
|
|
|
|
# Allow calling `user.school` (used in portfolio view) via the classroom's associated school.
|
|
# This prevents undefined method errors for Student records without directly adding a belongs_to.
|
|
delegate :school, to: :classroom, allow_nil: true
|
|
delegate :name, to: :school, prefix: :school, allow_nil: true
|
|
delegate :trading_enabled?, to: :classroom, allow_nil: true
|
|
|
|
has_one :portfolio, dependent: :destroy
|
|
accepts_nested_attributes_for :portfolio
|
|
has_many :orders, dependent: :destroy
|
|
|
|
# Include default devise modules. Others available are:
|
|
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
|
|
devise :database_authenticatable, :registerable,
|
|
:recoverable, :rememberable, :validatable
|
|
|
|
validates :email, uniqueness: true, presence: false, allow_blank: true
|
|
validates :username, presence: true, uniqueness: true
|
|
validates :type, inclusion: { in: %w[User Student Teacher] }
|
|
|
|
scope :students, -> { where(type: "Student") }
|
|
scope :teachers, -> { where(type: "Teacher") }
|
|
scope :admins, -> { where(admin: true) }
|
|
|
|
def student?
|
|
type == "Student"
|
|
end
|
|
|
|
def teacher?
|
|
type == "Teacher"
|
|
end
|
|
|
|
def teacher_or_admin?
|
|
teacher? || admin?
|
|
end
|
|
|
|
def display_name
|
|
username.presence || email&.split("@")&.first || "User"
|
|
end
|
|
|
|
def email_required?
|
|
teacher? || admin?
|
|
end
|
|
|
|
def email_changed?
|
|
false
|
|
end
|
|
|
|
def holding?(stock)
|
|
portfolio&.shares_owned(stock.id)&.positive?
|
|
end
|
|
|
|
private
|
|
|
|
def soft_delete_guard
|
|
return if Rails.env.production?
|
|
|
|
raise <<~MSG.squish
|
|
❌ Hard delete attempted on #{self.class}. Use #discard instead,
|
|
or #really_destroy! if you are ABSOLUTELY sure you need a hard delete.
|
|
MSG
|
|
end
|
|
end
|