class ActiveOopish::Validator

Public: Base class for validators.

Example

class BookValidator < ActiveOopish::Validator
  declear do
    validates :author, presence: true
    validate :title_must_include_author_name, if: :biography?
  end

  private

  def title_must_include_author_name(book)
    unless book.title.include?(book.author.name)
      book.errors.add(:author, "cannot write a biography for other people")
    end
  end

  def biography?(book)
    book.category == :biography
  end
end

class Book < ActiveRecord::Base
  belongs_to :author, class_name: 'User'
  BookValidator.monitor(self)
end

BookValidator.monitor?(Book)
# => true

book = Book.new(title: 'Qiitan biography', author: User.new(name: 'Yaotti'))

book.valid?
# => false

book.errors.full_messages_for(:author).first
# => "author cannot write a biography of another person"

Public Class Methods

injection_name() click to toggle source

Internal: Its name.

Returns a Symbol.

# File lib/activeoopish/validator.rb, line 86
def injection_name
  @method_name ||= "__validator_#{name.downcase.gsub('::', '_')}"
end
inspect() click to toggle source
# File lib/activeoopish/validator.rb, line 90
def inspect
  name
end
monitor(model_class) click to toggle source

Public: Start validating the given model_class’s instances.

model_class - A model Class to be validated by it.

Raises AlreadyMonitored if the given model_class has already been

monitored.

Raises DeclarationNotFound when it has not decleared yet. Returns nothing.

# File lib/activeoopish/validator.rb, line 66
def monitor(model_class)
  fail AlreadyMonitored if monitoring?(model_class)
  define_accessor_to_model_class(model_class)
  apply_decleared_rules_to_model_class(model_class)
  monitoring_model_classes << model_class
end
monitoring?(target) click to toggle source

Public: Whether the given model_class is watched by it.

target - A class which includes ActiveModel::Validations or its instance.

Returns true or false.

# File lib/activeoopish/validator.rb, line 78
def monitoring?(target)
  model_class = target.is_a?(ActiveModel::Validations) ? target.class : target
  monitoring_model_classes.include?(model_class)
end

Private Class Methods

apply_decleared_rules_to_model_class(model_class) click to toggle source
# File lib/activeoopish/validator.rb, line 146
def apply_decleared_rules_to_model_class(model_class)
  fail DeclarationNotFound unless @declaration
  @model_class = model_class
  begin
    class_eval(&@declaration)
  ensure
    @model_class = nil
  end
end
declear(&block) click to toggle source

Internal: Declear how to validate a model class.

block - A block which will be applied later.

It may call `validates`, `validates_with` and `validate`
methods to self of the block context.

Example

Validator.declear do
  # Delegated to @model_class.
  validates :name, presence: true
  validates_with AnActiveModelValidator

  # Create a proxy method to the corresponding validator method.
  validate :validate_method
end

Returns nothing.

# File lib/activeoopish/validator.rb, line 118
def declear(&block)
  @declaration = block
end
define_accessor_to_model_class(model_class) click to toggle source

Internal: Define a private instance method something like

def __validator_bookvalidator
  @__validator_bookvalidator ||= BookValidator.new
end

to the given model_class.

model_class - A model Class to be validated by it.

Returns nothing.

# File lib/activeoopish/validator.rb, line 133
def define_accessor_to_model_class(model_class)
  validator_class = self

  model_class.class_eval do
    unless private_instance_methods(false).include?(validator_class.injection_name)
      define_method(validator_class.injection_name) do
        instance_variable_set("@#{validator_class.injection_name}", validator_class.new)
      end
      private validator_class.injection_name
    end
  end
end
monitoring_model_classes() click to toggle source
# File lib/activeoopish/validator.rb, line 96
def monitoring_model_classes
  @monitoring_model_classes ||= []
end
validate(method_name, options = {}) click to toggle source

Internal: Define a private method to the model class which works as a proxy for the validator’s corresponding method. The validator method will be called with the model instance as the first argument.

Suppose a ‘UserValidator` declears:

validate :must_be_qiitan, if: :active?

then, this method defines some private methods to the @model_class something like:

def __validator_uservalidator_validate_must_be_qiitan
  __validator_uservalidator.__send__(:must_be_qiitan, self)
end

def __validator_uservalidator_if_active?
  __validator_uservalidator.__send__(:active?, self)
end

validate(
  :__validator_uservalidator_validate_must_be_qiitan,
  if: __validator_user_validator_if_active?
)

Note that it does not support a block and a proc for :if and :unless options, though ActiveModel::Validations::ClassMethods.validate supports them.

Returns nothing.

# File lib/activeoopish/validator.rb, line 191
def validate(method_name, options = {})
  validator_class = self

  proxy_method_name = "#{validator_class.injection_name}_validate_#{method_name}"
  proxy_map = { proxy_method_name => method_name }

  (options.keys & [:if, :unless]).each do |key|
    value = options[key]
    proxy_name = "#{validator_class.injection_name}_#{key}_#{value}"
    proxy_map[proxy_name] = value
    options[key] = proxy_name
  end

  @model_class.class_eval do
    proxy_map.each_pair do |model_method_name, validator_method_name|
      unless private_instance_methods(false).include?(model_method_name)
        define_method(model_method_name) do
          __send__(validator_class.injection_name).__send__(validator_method_name, self)
        end
        private model_method_name
      end
    end

    # Add the proxy method to the model_class as a validation method.
    validate(proxy_method_name, **options)
  end
end