Module: ActiveFedora::SemanticNode::ClassMethods

Defined in:
lib/active_fedora/semantic_node.rb

Instance Method Summary collapse

Instance Method Details

#create_bidirectional_relationship_finders(name, outbound_predicate, inbound_predicate, opts = {}) ⇒ Object

Generates relationship finders for predicates that point in both directions and registers predicate relationships for each direction.

Parameters:

  • name (String)

    Name of the relationship method(s) to create

  • outbound_predicate (Symbol)

    Predicate used in outbound relationships

  • inbound_predicate (Symbol)

    Predicate used in inbound relationships

  • opts (Hash) (defaults to: {})

    (optional)



340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
# File 'lib/active_fedora/semantic_node.rb', line 340

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
  def #{name}(opts={})
    opts = {:rows=>25}.merge(opts)
    if opts[:response_format] == :solr || opts[:response_format] == :load_from_solr
      outbound_id_array = []
      predicate = outbound_relationship_predicates["#{name}_outbound"]
      if !outbound_relationships[predicate].nil? 
        outbound_relationships[predicate].each do |rel|
          outbound_id_array << rel.gsub("info:fedora/", "")
        end
      end
      #outbound_id_array = #{outbound_method_name}(:response_format=>:id_array)
      query = self.class.bidirectional_relationship_query(self.pid,"#{name}",outbound_id_array)
      solr_result = SolrService.instance.conn.query(query, :rows=>opts[:rows])
      
      if opts[:response_format] == :solr
        return solr_result
      elsif opts[:response_format] == :load_from_solr || self.load_from_solr
        return ActiveFedora::SolrService.reify_solr_results(solr_result,{:load_from_solr=>true})
      else
        return ActiveFedora::SolrService.reify_solr_results(solr_result)
      end
    else
      ary = #{inbound_method_name}(opts) + #{outbound_method_name}(opts)
      return ary.uniq
    end
  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

#create_inbound_relationship_finders(name, predicate, opts = {}) ⇒ Object



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/active_fedora/semantic_node.rb', line 257

def create_inbound_relationship_finders(name, predicate, opts = {})
  class_eval <<-END
  def #{name}(opts={})
    opts = {:rows=>25}.merge(opts)
    query = self.class.inbound_relationship_query(self.pid,"#{name}")
    return [] if query.empty?
    solr_result = SolrService.instance.conn.query(query, :rows=>opts[:rows])
    if opts[:response_format] == :solr
      return solr_result
    else
      if opts[:response_format] == :id_array
        id_array = []
        solr_result.hits.each do |hit|
          id_array << hit[SOLR_DOCUMENT_ID]
        end
        return id_array
      elsif opts[:response_format] == :load_from_solr || self.load_from_solr
        return ActiveFedora::SolrService.reify_solr_results(solr_result,{:load_from_solr=>true})
      else
        return ActiveFedora::SolrService.reify_solr_results(solr_result)
      end
    end
  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

#create_outbound_relationship_finders(name, predicate, opts = {}) ⇒ Object



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
# File 'lib/active_fedora/semantic_node.rb', line 292

def create_outbound_relationship_finders(name, predicate, opts = {})
  class_eval <<-END
  def #{name}(opts={})
    id_array = []
    if !outbound_relationships[#{predicate.inspect}].nil? 
      outbound_relationships[#{predicate.inspect}].each do |rel|
        id_array << rel.gsub("info:fedora/", "")
      end
    end
    if opts[:response_format] == :id_array && !self.class.relationship_has_solr_filter_query?(:self,"#{name}")
      return id_array
    else
      query = self.class.outbound_relationship_query("#{name}",id_array)
      solr_result = SolrService.instance.conn.query(query)
      if opts[:response_format] == :solr
        return solr_result
      elsif opts[:response_format] == :id_array
        id_array = []
        solr_result.hits.each do |hit|
          id_array << hit[SOLR_DOCUMENT_ID]
        end
        return id_array
      elsif opts[:response_format] == :load_from_solr || self.load_from_solr
        return ActiveFedora::SolrService.reify_solr_results(solr_result,{:load_from_solr=>true})
      else
        return ActiveFedora::SolrService.reify_solr_results(solr_result)
      end
    end
  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

#default_predicate_namespaceObject



456
457
458
# File 'lib/active_fedora/semantic_node.rb', line 456

def default_predicate_namespace
  predicate_config[:default_namespace]
end

#find_predicate(predicate) ⇒ Object



460
461
462
463
464
465
466
467
# File 'lib/active_fedora/semantic_node.rb', line 460

def find_predicate(predicate)
  predicate_mappings.each do |namespace,predicates|
    if predicates.fetch(predicate,nil)
      return predicates[predicate], namespace
    end
  end
  raise ActiveFedora::UnregisteredPredicateError
end

#has_bidirectional_relationship(name, outbound_predicate, inbound_predicate, opts = {}) ⇒ Object

Generates relationship finders for predicates that point in both directions

Example:

has_bidirectional_relationship("parts", :has_part, :is_part_of)

will create three instance methods: parts_outbound, and parts_inbound and parts the inbound and outbound methods are the same that would result from calling create_inbound_relationship_finders and create_outbound_relationship_finders The third method combines the results of both and handles generating appropriate solr queries where necessary.

Parameters:

  • name (String)

    of the relationship method(s) to create

  • outbound_predicate (Symbol)

    Predicate used in outbound relationships

  • inbound_predicate (Symbol)

    Predicate used in inbound relationships

  • opts (Hash) (defaults to: {})


253
254
255
# File 'lib/active_fedora/semantic_node.rb', line 253

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

#has_relationship(name, predicate, opts = {}) ⇒ Object

Allows for a relationship to be treated like any other attribute of a model class. You define relationships in your model class using this method. You then have access to several helper methods to list, append, and remove objects from the list of relationships.

Examples to define two relationships

class AudioRecord < ActiveFedora::Base

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

The first two parameters are required:

name: relationship name
predicate: predicate for the relationship
opts:
  possible parameters  
    :inbound => if true loads an external relationship via Solr (defaults to false)
    :type => The type of model to use when instantiated an object from the pid in this relationship (defaults to ActiveFedora::Base)
    :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)

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

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 and one is outbound as shown in the example above. A full list of possible predicates are defined by predicate_mappings

For the oral_history relationship in the example above the following helper methods are created:

oral_history: returns array of OralHistory objects that have this AudioRecord with predicate :has_part 
oral_history_ids: Return array of pids for OralHistory objects that have this AudioRecord with predicate :has_part
oral_history_query: Return solr query that can be used to retrieve related objects as solr documents

For the outbound relationship “similar_audio” there are two additional methods to append and remove objects from that relationship since it is managed internally:

similar_audio: Return array of AudioRecord objects that have been added to similar_audio relationship
similar_audio_ids:  Return array of AudioRecord object pids that have been added to similar_audio relationship
similar_audio_query: Return solr query that can be used to retrieve related objects as solr documents
similar_audio_append: Add an AudioRecord object to the similar_audio relationship
similar_audio_remove: Remove an AudioRecord from the similar_audio relationship


222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/active_fedora/semantic_node.rb', line 222

def has_relationship(name, predicate, opts = {})
  opts = {:singular => nil, :inbound => false}.merge(opts)
  if opts[:inbound] == true
    #raise "Duplicate use of predicate for inbound relationship name not allowed" if predicate_exists_with_different_relationship_name?(:inbound,name,predicate)
    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 outbound relationship name not allowed" if predicate_exists_with_different_relationship_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

#predicate_configObject



448
449
450
# File 'lib/active_fedora/semantic_node.rb', line 448

def predicate_config
  @@predicate_config ||= YAML::load(File.open(ActiveFedora.predicate_config)) if File.exist?(ActiveFedora.predicate_config)
end

#predicate_lookup(predicate, namespace = "info:fedora/fedora-system:def/relations-external#") ⇒ Object

If predicate is a symbol, looks up the predicate in the predicate_mappings If predicate is not a Symbol, returns the predicate untouched

Raises:

  • UnregisteredPredicateError if the predicate is a symbol but is not found in the predicate_mappings



437
438
439
440
441
442
443
444
445
446
# File 'lib/active_fedora/semantic_node.rb', line 437

def predicate_lookup(predicate,namespace="info:fedora/fedora-system:def/relations-external#")
  if predicate.class == Symbol 
    if predicate_mappings[namespace].has_key?(predicate)
      return predicate_mappings[namespace][predicate]
    else
      raise ActiveFedora::UnregisteredPredicateError
    end
  end
  return predicate
end

#predicate_mappingsObject



452
453
454
# File 'lib/active_fedora/semantic_node.rb', line 452

def predicate_mappings
  predicate_config[:predicate_mapping]
end

#register_predicate(subject, predicate) ⇒ Object



402
403
404
405
406
407
# File 'lib/active_fedora/semantic_node.rb', line 402

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

#register_subject(subject) ⇒ Object



396
397
398
399
400
# File 'lib/active_fedora/semantic_node.rb', line 396

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

#relationshipsObject

relationships are tracked as a hash of structure

Examples:

ds.relationships # => {:self=>{:has_model=>["afmodel:SimpleThing"],:has_part=>["demo:20"]},:inbound=>{:is_part_of=>["demo:6"]} 


391
392
393
# File 'lib/active_fedora/semantic_node.rb', line 391

def relationships
  @class_relationships ||= Hash[:self => {}]
end

#relationships_to_rels_ext(pid, relationships = self.relationships) ⇒ Object

Creates a RELS-EXT datastream for insertion into a Fedora Object Note: This method is implemented on SemanticNode instead of RelsExtDatastream because SemanticNode contains the relationships array

Parameters:

  • pid (String)

    of the object that the RELS-EXT datastream belongs to

  • relationships (Hash) (defaults to: self.relationships)

    the relationships hash to transform into RELS-EXT RDF. @default the object’s relationships hash



415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
# File 'lib/active_fedora/semantic_node.rb', line 415

def relationships_to_rels_ext(pid, relationships=self.relationships)
  starter_xml = <<-EOL
  <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
    <rdf:Description rdf:about="info:fedora/#{pid}">
    </rdf:Description>
  </rdf:RDF>
  EOL
  xml = REXML::Document.new(starter_xml)

  # Iterate through the hash of predicates, adding an element to the RELS-EXT for each "object" in the predicate's corresponding array.
  self.outbound_relationships.each do |predicate, targets_array|
    targets_array.each do |target|
      #puts ". #{predicate} #{target}"
      xml.root.elements["rdf:Description"].add_element(predicate_lookup(predicate), {"xmlns" => "info:fedora/fedora-system:def/relations-external#", "rdf:resource"=>target})
    end
  end
  xml.to_s
end