diff --git a/app/validators/base_validator.rb b/app/validators/base_validator.rb new file mode 100644 index 0000000..eae8a24 --- /dev/null +++ b/app/validators/base_validator.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +# Base class for all validators. +# This class provides a simple interface for validating objects using ActiveModel validations. +# +# Usage: +# +# class CreateCandidateValidator < BaseValidator +# validates :name, presence: true +# validates :email, presence: true, email: true +# end +# +# candidate = Candidate.new(name: '', email: 'john@example.com') +# CreateCandidateValidator.valid?(candidate) # => false +# CreateCandidateValidator.validate!(candidate) # => raises BaseValidator::ValidationError +class BaseValidator < SimpleDelegator + class ValidationError < StandardError; end + + include ActiveModel::Validations + + # Checks if the object is valid based on the defined validations. + # + # @param object [Object] The object to be validated. + # @param params [Hash] Additional parameters for validation. + # @return [Boolean] Returns true if the object is valid, false otherwise. + def self.valid?(object, params = {}) + new(object, params).valid? + end + + # Validates the object and raises a ValidationError if it is not valid. + # + # @param object [Object] The object to be validated. + # @param params [Hash] Additional parameters for validation. + def self.validate!(object, params = {}) + validator = new(object, params) + return true if validator.valid? + + raise ValidationError, validator.errors.full_messages.join(', ') + end + + # Initializes a new instance of BaseValidator. + # + # @param object [Object] The object to be validated. + # @param params [Hash] Additional parameters for validation. + def initialize(object, params = {}) + super(object) + @object = object + @params = params + end +end diff --git a/app/validators/candidates/create_validator.rb b/app/validators/candidates/create_validator.rb new file mode 100644 index 0000000..764fc70 --- /dev/null +++ b/app/validators/candidates/create_validator.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Candidates + class CreateValidator < BaseValidator + validates :name, presence: true + validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP } + validates :birthdate, presence: true + validate :past_birtdate + + def past_birtdate + return if birthdate.blank? + + errors.add(:birthdate, :future_date) if birthdate > Date.current + end + end +end diff --git a/spec/validators/base_validator_spec.rb b/spec/validators/base_validator_spec.rb new file mode 100644 index 0000000..c030932 --- /dev/null +++ b/spec/validators/base_validator_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +class Test + class Validator < BaseValidator + validates :name, presence: true + validate :gmail_email + + def gmail_email + return if email.to_s.ends_with?('@gmail.com') + + errors.add(:email, 'must be a Gmail address') + end + end +end + +RSpec.describe BaseValidator do + describe '.valid?' do + subject(:valid?) { Test::Validator.valid?(candidate) } + + context 'when the candidate is valid' do + let(:candidate) { build(:candidate, email: 'john@gmail.com') } + + it { is_expected.to be true } + end + + context 'when the candidate is invalid' do + let(:candidate) { build(:candidate, email: 'john@hotmail.com') } + + it { is_expected.to be false } + end + end + + describe '.validate!' do + subject(:validate!) { Test::Validator.validate!(candidate) } + + context 'when the candidate is valid' do + let(:candidate) { build(:candidate, email: 'john@gmail.com') } + + it { is_expected.to be true } + end + + context 'when the candidate is invalid' do + let(:candidate) { build(:candidate, name: '', email: 'john@hotmail.com') } + + it 'raises a ValidationError' do + expect { validate! }.to raise_error(BaseValidator::ValidationError, + "Name can't be blank, Email must be a Gmail address") + end + end + end +end diff --git a/spec/validators/candidates/create_validator_spec.rb b/spec/validators/candidates/create_validator_spec.rb new file mode 100644 index 0000000..0e6bb40 --- /dev/null +++ b/spec/validators/candidates/create_validator_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +RSpec.describe Candidates::CreateValidator do + describe '.valid?' do + subject { described_class.valid?(candidate) } + + let(:candidate) { build(:candidate) } + + context 'when the candidate is valid' do + it { is_expected.to be true } + end + + context 'when the name is blank' do + before { candidate.name = '' } + + it { is_expected.to be false } + end + + context 'when the email is blank' do + before { candidate.email = '' } + + it { is_expected.to be false } + end + + context 'when the email is invalid' do + before { candidate.email = 'my.email.at.example.com' } + + it { is_expected.to be false } + end + + context 'when the birthdate is blank' do + before { candidate.birthdate = nil } + + it { is_expected.to be false } + end + + context 'when the birthdate is in the future' do + before { candidate.birthdate = 1.day.from_now } + + it { is_expected.to be false } + end + end +end