This library provides a way to ensure that object always satisfy a
specified set of constraints. An object that can be constrained must be
of kind <tt>Constraint::Shell</tt>. Constraints can be added to classes
and single objects. It is possible to define methods that handle
constraint violations and can make the object fit the demands.

Project:: http://rubyforge.org/projects/constraint/
Download:: http://rubyforge.org/frs/?group_id=748
Support:: http://rubyforge.org/forum/?group_id=748

In my experience, a certain type of errors in dynamically typed
languages are caused by pushing some wrong object to a collection type
of object. What makes this type of error so awkward is that they often
result in an exception in some totally different part of your program,
which is why it sometimes can be quite hard to track down what is
actually causing the problem. The goal of this library is to raise an
exception right where such a thing happens. While ruby is sometimes a
little bit hesitant in raising exceptions, this library tries to
counteract this tendency a little bit.

Be aware that this library introduces yet another level of indirection
and yet additional runtime checks what slows things down a little. If
you define CONSTRAINT_DISABLE before requiring the library, constraint
checks will be deactivated.


= Documentation

Constrained classes should inherit from <tt>Constraint::Shell</tt>. The
subclass should provide a way for defining new instances (by defining
#new_constrained) and redefine #process_constrained_value. The
@constrained_value instance variable contains the shell's actual values.

Examples:

class ConstrainedArray < Constraint::Shell
def new_constrained
[]
end

def process_constrained_value
@constrained_value.collect! {|e| yield e}
end
end

class NumericArray < ConstrainedArray
or_constraint("Numeric") {|e| e.kind_of?(Numeric)}
end

class EvenNumericArray < NumericArray
and_constraint("Even") {|e| e.modulo(2) == 0}
end

class EvenInteger < Constraint::Shell
and_constraint("Numeric") {|e| e.kind_of?(Integer)}
and_constraint("Even") {|e| e.modulo(2) == 0}
end

enum = EvenInteger.new(2)
enum.and_constraint("LT10") {|e| e < 10}

or:

class DuckPalace < ConstrainedArray
and_constraint('Quack') {|e| e.do_you_quack?}
and_constraint('Ticket') {|e| e.your_ticket_please == :valid}
end

whiteduckpalace = DuckPalace.new
whiteduckpalace.and_constraint('White') {|e| e.colour == 'white'}
class << whiteduckpalace
def handle_constraint_violation(contraint_name, object)
case contraint_name
when 'white'
return object.paint_me('white') if object.respond_to?(:paint_me)
end
return super
end
end