module ActiveFedora::Relationships::ClassMethods

def bidirectional_relationship_query(pid,relationship_name,outbound_pids)

Returns:
  • (String) -

Parameters:
  • An (Array) -- array of pids to include in the query
  • The (String) -- name of the relationship defined in the model
  • The (String) -- pid for the object that has these inbound relationships
def bidirectional_relationship_query(pid,relationship_name,outbound_pids)
  outbound_query = outbound_relationship_query("#{relationship_name}_outbound",outbound_pids) 
  inbound_query = inbound_relationship_query(pid,"#{relationship_name}_inbound")
  query = outbound_query # use outbound_query by default
  if !inbound_query.empty?
    query << " OR (" + inbound_relationship_query(pid,"#{relationship_name}_inbound") + ")"
  end
  return query      
end

def create_bidirectional_relationship_finders(name, outbound_predicate, inbound_predicate, opts={})

Parameters:
  • opts (Hash) -- (optional)
  • inbound_predicate (Symbol) -- Predicate used in inbound relationships
  • outbound_predicate (Symbol) -- Predicate used in outbound relationships
  • name (String) -- Name of the relationship method(s) to create
def create_bidirectional_relationship_finders(name, outbound_predicate, inbound_predicate, opts={})
  inbound_method_name = name.to_s+"_inbound"
  outbound_method_name = name.to_s+"_outbound"
  has_relationship(outbound_method_name, outbound_predicate, opts)
  has_relationship(inbound_method_name, inbound_predicate, opts.merge!(:inbound=>true))
  #create methods that mirror the outbound append and remove with our bidirectional name, assume just add and remove locally        
  create_bidirectional_relationship_name_methods(name,outbound_method_name)
  class_eval <<-END, __FILE__, __LINE__
  def #{name}(opts={})
    load_bidirectional("#{name}", :#{inbound_method_name}, :#{outbound_method_name}, opts)
  end
  def #{name}_ids
    #{name}(:response_format => :id_array)
  end
  def #{name}_from_solr
    #{name}(:response_format => :load_from_solr)
  end
  def #{name}_query
    relationship_query("#{name}")
  end
  END
end

def create_bidirectional_relationship_name_methods(name,outbound_name)

Parameters:
  • outbound (String) -- relationship method name associated with the bidirectional relationship ([bidirectional_name]_outbound)
  • bidirectional (String) -- relationship name
def create_bidirectional_relationship_name_methods(name,outbound_name)
  append_method_name = "#{name.to_s.downcase}_append"
  remove_method_name = "#{name.to_s.downcase}_remove"
  self.send(:define_method,:"#{append_method_name}") {|object| add_relationship_by_name(outbound_name,object)}
  self.send(:define_method,:"#{remove_method_name}") {|object| remove_relationship_by_name(outbound_name,object)}
end

def create_inbound_relationship_finders(name, predicate, opts = {})

def create_inbound_relationship_finders(name, predicate, opts = {})
  class_eval <<-END, __FILE__, __LINE__
  def #{name}(opts={})
    load_inbound_relationship('#{name}', '#{predicate}', opts)
  end
  def #{name}_ids
    #{name}(:response_format => :id_array)
  end
  def #{name}_from_solr
    #{name}(:response_format => :load_from_solr)
  end
  def #{name}_query
    relationship_query("#{name}")
  end
  END
end

def create_outbound_relationship_finders(name, predicate, opts = {})

def create_outbound_relationship_finders(name, predicate, opts = {})
  class_eval <<-END, __FILE__, __LINE__
  def #{name}(opts={})
    load_outbound_relationship(#{name.inspect}, #{predicate.inspect}, opts)
  end
  def #{name}_ids
    #{name}(:response_format => :id_array)
  end
  def #{name}_from_solr
    #{name}(:response_format => :load_from_solr)
  end
  def #{name}_query
    relationship_query("#{name}")
  end
  END
end

def create_relationship_name_methods(name)

Parameters:
  • relationship (String) -- name to create helper methods for
def create_relationship_name_methods(name)
  append_method_name = "#{name.to_s.downcase}_append"
  remove_method_name = "#{name.to_s.downcase}_remove"
  self.send(:define_method,:"#{append_method_name}") {|object| add_relationship_by_name(name,object)}
  self.send(:define_method,:"#{remove_method_name}") {|object| remove_relationship_by_name(name,object)}
end 

def has_bidirectional_relationship(name, outbound_predicate, inbound_predicate, opts={})

Parameters:
  • opts (Hash) --
  • inbound_predicate (Symbol) -- Predicate used in inbound relationships
  • outbound_predicate (Symbol) -- Predicate used in outbound relationships
  • name (String) -- of the relationship method(s) to create
def has_bidirectional_relationship(name, outbound_predicate, inbound_predicate, opts={})
  create_bidirectional_relationship_finders(name, outbound_predicate, inbound_predicate, opts)
end

def has_relationship(name, predicate, opts = {})

similar_audio_remove: Remove an AudioRecord from the similar_audio relationship
similar_audio_append: Add an AudioRecord object to the similar_audio relationship
similar_audio_query: Return solr query that can be used to retrieve related objects as solr documents
similar_audio_ids: Return array of AudioRecord object pids that have been added to similar_audio relationship
similar_audio: Return array of AudioRecord objects that have been added to similar_audio relationship
since it is managed internally:
For the outbound relationship "similar_audio" there are two additional methods to append and remove objects from that relationship

oral_history_query: Return solr query that can be used to retrieve related objects as solr documents
oral_history_ids: Return array of pids for OralHistory objects that have this AudioRecord with predicate :has_part
oral_history: returns array of OralHistory objects that have this AudioRecord with predicate :has_part
For the oral_history relationship in the example above the following helper methods are created:

and one is outbound as shown in the example above. A full list of possible predicates are defined by predicate_mappings
Word of caution - The same predicate may not be used twice for two inbound or two outbound relationships. However, it may be used twice if one is inbound

this object's RELS-EXT datastream
and to load that relationship from Solr. Otherwise, if inbound is true the relationship is stored in
If inbound is true it expects the relationship to be defined by another object's RELS-EXT

:solr_fq => Define a solr query here if you want to filter out some objects in your relationship (must be a properly formatted solr query)
:type => The type of model to use when instantiated an object from the pid in this relationship (defaults to ActiveFedora::Base)
:inbound => if true loads an external relationship via Solr (defaults to false)
possible parameters
opts:
predicate: predicate for the relationship
name: relationship name
The first two parameters are required:

has_relationship "similar_audio_wav", :has_part, :solr_fq=>"format_t:wav"
#returns only similar audio with format wav
has_relationship "similar_audio", :has_part, :type=>AudioRecord
# returns all similar audio
has_relationship "oral_history", :has_part, :inbound=>true, :type=>OralHistory

class AudioRecord < ActiveFedora::Base
====Examples to define two relationships
helper methods to list, append, and remove objects from the list of relationships.
relationships in your model class using this method. You then have access to several
Allows for a relationship to be treated like any other attribute of a model class. You define
def has_relationship(name, predicate, opts = {})
  opts = {:singular => nil, :inbound => false}.merge(opts)
  if opts[:inbound] == true
    register_relationship_desc(:inbound, name, predicate, opts)
    register_predicate(:inbound, predicate)
    create_inbound_relationship_finders(name, predicate, opts)
  else
    #raise "Duplicate use of predicate for named outbound relationship \"#{predicate.inspect}\" not allowed" if named_predicate_exists_with_different_name?(:self,name,predicate)
    register_relationship_desc(:self, name, predicate, opts)
    register_predicate(:self, predicate)
    create_relationship_name_methods(name)
    create_outbound_relationship_finders(name, predicate, opts)
  end
end

def inbound_relationship_query(pid,relationship_name)

Returns:
  • (String) -

Parameters:
  • The (String) -- name of the relationship defined in the model
  • The (String) -- pid for the object that has these inbound relationships
def inbound_relationship_query(pid,relationship_name)
  query = ""
  subject = :inbound
  if relationships_desc.has_key?(subject) && relationships_desc[subject].has_key?(relationship_name)
    predicate = relationships_desc[subject][relationship_name][:predicate]
    internal_uri = "info:fedora/#{pid}"
    escaped_uri = internal_uri.gsub(/(:)/, '\\:')
    query = "#{predicate}_s:#{escaped_uri}" 
    if relationships_desc.has_key?(subject) && relationships_desc[subject].has_key?(relationship_name) && relationships_desc[subject][relationship_name].has_key?(:solr_fq)
      solr_fq = relationships_desc[subject][relationship_name][:solr_fq]
      query << " AND " unless query.empty?
      query << solr_fq
    end
  end
  query
end

def is_bidirectional_relationship?(relationship_name)

Returns:
  • (Boolean) -

Parameters:
  • relationship (String) -- name to test
def is_bidirectional_relationship?(relationship_name)
  (relationships_desc.has_key?(:self)&&relationships_desc.has_key?(:inbound)&&relationships_desc[:self].has_key?("#{relationship_name}_outbound") && relationships_desc[:inbound].has_key?("#{relationship_name}_inbound")) 
end

def outbound_relationship_query(relationship_name,outbound_pids)

Returns:
  • (String) -

Parameters:
  • An (Array) -- array of pids to include in the query
  • The (String) -- name of the relationship defined in the model
def outbound_relationship_query(relationship_name,outbound_pids)
  query = ActiveFedora::SolrService.construct_query_for_pids(outbound_pids)
  subject = :self
  if relationships_desc.has_key?(subject) && relationships_desc[subject].has_key?(relationship_name) && relationships_desc[subject][relationship_name].has_key?(:solr_fq)
    solr_fq = relationships_desc[subject][relationship_name][:solr_fq]
    unless query.empty?
      #substitute in the filter query for each pid so that it is applied to each in the query
      query_parts = query.split(/OR/)
      query = ""
      query_parts.each_with_index do |query_part,index|
        query_part.strip!
        query << " OR " if index > 0
        query << "(#{query_part} AND #{solr_fq})"
      end
    else
      query = solr_fq
    end
  end
  query
end

def register_predicate(subject, predicate)

def register_predicate(subject, predicate)
  register_subject(subject)
  if !relationships[subject].has_key?(predicate) 
    relationships[subject][predicate] = []
  end
end

def register_relationship_desc(subject, name, predicate, opts={})

Parameters:
  • Any (Hash) -- options passed to has_relationship such as :type, :solr_fq, etc.
  • Fedora (Symbol) -- ontology predicate to use
  • Name (String) -- of relationship being registered
  • Subject (Symbol) -- name to register
def register_relationship_desc(subject, name, predicate, opts={})
  register_relationship_desc_subject(subject)
  opts.merge!({:predicate=>predicate})
  relationships_desc[subject][name] = opts
end

def register_relationship_desc_subject(subject)

Parameters:
  • Subject (Symbol) -- name to register (will probably be something like :self or :inbound)
def register_relationship_desc_subject(subject)
  unless relationships_desc.has_key?(subject) 
    relationships_desc[subject] = {} 
  end
end

def register_subject(subject)

def register_subject(subject)
  if !relationships.has_key?(subject) 
      relationships[subject] = {} 
  end
end

def relationships

ds.relationships # => {:self=>{:has_model=>["afmodel:SimpleThing"],:has_part=>["demo:20"]},:inbound=>{:is_part_of=>["demo:6"]}
@example
relationships are tracked as a hash of structure
def relationships
  @class_relationships ||= Hash[:self => {}]
end