Class: DataMapper::Associations::Relationship

### Home Classes Methods

### In Files

- lib/dm-core/associations/relationship.rb

### Parent

Object

### Methods

- ::new
- #==
- #child_key
- #child_model
- #child_model?
- #child_model_name
- #eager_load
- #eql?
- #field
- #get
- #get!
- #hash
- #inverse
- #loaded?
- #parent_key
- #parent_model
- #parent_model?
- #parent_model_name
- #query_for
- #relative_target_repository_name
- #relative_target_repository_name_for
- #set
- #set!
- #source_scope
- #valid?

### Included Modules

- DataMapper::Assertions
- DataMapper::Subject

### Files

- LICENSE
- README.rdoc

### Class/Module Index

Quicksearch

- DataMapper
- DataMapper::Adapters
- DataMapper::Adapters::AbstractAdapter
- DataMapper::Adapters::InMemoryAdapter
- DataMapper::Assertions
- DataMapper::Associations
- DataMapper::Associations::ManyToMany
- DataMapper::Associations::ManyToMany::Collection
- DataMapper::Associations::ManyToMany::Relationship
- DataMapper::Associations::ManyToOne
- DataMapper::Associations::ManyToOne::Relationship
- DataMapper::Associations::OneToMany
- DataMapper::Associations::OneToMany::Collection
- DataMapper::Associations::OneToMany::Relationship
- DataMapper::Associations::OneToOne
- DataMapper::Associations::OneToOne::Relationship
- DataMapper::Associations::Relationship
- DataMapper::Chainable
- DataMapper::Collection
- DataMapper::Deprecate
- DataMapper::DescendantSet
- DataMapper::Equalizer
- DataMapper::Ext
- DataMapper::Ext::Array
- DataMapper::Ext::Hash
- DataMapper::Ext::Module
- DataMapper::Ext::Object
- DataMapper::Ext::String
- DataMapper::Hook
- DataMapper::Hook::ClassMethods
- DataMapper::IdentityMap
- DataMapper::ImmutableDeletedError
- DataMapper::ImmutableError
- DataMapper::IncompleteModelError
- DataMapper::Inflector
- DataMapper::Inflector::Inflections
- DataMapper::LocalObjectSpace
- DataMapper::Logger
- DataMapper::Mash
- DataMapper::Model
- DataMapper::Model::Hook
- DataMapper::Model::Hook::MethodCommand
- DataMapper::Model::Hook::Methods
- DataMapper::Model::Hook::ProcCommand
- DataMapper::Model::Is
- DataMapper::Model::Property
- DataMapper::Model::Relationship
- DataMapper::Model::Scope
- DataMapper::NamingConventions
- DataMapper::NamingConventions::Field
- DataMapper::NamingConventions::Field::Underscored
- DataMapper::NamingConventions::Field::UnderscoredAndPluralized
- DataMapper::NamingConventions::Field::UnderscoredAndPluralizedWithoutModule
- DataMapper::NamingConventions::Field::Yaml
- DataMapper::NamingConventions::Resource
- DataMapper::NamingConventions::Resource::Underscored
- DataMapper::NamingConventions::Resource::UnderscoredAndPluralized
- DataMapper::NamingConventions::Resource::UnderscoredAndPluralizedWithoutLeadingModule
- DataMapper::NamingConventions::Resource::UnderscoredAndPluralizedWithoutModule
- DataMapper::NamingConventions::Resource::Yaml
- DataMapper::ObjectNotFoundError
- DataMapper::OrderedSet
- DataMapper::OrderedSet::Cache
- DataMapper::OrderedSet::Cache::API
- DataMapper::PersistenceError
- DataMapper::PluginNotFoundError
- DataMapper::Property
- DataMapper::Property::Binary
- DataMapper::Property::Boolean
- DataMapper::Property::Class
- DataMapper::Property::Date
- DataMapper::Property::DateTime
- DataMapper::Property::Decimal
- DataMapper::Property::Discriminator
- DataMapper::Property::Discriminator::Model
- DataMapper::Property::Float
- DataMapper::Property::Integer
- DataMapper::Property::Lookup
- DataMapper::Property::Numeric
- DataMapper::Property::Object
- DataMapper::Property::PassThroughLoadDump
- DataMapper::Property::Serial
- DataMapper::Property::String
- DataMapper::Property::Text
- DataMapper::Property::Time
- DataMapper::Property::Typecast
- DataMapper::Property::Typecast::Numeric
- DataMapper::Property::Typecast::Time
- DataMapper::PropertySet
- DataMapper::Query
- DataMapper::Query::Conditions
- DataMapper::Query::Conditions::AbstractComparison
- DataMapper::Query::Conditions::AbstractOperation
- DataMapper::Query::Conditions::AndOperation
- DataMapper::Query::Conditions::Comparison
- DataMapper::Query::Conditions::EqualToComparison
- DataMapper::Query::Conditions::FlattenOperation
- DataMapper::Query::Conditions::GreaterThanComparison
- DataMapper::Query::Conditions::GreaterThanOrEqualToComparison
- DataMapper::Query::Conditions::InclusionComparison
- DataMapper::Query::Conditions::LessThanComparison
- DataMapper::Query::Conditions::LessThanOrEqualToComparison
- DataMapper::Query::Conditions::LikeComparison
- DataMapper::Query::Conditions::NotOperation
- DataMapper::Query::Conditions::NullOperation
- DataMapper::Query::Conditions::Operation
- DataMapper::Query::Conditions::OrOperation
- DataMapper::Query::Conditions::RegexpComparison
- DataMapper::Query::Conditions::RelationshipHandler
- DataMapper::Query::Direction
- DataMapper::Query::Operator
- DataMapper::Query::Path
- DataMapper::Query::Sort
- DataMapper::RelationshipSet
- DataMapper::Repository
- DataMapper::RepositoryNotSetupError
- DataMapper::Resource
- DataMapper::Resource::PersistenceState
- DataMapper::Resource::PersistenceState::Clean
- DataMapper::Resource::PersistenceState::Deleted
- DataMapper::Resource::PersistenceState::Dirty
- DataMapper::Resource::PersistenceState::Immutable
- DataMapper::Resource::PersistenceState::Persisted
- DataMapper::Resource::PersistenceState::Transient
- DataMapper::SaveFailureError
- DataMapper::Spec
- DataMapper::Spec::Adapters
- DataMapper::Spec::Adapters::Adapter
- DataMapper::Spec::Adapters::Helpers
- DataMapper::Spec::Adapters::InMemoryAdapter
- DataMapper::Spec::CollectionHelpers
- DataMapper::Spec::CollectionHelpers::GroupMethods
- DataMapper::Spec::Helpers
- DataMapper::Spec::PendingHelpers
- DataMapper::Subject
- DataMapper::SubjectSet
- DataMapper::SubjectSet::NameCache
- DataMapper::Undefined
- DataMapper::UnknownRelationshipError
- DataMapper::UnsavedParentError
- DataMapper::UpdateConflictError
- Blog
- Blog::Article
- ChildSubType
- CounterAdapter
- Heffalump
- Kernel
- LazyArray
- Object
- OtherComparison
- Pathname
- SubType
- Symbol

No matching classes.

# DataMapper::Associations::Relationship

Base class for relationships. Each type of relationship (1 to 1, 1 to n, n to m) implements a subclass of this class with methods like get and set overridden.

### Constants

OPTIONS

### Attributes

child_repository_name[R]

Repository from where child objects are loaded

@api semipublic

instance_variable_name[R]

ivar used to store collection of child options in source

@example for :commits association in

    class VersionControl::Branch
      # ...

      has n, :commits
    end

instance variable name for source will be @commits

@api semipublic

max[R]

Maximum number of child objects for relationship

@example for :fouls association in

    class Basketball::Player
      # ...

      has 0..5, :fouls
    end

maximum is 5

@api semipublic

min[R]

Minimum number of child objects for relationship

@example for :cores association in

    class CPU::Multicore
      # ...

      has 2..n, :cores
    end

minimum is 2

@api semipublic

name[R]

Relationship name

@example for :parent association in

    class VersionControl::Commit
      # ...

      belongs_to :parent
    end

name is :parent

@api semipublic

options[R]

Options used to set up association of this relationship

@example for :author association in

    class VersionControl::Commit
      # ...

      belongs_to :author, :model => 'Person'
    end

options is a hash with a single key, :model

@api semipublic

parent_repository_name[R]

Repository from where parent objects are loaded

@api semipublic

query[R]

Returns query options for relationship.

For this base class, always returns query options has been initialized with. Overriden in subclasses.

@api private

reader_visibility[R]

Returns the visibility for the source accessor

@return [Symbol]

    the visibility for the accessor added to the source

@api semipublic

writer_visibility[R]

Returns the visibility for the source mutator

@return [Symbol]

    the visibility for the mutator added to the source

@api semipublic

### Public Class Methods

new(name, child_model, parent_model, options = {}) click to toggle source

Initializes new Relationship: sets attributes of relationship from options as well as conventions: for instance, @ivar name for association is constructed by prefixing @ to association name.

Once attributes are set, reader and writer are created for the resource association belongs to

@api semipublic

    # File lib/dm-core/associations/relationship.rb, line 446
    def initialize(name, child_model, parent_model, options = {})
      initialize_object_ivar('child_model',  child_model)
      initialize_object_ivar('parent_model', parent_model)

      @name                   = name
      @instance_variable_name = "@#{@name}".freeze
      @options                = options.dup.freeze
      @child_repository_name  = @options[:child_repository_name]
      @parent_repository_name = @options[:parent_repository_name]

      unless @options[:child_key].nil?
        @child_properties     = DataMapper::Ext.try_dup(@options[:child_key]).freeze
      end
      unless @options[:parent_key].nil?
        @parent_properties    = DataMapper::Ext.try_dup(@options[:parent_key]).freeze
      end

      @min                    = @options[:min]
      @max                    = @options[:max]
      @reader_visibility      = @options.fetch(:reader_visibility, :public)
      @writer_visibility      = @options.fetch(:writer_visibility, :public)
      @default                = @options.fetch(:default, nil)

      # TODO: normalize the @query to become :conditions => AndOperation
      #  - Property/Relationship/Path should be left alone
      #  - Symbol/String keys should become a Property, scoped to the target_repository and target_model
      #  - Extract subject (target) from Operator
      #    - subject should be processed same as above
      #  - each subject should be transformed into AbstractComparison
      #    object with the subject, operator and value
      #  - transform into an AndOperation object, and return the
      #    query as :condition => and_object from self.query
      #  - this should provide the best performance

      @query = DataMapper::Ext::Hash.except(@options, *self.class::OPTIONS).freeze
    end

### Public Instance Methods

==(other) click to toggle source

Compares another Relationship for equivalency

@param [Relationship] other

    the other Relationship to compare with

@return [Boolean]

    true if they are equal, false if not

@api public

    # File lib/dm-core/associations/relationship.rb, line 368
    def ==(other)
      return true  if equal?(other)
      other.respond_to?(:cmp_repository?, true) &&
      other.respond_to?(:cmp_model?, true)      &&
      other.respond_to?(:cmp_key?, true)        &&
      other.respond_to?(:min)                   &&
      other.respond_to?(:max)                   &&
      other.respond_to?(:query)                 &&
      cmp?(other, :==)
    end

child_key() click to toggle source

Returns a set of keys that identify the target model

@return [PropertySet]

    a set of properties that identify the target model

@api semipublic

    # File lib/dm-core/associations/relationship.rb, line 195
    def child_key
      return @child_key if defined?(@child_key)

      repository_name = child_repository_name || parent_repository_name
      properties      = child_model.properties(repository_name)

      @child_key = if @child_properties
        child_key = properties.values_at(*@child_properties)
        properties.class.new(child_key).freeze
      else
        properties.key
      end
    end

Also aliased as: relationship_child_key
child_model() click to toggle source

Returns model class used by child side of the relationship

@return [Resource]

    Model for association child

@api private

    # File lib/dm-core/associations/relationship.rb, line 168
    def child_model
      return @child_model if defined?(@child_model)
      child_model_name = self.child_model_name
      @child_model = DataMapper::Ext::Module.find_const(@parent_model || Object, child_model_name)
    rescue NameError
      raise NameError, "Cannot find the child_model #{child_model_name} for #{parent_model_name} in #{name}"
    end

child_model?() click to toggle source

@api private

    # File lib/dm-core/associations/relationship.rb, line 177
    def child_model?
      child_model
      true
    rescue NameError
      false
    end

child_model_name() click to toggle source

@api private

    # File lib/dm-core/associations/relationship.rb, line 185
    def child_model_name
      @child_model ? child_model.name : @child_model_name
    end

eager_load(source, query = nil) click to toggle source

Eager load the collection using the source as a base

@param [Collection] source

    the source collection to query with

@param [Query, Hash] query

    optional query to restrict the collection

@return [Collection]

    the loaded collection for the source

@api private

    # File lib/dm-core/associations/relationship.rb, line 307
    def eager_load(source, query = nil)
      targets = source.model.all(query_for(source, query))

      # FIXME: cannot associate targets to m:m collection yet
      if source.loaded? && !source.kind_of?(ManyToMany::Collection)
        associate_targets(source, targets)
      end

      targets
    end

eql?(other) click to toggle source

Compares another Relationship for equality

@param [Relationship] other

    the other Relationship to compare with

@return [Boolean]

    true if they are equal, false if not

@api public

    # File lib/dm-core/associations/relationship.rb, line 354
    def eql?(other)
      return true if equal?(other)
      instance_of?(other.class) && cmp?(other, :eql?)
    end

field() click to toggle source

Returns the String the Relationship would use in a Hash

@return [String]

    String name for the Relationship

@api private

    # File lib/dm-core/associations/relationship.rb, line 131
    def field
      name.to_s
    end

get(resource, other_query = nil) click to toggle source

Loads and returns "other end" of the association. Must be implemented in subclasses.

@api semipublic

    # File lib/dm-core/associations/relationship.rb, line 266
    def get(resource, other_query = nil)
      raise NotImplementedError, "#{self.class}#get not implemented"
    end

get!(resource) click to toggle source

Gets "other end" of the association directly as @ivar on given resource. Subclasses usually use implementation of this class.

@api semipublic

    # File lib/dm-core/associations/relationship.rb, line 275
    def get!(resource)
      resource.instance_variable_get(instance_variable_name)
    end

hash() click to toggle source

@api private

    # File lib/dm-core/associations/relationship.rb, line 416
    def hash
      self.class.hash             ^
      name.hash                   ^
      child_repository_name.hash  ^
      parent_repository_name.hash ^
      child_model.hash            ^
      parent_model.hash           ^
      child_properties.hash       ^
      parent_properties.hash      ^
      min.hash                    ^
      max.hash                    ^
      query.hash
    end

inverse() click to toggle source

Get the inverse relationship from the target model

@api semipublic

    # File lib/dm-core/associations/relationship.rb, line 382
    def inverse
      return @inverse if defined?(@inverse)

      @inverse = options[:inverse]

      if kind_of_inverse?(@inverse)
        return @inverse
      end

      relationships = target_model.relationships(relative_target_repository_name)

      @inverse = relationships.detect { |relationship| inverse?(relationship) } ||
        invert

      @inverse.child_key

      @inverse
    end

loaded?(resource) click to toggle source

Checks if "other end" of association is loaded on given resource.

@api semipublic

    # File lib/dm-core/associations/relationship.rb, line 322
    def loaded?(resource)
      resource.instance_variable_defined?(instance_variable_name)
    end

parent_key() click to toggle source

Returns a set of keys that identify parent model

@return [PropertySet]

    a set of properties that identify parent model

@api private

    # File lib/dm-core/associations/relationship.rb, line 248
    def parent_key
      return @parent_key if defined?(@parent_key)

      repository_name = parent_repository_name || child_repository_name
      properties      = parent_model.properties(repository_name)

      @parent_key = if @parent_properties
        parent_key = properties.values_at(*@parent_properties)
        properties.class.new(parent_key).freeze
      else
        properties.key
      end
    end

parent_model() click to toggle source

Returns model class used by parent side of the relationship

@return [Resource]

    Class of association parent

@api private

    # File lib/dm-core/associations/relationship.rb, line 221
    def parent_model
      return @parent_model if defined?(@parent_model)
      parent_model_name = self.parent_model_name
      @parent_model = DataMapper::Ext::Module.find_const(@child_model || Object, parent_model_name)
    rescue NameError
      raise NameError, "Cannot find the parent_model #{parent_model_name} for #{child_model_name} in #{name}"
    end

parent_model?() click to toggle source

@api private

    # File lib/dm-core/associations/relationship.rb, line 230
    def parent_model?
      parent_model
      true
    rescue NameError
      false
    end

parent_model_name() click to toggle source

@api private

    # File lib/dm-core/associations/relationship.rb, line 238
    def parent_model_name
      @parent_model ? parent_model.name : @parent_model_name
    end

query_for(source, other_query = nil) click to toggle source

Creates and returns Query instance that fetches target resource(s) (ex.: articles) for given target resource (ex.: author)

@api semipublic

    # File lib/dm-core/associations/relationship.rb, line 150
    def query_for(source, other_query = nil)
      repository_name = relative_target_repository_name_for(source)

      DataMapper.repository(repository_name).scope do
        query = target_model.query.dup
        query.update(self.query)
        query.update(:conditions => source_scope(source))
        query.update(other_query) if other_query
        query.update(:fields => query.fields | target_key)
      end
    end

relative_target_repository_name() click to toggle source

@api private

    # File lib/dm-core/associations/relationship.rb, line 402
    def relative_target_repository_name
      target_repository_name || source_repository_name
    end

relative_target_repository_name_for(source) click to toggle source

@api private

    # File lib/dm-core/associations/relationship.rb, line 407
    def relative_target_repository_name_for(source)
      target_repository_name || if source.respond_to?(:repository)
        source.repository.name
      else
        source_repository_name
      end
    end

set(resource, association) click to toggle source

Sets value of the "other end" of association on given resource. Must be implemented in subclasses.

@api semipublic

    # File lib/dm-core/associations/relationship.rb, line 283
    def set(resource, association)
      raise NotImplementedError, "#{self.class}#set not implemented"
    end

set!(resource, association) click to toggle source

Sets "other end" of the association directly as @ivar on given resource. Subclasses usually use implementation of this class.

@api semipublic

    # File lib/dm-core/associations/relationship.rb, line 292
    def set!(resource, association)
      resource.instance_variable_set(instance_variable_name, association)
    end

source_scope(source) click to toggle source

Returns a hash of conditions that scopes query that fetches target object

@return [Hash]

    Hash of conditions that scopes query

@api private

    # File lib/dm-core/associations/relationship.rb, line 142
    def source_scope(source)
      { inverse => source }
    end

valid?(value, negated = false) click to toggle source

Test the resource to see if it is a valid target

@param [Object] source

    the resource or collection to be tested

@return [Boolean]

    true if the resource is valid

@api semipulic

    # File lib/dm-core/associations/relationship.rb, line 335
    def valid?(value, negated = false)
      case value
        when Enumerable then valid_target_collection?(value, negated)
        when Resource   then valid_target?(value)
        when nil        then true
        else
          raise ArgumentError, "+value+ should be an Enumerable, Resource or nil, but was a #{value.class.name}"
      end
    end

[Validate]

Generated with the Darkfish Rdoc Generator 2.