module UniverseCompiler module Entity module FieldConstraintManagement BOOLEAN_CONSTRAINTS = %i(not_null not_empty is_array is_hash).freeze PARAMETRIZED_CONSTRAINTS = %i(should_match class_name).freeze INCOMPATIBLE_CONSTRAINTS = { has_one: %i(has_many is_array is_hash), has_many: %i(has_one is_hash), is_array: %i(has_one is_hash), is_hash: %i(has_one has_many is_array) }.freeze def fields_constraints return @fields_constraints unless @fields_constraints.nil? @fields_constraints = if superclass.respond_to? :fields_constraints superclass.fields_constraints.dup else {} end end # Dynamically created constraint methods BOOLEAN_CONSTRAINTS.each do |constraint_name| self.class_eval do define_method constraint_name do |*field_names| field_names.each do |field_name| define_constraint field_name, constraint_name, true end end end end PARAMETRIZED_CONSTRAINTS.each do |constraint_name| self.class_eval do define_method constraint_name do |field_name, param| define_constraint field_name, constraint_name, param end end end def field(field_name, *options) ['', '='].each do |suffix| method_name = '%s%s' % [field_name, suffix] if instance_methods.include? method_name raise UniverseCompiler::Error, "'#{method_name}' method cannot be defined on '#{self}' as it is already defined !" end end define_constraint field_name if options.empty? options.each do |option| case option when Symbol || String if BOOLEAN_CONSTRAINTS.include? option.to_sym send option, field_name else raise UniverseCompiler::Error, "Unknown field option '#{option}' !" end when Hash option.each do |constraint_name, value| if PARAMETRIZED_CONSTRAINTS.include? constraint_name.to_sym send constraint_name, field_name, value else raise UniverseCompiler::Error, "Unknown field option '#{constraint_name}' !" end end else raise UniverseCompiler::Error, "Invalid option for '#{field_name}': #{option.class.name} => #{option.to_s}" end end end def define_constraint(field_name, constraint_name = nil, value = nil) fields_constraints[field_name] ||= {} check_constraints_incompatibilities(field_name, constraint_name) unless constraint_name.nil? and value.nil? fields_constraints[field_name][constraint_name] = value end end private def define_constraints_on_target_entity_type(target_entity_type, source_field, reverse_method = nil) target_class = UniverseCompiler::Entity::TypeManagement.types_classes_mapping[target_entity_type] raise 'NOT IMPLEMENTED' end def check_constraints_incompatibilities(field_name, constraint_name) unless INCOMPATIBLE_CONSTRAINTS[constraint_name].nil? INCOMPATIBLE_CONSTRAINTS[constraint_name].each do |incompatible_constraint| if fields_constraints[field_name].keys.include? incompatible_constraint raise UniverseCompiler::Error, "Cannot set constraint '#{constraint_name}' on field '#{field_name}', incompatible with existing ones !" end end end end end end end