module DataMapper class ForeignKeyNotFoundError < StandardError; end class AssociationProtectedError < StandardError; end module Associations class HasNAssociation attr_reader :options OPTIONS = [ :class, :class_name, :foreign_key, :dependent ] def initialize(klass, association_name, options) @constant = klass @adapter = database.adapter @table = @adapter.table(klass) @association_name = association_name.to_sym @options = options || Hash.new define_accessor(klass) Persistence::dependencies.add(associated_constant_name) do |klass| @foreign_key_column = associated_table[foreign_key_name] unless @foreign_key_column associated_constant.property(foreign_key_name, foreign_key_type) @foreign_key_column = associated_table[foreign_key_name] if @foreign_key_column.nil? raise ForeignKeyNotFoundError.new(<<-EOS.compress_lines) key_table => #{key_table.inspect}, association_table => #{associated_table.inspect}, association_name => #{name}, foreign_key_name => #{foreign_key_name.inspect}, foreign_key_type => #{foreign_key_type.inspect}, constant => #{constant.inspect}, associated_constant => #{associated_constant.inspect} EOS end end end end def name @association_name end def constant @constant end def associated_constant @associated_constant || @associated_constant = Kernel.const_get(associated_constant_name) end def associated_constant_name @associated_constant_name || begin if @options.has_key?(:class) || @options.has_key?(:class_name) @associated_constant_name = (@options[:class] || @options[:class_name]) if @associated_constant_name.kind_of?(String) @associated_constant_name = Inflector.classify(@associated_constant_name) elsif @associated_constant_name.kind_of?(Class) @associated_constant_name = @associated_constant_name.name end else @associated_constant_name = Inflector.classify(@association_name) end @associated_constant_name end end def primary_key_column @primary_key_column || @primary_key_column = key_table.key end def foreign_key_column @foreign_key_column end def foreign_key_name @foreign_key_name || @foreign_key_name = (@options[:foreign_key] || key_table.default_foreign_key) end def foreign_key_type @foreign_key_type || @foreign_key_type = key_table.key.type end def key_table @key_table || @key_table = @adapter.table(constant) end def associated_table @association_table || @association_table = @adapter.table(associated_constant) end def associated_columns associated_table.columns.reject { |column| column.lazy? } end def complementary_association @complementary_association || begin @complementary_association = associated_table.associations.find do |mapping| mapping.is_a?(BelongsToAssociation) && mapping.foreign_key_column == foreign_key_column && mapping.key_table.name == key_table.name end if @complementary_association class << self attr_accessor :complementary_association end end return @complementary_association end end def finder_options @finder_options || @finder_options = @options.reject { |k,v| self.class::OPTIONS.include?(k) } end def to_sql "JOIN #{associated_table.to_sql} ON #{foreign_key_column.to_sql(true)} = #{primary_key_column.to_sql(true)}" end def activate!(force = false) foreign_key_column end end end end