# 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