# Error, that indicates a constraint violation
class ConstraintViolationError < StandardError
end
# Team class which enables sensing of constraint violation.
# A constraint is a subtype of class Constraint.
#
class ConstraintSensing < Team
# Constraint class to check a certain attribute.
class AttributeConstraint
# This method has to be implemented to check
# a given value if the constraint is valid.
def check_attribute(value)
raise "abstract method called"
end
end
# Just a simple debug constraint, to see if the check is performed.
class DebugConstraint < AttributeConstraint
def check_attribute(value)
puts "check attribute #{value.class} = #{value.to_s}\n"
end
end
# Ensure, that the given attribute lives in a defined boundary.
# The instance has to be initialized with two boundary objects,
# which have to be understand the "<" and ">" operators.
class MinMaxValue < AttributeConstraint
#lower bound
attr :min
#upper bound
attr :max
def initialize(min, max)
@min = min
@max = max
end
def check_attribute(value)
return "Value less than #{@min}" if (value<@min)
return "Value greater than #{@max}" if (value>@max)
end
end
# Ensure, that the given attribute has a length between a given boundary.
class MinMaxLength < AttributeConstraint
#lower bound
attr :min
#upper bound
attr :max
def initialize(min, max)
@min = min
@max = max
end
def check_attribute(value)
if (value.respond_to?("length"))
return "Value.length less than #{@min}" if (value.length<@min)
return "Value.length greater than #{@min}" if (value.length>@max)
end
end
end
# Ensure, that the given value is not nil.
class Mandatory < AttributeConstraint
def check_attribute(value)
return "Mandatory attribute can't be null" if (value.nil?)
end
end
# Ensure, that the given value applies to the given regular expression
class RegExp < AttributeConstraint
attr :regexp
def initialize(regexp)
@regexp = regexp
end
def check_attribute(value)
return ">#{value}< does not match #{@regexp.source}" if (!@regexp.match(value))
end
end
# This class represents the object, which constraints have to be checked.
# Each attribute can have several constraints.
class ConstraintObject
#constraints as hash: accessormethod -> list of constraints
attr :constraints
def initialize()
@constraints = Hash.new
end
# To add a certain constraint to a given attribute.
def add_constraint(attribute, constraints)
@constraints[attribute] = constraints
end
# This method checks for the given attribute all defined constraints.
# The method handle_constraint_check is called, if the given value does
# not conform to the defined constraints.
def check_attribute(attribute, value)
flag = true
while flag
flag = false
@constraints[attribute].each { |constraint|
desc = constraint.check_attribute(value)
if (desc)
value = handle_constraint_check(attribute, value, constraint, desc)
flag = true
end
}
end
return value
end
# Handle constraint violations:
# Raise an exception to signal the violation.
# Refine this method to handle the violation.
def handle_constraint_check(attribute, value, constraint, description)
raise "#{attribute} constraint #{constraint.class} failed: #{description}"
end
# Convenient method to check attribute settings via all set_XXX.
# If you conform to the coding convention set_XXX(NEWXXX), that changes
# attribute XXX to NEWXXX, than you can use this method as callin trigger.
# The name of te joinpoint indicates the attribute to check for.
def check_setter(value)
setter = JoinPoint.actualJoinPoint.source_method
setter = setter.sub(/^set_/, "")
base(check_attribute(setter, value))
end
end
end
# The class ConstraintSensing is able to detect a constraint violation, but
# is not able to remedy the failure. This team tries to solve a certain
# constraint violation.
class ConstraintHealing < ConstraintSensing
# Baseclass of all solving strategies.
class Solver
def solve(attribute, value, constraint, description)
raise "abstract method called"
end
end
# Query a new String.
class StringSolver < Solver
def solve(attribute, value, constraint, description)
puts "#{constraint.class.name} Error for #{attribute} of value >>#{value}<<:\n#{description}\n"
puts "Please enter new string\n\n"
return Readline.readline
end
end
# Query for new Date.
class DateSolver < Solver
def solve(attribute, value, constraint, description)
puts "#{constraint.class.name} Error: #{description}\n"
puts "Please enter new date\n\n"
begin
newdate = Date.parse(Readline.readline)
rescue ArgumentError=>er
puts "\nCan't parse date! Reverting to old value...\n"
newdate = value
end
return newdate
end
end
# Refine this class to be able to solve constraint violation.
class ConstraintObject
# hash holding all solving strategies.
attr :solver
def initialize()
super()
@solver = Hash.new
end
# add solving strategy as well as the constraints to check.
def add_constraint(attribute, constraints, solver)
super(attribute, constraints)
@solver[attribute] = solver
end
# Try to solve the given problem.
def handle_constraint_check(attribute, value, constraint, description)
solver = @solver[attribute]
raise "no solver for #{attribute} given!" if solver.nil?
newvalue = solver.solve(attribute, value, constraint, description)
return newvalue
end
end
end
syntax highlighted by Code2HTML, v. 0.9.1