lib/ohm/validations.rb in ohm-0.1.0.rc6 vs lib/ohm/validations.rb in ohm-0.1.0
- old
+ new
@@ -1,9 +1,62 @@
# encoding: UTF-8
module Ohm
+ # Provides a base implementation for extensible validation routines.
+ # {Ohm::Validations} currently only provides the following assertions:
+ #
+ # * assert
+ # * assert_present
+ # * assert_format
+ # * assert_numeric
+ #
+ # The core tenets that Ohm::Validations advocates can be summed up in a
+ # few bullet points:
+ #
+ # 1. Validations are much simpler and better done using composition rather
+ # than macros.
+ # 2. Error messages should be kept separate and possibly in the view or
+ # presenter layer.
+ # 3. It should be easy to write your own validation routine.
+ #
+ # Since Ohm's philosophy is to keep the core code small, other validations
+ # are simply added on a per-model or per-project basis.
+ #
+ # If you want other validations you may want to take a peek at Ohm::Contrib
+ # and all of the validation modules it provides.
+ #
+ # @see http://cyx.github.com/ohm-contrib/doc/Ohm/WebValidations.html
+ # @see http://cyx.github.com/ohm-contrib/doc/Ohm/NumberValidations.html
+ # @see http://cyx.github.com/ohm-contrib/doc/Ohm/ExtraValidations.html
+ #
+ # @example
+ #
+ # class Product < Ohm::Model
+ # attribute :title
+ # attribute :price
+ # attribute :date
+ #
+ # def validate
+ # assert_present :title
+ # assert_numeric :price
+ # assert_format :date, /\A[\d]{4}-[\d]{1,2}-[\d]{1,2}\z
+ # end
+ # end
+ #
+ # product = Product.new
+ # product.valid? == false
+ # # => true
+ #
+ # product.errors == [[:title, :not_present], [:price, :not_numeric],
+ # [:date, :format]]
+ # # => true
+ #
module Validations
+ # Provides a simple implementation using the Presenter Pattern. When
+ # presenting errors, you have to properly catch all errors generated, or
+ # else you'll get an {Ohm::Validations::Presenter::UnhandledErrors}
+ # exception.
class Presenter
class UnhandledErrors < StandardError
attr :errors
def initialize(errors)
@@ -45,10 +98,12 @@
yield(errors) if block_given?
end
end
end
+ # A simple class for storing all errors. Since {Ohm::Validations::Errors}
+ # extends Array, you can expect all array methods to work on it.
class Errors < Array
attr_accessor :model
def initialize(model)
@model = model
@@ -57,41 +112,102 @@
def present(presenter = Presenter, &block)
presenter.new(model.errors).present(&block)
end
end
+ # Check if the current model state is valid. Each call to {#valid?} will
+ # reset the {#errors} array.
+ #
+ # All model validations should be declared in a `validate` method.
+ #
+ # @example
+ #
+ # class Post < Ohm::Model
+ # attribute :title
+ #
+ # def validate
+ # assert_present :title
+ # end
+ # end
+ #
def valid?
errors.clear
validate
errors.empty?
end
+ # Base validate implementation.
def validate
end
+ # All errors for this model.
def errors
@errors ||= Errors.new(self)
end
protected
+ # Allows you to do a validation check against a regular expression.
+ # It's important to note that this internally calls {#assert_present},
+ # therefore you need not structure your regular expression to check
+ # for a non-empty value.
+ #
+ # @param [Symbol] att The attribute you want to verify the format of.
+ # @param [Regexp] format The regular expression with which to compare
+ # the value of att with.
+ # @param [Array<Symbol, Symbol>] error The error that should be returned
+ # when the validation fails.
def assert_format(att, format, error = [att, :format])
if assert_present(att, error)
assert(send(att).to_s.match(format), error)
end
end
+ # The most basic and highly useful assertion. Simply checks if the
+ # value of the attribute is empty.
+ #
+ # @param [Symbol] att The attribute you wish to verify the presence of.
+ # @param [Array<Symbol, Symbol>] error The error that should be returned
+ # when the validation fails.
def assert_present(att, error = [att, :not_present])
assert(!send(att).to_s.empty?, error)
end
+ # Checks if all the characters of an attribute is a digit. If you want to
+ # verify that a value is a decimal, try looking at Ohm::Contrib's
+ # assert_decimal assertion.
+ #
+ # @param [Symbol] att The attribute you wish to verify the numeric format.
+ # @param [Array<Symbol, Symbol>] error The error that should be returned
+ # when the validation fails.
+ # @see http://cyx.github.com/ohm-contrib/doc/Ohm/NumberValidations.html
def assert_numeric(att, error = [att, :not_numeric])
if assert_present(att, error)
assert_format(att, /^\d+$/, error)
end
end
+ # The grand daddy of all assertions. If you want to build custom
+ # assertions, or even quick and dirty ones, you can simply use this method.
+ #
+ # @example
+ #
+ # class Post < Ohm::Model
+ # attribute :slug
+ # attribute :votes
+ #
+ # def validate
+ # assert_slug :slug
+ # assert votes.to_i > 0, [:votes, :not_valid]
+ # end
+ #
+ # protected
+ # def assert_slug(att, error = [att, :not_slug])
+ # assert send(att).to_s =~ /\A[a-z\-0-9]+\z/, error
+ # end
+ # end
def assert(value, error)
value or errors.push(error) && false
end
end
end
+