Parent

Class/Module Index [+]

Quicksearch

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.