Mostah
Mostah

Reputation: 25

Rails Validation with two attributes of a variable

I really need your help, because I can't find a solution on my own since 2 hours. I tried many different approaches, but I just don't get how I can validate an attribute of a variable by using another attribute as a condition.

This was my last try:

class User < ApplicationRecord
validates :admin, absence: true,
              if: ["student?", :email]
                def student?(user)
                  user.email.include? 'stud'
                end
end

I want to validate, that a user with the attribute "admin" can't have an email address, which includes 'stud'. Even better would be a validation, that 'stud' users can't be admin.

When I run db:seed I get this error, which stays if I put the 'stud' in brackets:

rails aborted!
ArgumentError: wrong number of arguments (given 0, expected 1)
/home/studi/Bachelorarbeitsapp/sample_app/app/models/user.rb:22:in `student?'

Please have a look at this issue! I am willing to share any other code, which is necessary to solve the problem. Thanks!

Here is the full helper code:

class User < ApplicationRecord
    has_many :preferences, dependent: :destroy
    accepts_nested_attributes_for :preferences

    attr_accessor :remember_token, :activation_token, :reset_token
    before_save   :downcase_email
    before_create :create_activation_digest

    has_secure_password

    validates :name, presence: true, length: { maximum: 50 }

    VALID_EMAIL_REGEX = /\A[\w+\-.]+@(stud.uni-hannover.de|wiwi.uni-hannover.de)+\z/i
    validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: {case_sensitive: false}

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

    validates :mat_number, presence: true, length: { minimum: 7, maximum: 8 }, numericality: { only_integer: true }

    validates :admin, absence: true,
                  if: ["student?", :email]
                    def student?(user)
                      user.email.include? 'stud'
                    end

    validates :ects, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 170, only_integer: true }, allow_nil: true

    validates :grade_avg, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 4 }, 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?(attribute, token)
    digest = send("#{attribute}_digest")
    return false if digest.nil?
    BCrypt::Password.new(digest).is_password?(token)
  end

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

  # Activates an account.
  def activate
    update_columns(activated: true, 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_columns(reset_digest:  User.digest(reset_token), 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

  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

Upvotes: 0

Views: 927

Answers (2)

neo
neo

Reputation: 4116

You don’t need a custom validation:

Could use validates_exclusion_of

validates_exclusion_of :email, in: %w( stud ), if: -> { |user| user.email.include? “admin” }, message: “admins can’t have stud in email”


validates_exclusion_of :email, in: %w( admin ), if: -> { |user|user.email.include? “stud” }, message: “students can’t have admin in email”

Upvotes: 0

jvillian
jvillian

Reputation: 20263

Based on the Active Record Validations Guide section 6.2 Custom Methods, it seems like you should be able to do something like:

class User < ApplicationRecord

  validate :admin_cant_be_student

  def admin_cant_be_student 
    if admin and email.include?('stud')
      errors.add(:admin, "can't have a student email address")
    end
  end

end

Upvotes: 3

Related Questions