module UniverseCompiler module Entity module FieldManagement private def define_reverse_methods self.class.fields_constraints.each do |method_name, constraints| next unless constraints[:reverse_method] define_reverse_method method_name, constraints[:reverse_method] end end def define_reverse_method(default_method_name, method_definition_constraints) metaclass = class << self; self ; end method_name = normalize_method_name(default_method_name, method_definition_constraints) UniverseCompiler.logger.debug 'Defining reverse method "%s" on class %s (%s)' % [method_name, metaclass, self.type] raise UniverseCompiler::Error, "'#{method_name}' already exists on class '#{metaclass}'. Skipped !" if self.respond_to? method_name method_definition_constraints[:actual_method] = method_name check_operation = case method_definition_constraints[:relation_type] when :has_one '=='.to_sym when :has_many 'include?'.to_sym end metaclass.instance_eval do define_method method_name do raise UniverseCompiler::Error, "Entity '#{as_path}' is not in a universe. Reverse methods can't work !" if universe.nil? res = universe.get_entities(criterion: :by_type, value: method_definition_constraints[:source_entity]).select do |entity| entity[method_definition_constraints[:source_field]].send check_operation, self end return res if res.nil? || res.empty? if method_definition_constraints[:unique_result] if res.size == 1 return res.first else UniverseCompiler.logger.warn 'Too many results. Must be one or none !' UniverseCompiler.logger.debug res.inspect raise UniverseCompiler::Error, "'#{self.as_path}##{method_name}' should return maximum one '#{method_definition_constraints[:source_entity]}' !" end end res end end end def normalize_method_name(default_method_name, method_definition_constraints) if default_method_name == method_definition_constraints[:source_entity] "referenced_from_#{method_definition_constraints[:source_entity]}_entities" else default_method_name end end def define_known_fields_accessors self.class.fields_constraints.each do |field_name, constraints| next if constraints[:reverse_method] define_field_accessor field_name if fields[field_name].nil? fields[field_name] = [] if constraints[:has_many] || constraints[:is_array] fields[field_name] = {} if constraints[:is_hash] end end end def define_field_accessor(field_name) metaclass = class << self; self ; end UniverseCompiler.logger.debug 'Defining field accessor %s on class %s (%s)' % [field_name, metaclass, self.type] define_field_reader(field_name, metaclass) define_field_writer(field_name, metaclass) end def define_field_writer(field_name, metaclass) if self.respond_to? "#{field_name}=" UniverseCompiler.logger.info "'#{field_name}=' already exists on class '#{metaclass}'. Skipped !" else metaclass.instance_eval do define_method "#{field_name}=" do |value| fields[field_name] = value end end end end def define_field_reader(field_name, metaclass) if self.respond_to? field_name UniverseCompiler.logger.info "'#{field_name}' already exists on class '#{metaclass}'. Skipped !" else metaclass.instance_eval do define_method field_name do fields[field_name] end end end end end end end