active-fedora 1.1.13 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -42,11 +42,18 @@ module ActiveFedora
42
42
  # ds.update_attributes({:myfield=>{"0"=>"a","1"=>"b"},:myotherfield=>{"-1"=>"c"}})
43
43
  #
44
44
  def update_indexed_attributes(params={}, opts={})
45
+
46
+ ##FIX this bug, it should delete it from a copy of params in case passed to another datastream for update since this will modify params
47
+ ##for subsequent calls if updating more than one datastream in a single update_indexed_attributes call
48
+ current_params = params.clone
45
49
  # remove any fields from params that this datastream doesn't recognize
46
- params.delete_if {|field_name,new_values| !self.fields.include?(field_name.to_sym) }
50
+ current_params.delete_if {|field_name,new_values| !self.fields.include?(field_name.to_sym) }
47
51
 
48
- result = params.dup
49
- params.each do |field_name,new_values|
52
+ result = current_params.dup
53
+ current_params.each do |field_name,new_values|
54
+ ##FIX this bug, it should delete it from a copy of params in case passed to another datastream for update
55
+ #if field does not exist just skip it
56
+ next if !self.fields.include?(field_name.to_sym)
50
57
  field_accessor_method = "#{field_name}_values"
51
58
 
52
59
  if new_values.kind_of?(Hash)
@@ -1,3 +1,5 @@
1
+ require 'solrizer/field_name_mapper'
2
+
1
3
  #this class represents a MetadataDatastream, a special case of ActiveFedora::Datastream
2
4
  module ActiveFedora::MetadataDatastreamHelper
3
5
 
@@ -16,7 +18,7 @@ module ActiveFedora::MetadataDatastreamHelper
16
18
 
17
19
  def self.included(klass)
18
20
  klass.extend(ClassMethods)
19
- klass.send(:include, ActiveFedora::SolrMapper)
21
+ klass.send(:include, Solrizer::FieldNameMapper)
20
22
  end
21
23
 
22
24
  #constructor, calls up to ActiveFedora::Datastream's constructor
@@ -38,7 +40,7 @@ module ActiveFedora::MetadataDatastreamHelper
38
40
  def to_solr(solr_doc = Solr::Document.new) # :nodoc:
39
41
  fields.each do |field_key, field_info|
40
42
  if field_info.has_key?(:values) && !field_info[:values].nil?
41
- field_symbol = generate_solr_symbol(field_key, field_info[:type])
43
+ field_symbol = solr_name(field_key, field_info[:type])
42
44
  field_info[:values].each do |val|
43
45
  solr_doc << Solr::Field.new(field_symbol => val)
44
46
  end
@@ -48,6 +50,20 @@ module ActiveFedora::MetadataDatastreamHelper
48
50
  return solr_doc
49
51
  end
50
52
 
53
+ def from_solr(solr_doc)
54
+ fields.each do |field_key, field_info|
55
+ field_symbol = Solrizer::FieldNameMapper.solr_name(field_key, field_info[:type])
56
+ value = (solr_doc[field_symbol].nil? ? solr_doc[field_symbol.to_s]: solr_doc[field_symbol])
57
+ unless value.nil?
58
+ if value.is_a? Array
59
+ update_attributes({field_key=>value})
60
+ else
61
+ update_indexed_attributes({field_key=>{0=>value}})
62
+ end
63
+ end
64
+ end
65
+ end
66
+
51
67
  def to_xml(xml = Nokogiri::XML::Document.parse("<fields />")) #:nodoc:
52
68
  if xml.instance_of?(Nokogiri::XML::Builder)
53
69
  xml_insertion_point = xml.doc.root
@@ -71,10 +87,10 @@ module ActiveFedora::MetadataDatastreamHelper
71
87
  end
72
88
 
73
89
 
74
- protected
75
-
76
- def generate_solr_symbol(field_name, field_type) # :nodoc:
77
- solr_name(field_name, field_type)
78
- end
90
+ # protected
91
+ #
92
+ # def generate_solr_symbol(field_name, field_type) # :nodoc:
93
+ # solr_name(field_name, field_type)
94
+ # end
79
95
 
80
96
  end
@@ -56,7 +56,7 @@ module ActiveFedora
56
56
  def find(args)
57
57
  if args == :all
58
58
  escaped_class_name = self.name.gsub(/(:)/, '\\:')
59
- q = "#{SolrMapper.solr_name(:active_fedora_model, :symbol)}:#{escaped_class_name}"
59
+ q = "#{Solrizer::FieldNameMapper.solr_name(:active_fedora_model, :symbol)}:#{escaped_class_name}"
60
60
  elsif args.class == String
61
61
  escaped_id = args.gsub(/(:)/, '\\:')
62
62
  q = "#{SOLR_DOCUMENT_ID}:#{escaped_id}"
@@ -89,13 +89,115 @@ module ActiveFedora
89
89
  def find_by_solr(query, args={})
90
90
  if query == :all
91
91
  escaped_class_name = self.name.gsub(/(:)/, '\\:')
92
- SolrService.instance.conn.query("#{SolrMapper.solr_name(:active_fedora_model, :symbol)}:#{escaped_class_name}", args)
92
+ SolrService.instance.conn.query("#{Solrizer::FieldNameMapper.solr_name(:active_fedora_model, :symbol)}:#{escaped_class_name}", args)
93
93
  elsif query.class == String
94
94
  escaped_id = query.gsub(/(:)/, '\\:')
95
95
  SolrService.instance.conn.query("#{SOLR_DOCUMENT_ID}:#{escaped_id}", args)
96
96
  end
97
97
  end
98
98
 
99
+ # Find all ActiveFedora objects for this model that match arguments
100
+ # passed in by querying Solr. Like find_by_solr this returns a solr result.
101
+ #
102
+ # query_fields a hash of object field names and values to filter on
103
+ # opts specifies options for the solr query
104
+ #
105
+ # options may include:
106
+ #
107
+ # :sort => array of hash with one hash per sort by field... defaults to [{system_create=>:descending}]
108
+ # :default_field, :rows, :filter_queries, :debug_query,
109
+ # :explain_other, :facets, :highlighting, :mlt,
110
+ # :operator => :or / :and
111
+ # :start => defaults to 0
112
+ # :field_list => array, defaults to ["*", "score"]
113
+ #
114
+ def find_by_fields_by_solr(query_fields,opts={})
115
+ #create solr_args from fields passed in, needs to be comma separated list of form field1=value1,field2=value2,...
116
+ escaped_class_name = self.name.gsub(/(:)/, '\\:')
117
+ query = "#{Solrizer::FieldNameMapper.solr_name(:active_fedora_model, :symbol)}:#{escaped_class_name}"
118
+
119
+ query_fields.each_pair do |key,value|
120
+ unless value.nil?
121
+ solr_key = key
122
+ #convert to symbol if need be
123
+ key = key.to_sym if !class_fields.has_key?(key)&&class_fields.has_key?(key.to_sym)
124
+ #do necessary mapping with suffix in most cases, otherwise ignore as a solr field key that activefedora does not know about
125
+ if class_fields.has_key?(key) && class_fields[key].has_key?(:type)
126
+ type = class_fields[key][:type]
127
+ type = :string unless type.kind_of?(Symbol)
128
+ solr_key = Solrizer::FieldNameMapper.solr_name(key,type)
129
+ end
130
+
131
+ escaped_value = value.gsub(/(:)/, '\\:')
132
+ #escaped_value = escaped_value.gsub(/ /, '\\ ')
133
+ key = SOLR_DOCUMENT_ID if (key === :id || key === :pid)
134
+ query = key.to_s.eql?(SOLR_DOCUMENT_ID) ? "#{query} AND #{key}:#{escaped_value}" : "#{query} AND #{solr_key}:#{escaped_value}"
135
+ end
136
+ end
137
+
138
+ query_opts = {}
139
+ opts.each do |key,value|
140
+ key = key.to_sym
141
+ query_opts[key] = value
142
+ end
143
+
144
+ #set default sort to created date ascending
145
+ unless query_opts.include?(:sort)
146
+ query_opts.merge!({:sort=>[Solrizer::FieldNameMapper.solr_name(:system_create,:date)=>:ascending]})
147
+ else
148
+ #need to convert to solr names for all fields
149
+ sort_array =[]
150
+
151
+ opts[:sort].collect do |sort|
152
+ sort_direction = :ascending
153
+ if sort.respond_to?(:keys)
154
+ key = sort.keys[0]
155
+ sort_direction = sort[key]
156
+ sort_direction =~ /^desc/ ? sort_direction = :descending : :ascending
157
+ else
158
+ key = sort.to_s
159
+ end
160
+ field_name = key
161
+
162
+ if key.to_s =~ /^system_create/
163
+ field_name = :system_create_date
164
+ key = :system_create
165
+ elsif key.to_s =~ /^system_mod/
166
+ field_name = :system_modified_date
167
+ key = :system_modified
168
+ end
169
+
170
+ solr_name = field_name
171
+ if class_fields.include?(field_name.to_sym)
172
+ solr_name = Solrizer::FieldNameMapper.solr_name(key,class_fields[field_name.to_sym][:type])
173
+ end
174
+ sort_array.push({solr_name=>sort_direction})
175
+ end
176
+
177
+ query_opts[:sort] = sort_array
178
+ end
179
+
180
+ puts "Querying solr for #{self.name} objects with query: '#{query}'"
181
+ results = ActiveFedora::SolrService.instance.conn.query(query,query_opts)
182
+ #objects = []
183
+ # results.hits.each do |hit|
184
+ # puts "get object for #{hit[SOLR_DOCUMENT_ID]}"
185
+ # obj = Fedora::Repository.instance.find_model(hit[SOLR_DOCUMENT_ID], self)
186
+ # obj.inner_object.new_object = false
187
+ # objects.push(obj)
188
+ #end
189
+ #objects
190
+ #ActiveFedora::SolrService.reify_solr_results(results)
191
+ end
192
+
193
+ def class_fields
194
+ #create dummy object that is empty by passing in fake pid
195
+ object = self.new({:pid=>'FAKE'})
196
+ fields = object.fields
197
+ #reset id to nothing
198
+ fields[:id][:values] = []
199
+ return fields
200
+ end
99
201
 
100
202
  #wrapper around instance_variable_set, sets @name to value
101
203
  def attribute_set(name, value)
@@ -1,10 +1,14 @@
1
1
  require "nokogiri"
2
2
  require "om"
3
+ require "solrizer/xml"
4
+
3
5
  #this class represents a MetadataDatastream, a special case of ActiveFedora::Datastream
4
6
  class ActiveFedora::NokogiriDatastream < ActiveFedora::Datastream
5
7
 
6
8
  include ActiveFedora::MetadataDatastreamHelper
7
- include OM::XML
9
+ include OM::XML::Document
10
+ include Solrizer::XML::TerminologyBasedSolrizer # this adds support for calling .to_solr
11
+
8
12
  # extend(OM::XML::Container::ClassMethods)
9
13
 
10
14
  attr_accessor :ng_xml
@@ -75,15 +79,9 @@ class ActiveFedora::NokogiriDatastream < ActiveFedora::Datastream
75
79
  return xml.to_xml {|config| config.no_declaration}
76
80
  end
77
81
 
78
- def to_solr(solr_doc = Solr::Document.new) # :nodoc:
79
-
80
- unless self.class.accessors.nil?
81
- self.class.accessors.each_pair do |accessor_name,accessor_info|
82
- solrize_accessor(accessor_name, accessor_info, :solr_doc=>solr_doc)
83
- end
84
- end
85
-
86
- return solr_doc
82
+ #overriding this method just so metadatahelper method does not get called
83
+ def from_solr(solr_doc)
84
+ #do nothing for now
87
85
  end
88
86
 
89
87
  def solrize_accessor(accessor_name, accessor_info, opts={})
@@ -116,36 +114,43 @@ class ActiveFedora::NokogiriDatastream < ActiveFedora::Datastream
116
114
 
117
115
  def solrize_node(node, accessor_pointer, solr_doc = Solr::Document.new)
118
116
  generic_field_name_base = self.class.accessor_generic_name(*accessor_pointer)
119
- generic_field_name = generate_solr_symbol(generic_field_name_base, :text)
117
+ generic_field_name = Solrizer::FieldNameMapper.solr_name(generic_field_name_base, :text)
120
118
 
121
119
  solr_doc << Solr::Field.new(generic_field_name => node.text)
122
120
 
123
121
  if accessor_pointer.length > 1
124
122
  hierarchical_field_name_base = self.class.accessor_hierarchical_name(*accessor_pointer)
125
- hierarchical_field_name = generate_solr_symbol(hierarchical_field_name_base, :text)
123
+ hierarchical_field_name = Solrizer::FieldNameMapper.solr_name(hierarchical_field_name_base, :text)
126
124
  solr_doc << Solr::Field.new(hierarchical_field_name => node.text)
127
125
  end
128
126
  end
129
127
 
130
128
  def update_indexed_attributes(params={}, opts={})
129
+ if self.class.terminology.nil?
130
+ raise "No terminology is set for this NokogiriDatastream class. Cannot perform update_indexed_attributes"
131
+ end
131
132
  # remove any fields from params that this datastream doesn't recognize
132
- params.delete_if do |field_key,new_values|
133
- if field_key.kind_of?(String)
133
+ #make sure to make a copy of params so not to modify hash that might be passed to other methods
134
+ current_params = params.clone
135
+ current_params.delete_if do |term_pointer,new_values|
136
+ if term_pointer.kind_of?(String)
134
137
  true
135
138
  else
136
- self.class.accessor_xpath(*OM.destringify(field_key) ).nil?
139
+ !self.class.terminology.has_term?(*OM.destringify(term_pointer))
137
140
  end
138
141
  end
142
+
139
143
  result = {}
140
- unless params.empty?
141
- result = update_properties( params )
144
+ unless current_params.empty?
145
+ result = update_values( current_params )
142
146
  self.dirty = true
143
147
  end
148
+
144
149
  return result
145
150
  end
146
151
 
147
152
  def get_values(field_key,default=[])
148
- property_values(*field_key)
153
+ term_values(*field_key)
149
154
  end
150
155
 
151
156
  end
@@ -6,6 +6,7 @@ module ActiveFedora
6
6
  #
7
7
  #Fields can still be overridden if more specificity is desired (see ActiveFedora::Datastream#fields method).
8
8
  class QualifiedDublinCoreDatastream < MetadataDatastream
9
+
9
10
  #A frozen array of Dublincore Terms.
10
11
  DCTERMS = [
11
12
  :contributor, :coverage, :creator, :description, :format, :identifier, :language, :publisher, :relation, :source, :title, :abstract, :accessRights, :accrualMethod, :accrualPeriodicity, :accrualPolicy, :alternative, :audience, :available, :bibliographicCitation, :conformsTo, :contributor, :coverage, :created, :creator, :date, :dateAccepted, :dateCopyrighted, :dateSubmitted, :description, :educationLevel, :extent, :format, :hasFormat, :hasPart, :hasVersion, :identifier, :instructionalMethod, :isFormatOf, :isPartOf, :isReferencedBy, :isReplacedBy, :isRequiredBy, :issued, :isVersionOf, :language, :license, :mediator, :medium, :modified, :provenance, :publisher, :references, :relation, :replaces, :requires, :rights, :rightsHolder, :source, :spatial, :subject, :tableOfContents, :temporal, :type, :valid
@@ -1,9 +1,10 @@
1
+ require 'solrizer/field_name_mapper'
1
2
 
2
3
  module ActiveFedora
3
4
  class RelsExtDatastream < Datastream
4
5
 
5
6
  include ActiveFedora::SemanticNode
6
- include ActiveFedora::SolrMapper
7
+ include Solrizer::FieldNameMapper
7
8
 
8
9
 
9
10
  def initialize(attrs=nil)
@@ -52,5 +53,25 @@ module ActiveFedora
52
53
  end
53
54
  return solr_doc
54
55
  end
56
+
57
+ def from_solr(solr_doc)
58
+ #cycle through all possible predicates
59
+ PREDICATE_MAPPINGS.keys.each do |predicate|
60
+ predicate_symbol = Solrizer::FieldNameMapper.solr_name(predicate, :symbol)
61
+ value = (solr_doc[predicate_symbol].nil? ? solr_doc[predicate_symbol.to_s]: solr_doc[predicate_symbol])
62
+ unless value.nil?
63
+ if value.is_a? Array
64
+ value.each do |obj|
65
+ r = ActiveFedora::Relationship.new(:subject=>:self, :predicate=>predicate, :object=>obj)
66
+ add_relationship(r)
67
+ end
68
+ else
69
+ r = ActiveFedora::Relationship.new(:subject=>:self, :predicate=>predicate, :object=>value)
70
+ add_relationship(r)
71
+ end
72
+ end
73
+ end
74
+ @load_from_solr = true
75
+ end
55
76
  end
56
77
  end
@@ -1,9 +1,9 @@
1
1
  module ActiveFedora
2
2
  module SemanticNode
3
3
  include MediaShelfClassLevelInheritableAttributes
4
- ms_inheritable_attributes :class_relationships, :internal_uri
4
+ ms_inheritable_attributes :class_relationships, :internal_uri, :class_named_relationships_desc
5
5
 
6
- attr_accessor :internal_uri, :relationships
6
+ attr_accessor :internal_uri, :named_relationship_desc, :relationships_are_dirty, :load_from_solr
7
7
 
8
8
  PREDICATE_MAPPINGS = Hash[:is_member_of => "isMemberOf",
9
9
  :has_member => "hasMember",
@@ -40,6 +40,7 @@ module ActiveFedora
40
40
  def add_relationship(relationship)
41
41
  # Only accept ActiveFedora::Relationships as input arguments
42
42
  assert_kind_of 'relationship', relationship, ActiveFedora::Relationship
43
+ self.relationships_are_dirty = true
43
44
  register_triple(relationship.subject, relationship.predicate, relationship.object)
44
45
  end
45
46
 
@@ -62,6 +63,56 @@ module ActiveFedora
62
63
  end
63
64
  end
64
65
 
66
+ def register_named_subject(subject)
67
+ self.class.register_named_subject(subject)
68
+ end
69
+
70
+ def register_named_relationship(subject, name, predicate, opts)
71
+ self.class.register_named_relationship(subject, name, predicate, opts)
72
+ end
73
+
74
+ def remove_relationship(relationship)
75
+ @relationships_are_dirty = true
76
+ unregister_triple(relationship.subject, relationship.predicate, relationship.object)
77
+ end
78
+
79
+ def unregister_triple(subject, predicate, object)
80
+ if relationship_exists?(subject, predicate, object)
81
+ relationships[subject][predicate].delete_if {|curObj| curObj == object}
82
+ relationships[subject].delete(predicate) if relationships[subject][predicate].nil? || relationships[subject][predicate].empty?
83
+ else
84
+ return false
85
+ end
86
+ end
87
+
88
+ def relationship_exists?(subject, predicate, object)
89
+ outbound_only = (subject != :inbound)
90
+ #cache the call in case it is retrieving inbound as well, don't want to hit solr too many times
91
+ cached_relationships = relationships(outbound_only)
92
+ cached_relationships.has_key?(subject)&&cached_relationships[subject].has_key?(predicate)&&cached_relationships[subject][predicate].include?(object)
93
+ end
94
+
95
+ def inbound_relationships(response_format=:uri)
96
+ rel_values = {}
97
+ inbound_named_relationship_predicates.each_pair do |name,predicate|
98
+ objects = self.send("#{name}",{:response_format=>response_format})
99
+ items = []
100
+ objects.each do |object|
101
+ if (response_format == :uri)
102
+ #create a Relationship object so that it generates the appropriate uri
103
+ r = ActiveFedora::Relationship.new(:subject=>:self, :predicate=>predicate, :object=>object)
104
+ items.push(r.object)
105
+ else
106
+ items.push(object)
107
+ end
108
+ end
109
+ unless items.empty?
110
+ rel_values.merge!({predicate=>items})
111
+ end
112
+ end
113
+ return rel_values
114
+ end
115
+
65
116
  def outbound_relationships()
66
117
  if !internal_uri.nil? && !relationships[internal_uri].nil?
67
118
  return relationships[:self].merge(relationships[internal_uri])
@@ -70,8 +121,10 @@ module ActiveFedora
70
121
  end
71
122
  end
72
123
 
73
- def relationships
124
+ # If outbound_only is false, inbound relationships will be included.
125
+ def relationships(outbound_only=true)
74
126
  @relationships ||= relationships_from_class
127
+ outbound_only ? @relationships : @relationships.merge(:inbound=>inbound_relationships)
75
128
  end
76
129
 
77
130
  def relationships_from_class
@@ -82,10 +135,194 @@ module ActiveFedora
82
135
  rels[subj][pred_key] = []
83
136
  end
84
137
  end
85
- #puts "rels from class: #{rels.inspect}"
86
138
  return rels
87
139
  end
88
140
 
141
+ def named_relationship(name)
142
+ rels = nil
143
+ if inbound_relationship_names.include?(name)
144
+ rels = named_relationships(false)[:inbound][name]
145
+ elsif outbound_relationship_names.include?(name)
146
+ rels = named_relationships[:self][name]
147
+ end
148
+ rels = [] if rels.nil?
149
+ return rels
150
+ end
151
+
152
+ def named_relationships(outbound_only=true)
153
+ #make sure to update if relationships have been updated
154
+ if @relationships_are_dirty == true
155
+ @named_relationships = named_relationships_from_class()
156
+ @relationships_are_dirty = false
157
+ end
158
+
159
+ #this will get called normally on first fetch if relationships are not dirty
160
+ @named_relationships ||= named_relationships_from_class()
161
+ outbound_only ? @named_relationships : @named_relationships.merge(:inbound=>named_inbound_relationships)
162
+ end
163
+
164
+ def named_relationships_from_class()
165
+ rels = {}
166
+ named_relationship_predicates.each_pair do |subj, names|
167
+ if relationships.has_key?(subj)
168
+ rels[subj] = {}
169
+ names.each_pair do |name, predicate|
170
+ rels[subj][name] = (relationships[subj].has_key?(predicate) ? relationships[subj][predicate] : [])
171
+ end
172
+ end
173
+ end
174
+ return rels
175
+ end
176
+
177
+ def named_inbound_relationships
178
+ rels = {}
179
+ if named_relationships_desc.has_key?(:inbound)&&!named_relationships_desc[:inbound].empty?()
180
+ inbound_rels = inbound_relationships
181
+
182
+ if named_relationship_predicates.has_key?(:inbound)
183
+ named_relationship_predicates[:inbound].each do |name, predicate|
184
+ rels[name] = inbound_rels.has_key?(predicate) ? inbound_rels[predicate] : []
185
+ end
186
+ end
187
+ end
188
+ return rels
189
+ end
190
+
191
+ def outbound_named_relationship_predicates
192
+ named_relationship_predicates.has_key?(:self) ? named_relationship_predicates[:self] : {}
193
+ end
194
+
195
+ def inbound_named_relationship_predicates
196
+ named_relationship_predicates.has_key?(:inbound) ? named_relationship_predicates[:inbound] : {}
197
+ end
198
+
199
+ def named_relationship_predicates
200
+ @named_relationship_predicates ||= named_relationship_predicates_from_class
201
+ end
202
+
203
+ def named_relationship_predicates_from_class
204
+ rels = {}
205
+ named_relationships_desc.each_pair do |subj, names|
206
+ rels[subj] = {}
207
+ names.each_pair do |name, args|
208
+ rels[subj][name] = args[:predicate]
209
+ end
210
+ end
211
+ return rels
212
+ end
213
+
214
+ def relationship_names
215
+ names = []
216
+ named_relationships_desc.each_key do |subject|
217
+ names = names.concat(named_relationships_desc[subject].keys)
218
+ end
219
+ names
220
+ end
221
+
222
+ def inbound_relationship_names
223
+ named_relationships_desc.has_key?(:inbound) ? named_relationships_desc[:inbound].keys : []
224
+ end
225
+
226
+ def outbound_relationship_names
227
+ named_relationships_desc.has_key?(:self) ? named_relationships_desc[:self].keys : []
228
+ end
229
+
230
+ def named_outbound_relationships
231
+ named_relationships_desc.has_key?(:self) ? named_relationships[:self] : {}
232
+ end
233
+
234
+ def is_named_relationship?(name, outbound_only=true)
235
+ if outbound_only
236
+ outbound_relationship_names.include?(name)
237
+ else
238
+ (outbound_relationship_names.include?(name)||inbound_relationship_names.include?(name))
239
+ end
240
+ end
241
+
242
+ def named_relationships_desc
243
+ @named_relationships_desc ||= named_relationships_desc_from_class
244
+ end
245
+
246
+ def named_relationships_desc_from_class
247
+ self.class.named_relationships_desc
248
+ end
249
+
250
+ def named_relationship_type(name)
251
+ if is_named_relationship?(name,true)
252
+ subject = outbound_relationship_names.include?(name)? :self : :inbound
253
+ if named_relationships_desc[subject][name].has_key?(:type)
254
+ return class_from_name(named_relationships_desc[subject][name][:type])
255
+ end
256
+ end
257
+ return nil
258
+ end
259
+
260
+ def add_named_relationship(name, object)
261
+ if is_named_relationship?(name,true)
262
+ if named_relationships_desc[:self][name].has_key?(:type)
263
+ klass = class_from_name(named_relationships_desc[:self][name][:type])
264
+ unless klass.nil?
265
+ (assert_kind_of_model 'object', object, klass)
266
+ end
267
+ end
268
+ #r = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>outbound_named_relationship_predicates[name],:object=>object})
269
+ #add_relationship(r)
270
+ add_relationship(outbound_named_relationship_predicates[name],object)
271
+ else
272
+ false
273
+ end
274
+ end
275
+
276
+ def remove_named_relationship(name, object)
277
+ if is_named_relationship?(name,true)
278
+ remove_relationship(outbound_named_relationship_predicates[name],object)
279
+ else
280
+ return false
281
+ end
282
+ end
283
+
284
+ def assert_kind_of_model(name, object, model_class)
285
+ raise "Assertion failure: #{name}: #{object.pid} does not have model #{model_class}, it has model #{relationships[:self][:has_model]}" unless object.kind_of_model?(model_class)
286
+ end
287
+
288
+ ############################################################################
289
+ # Checks that this class is either of type passed in or a child of that type.
290
+ # It also makes sure that this object was created as the same type by
291
+ # checking that hasmodel and the class match. This would not match
292
+ # if someone called load_instance on a Fedora Object that was created
293
+ # via a different model class than the one that was recreated from Fedora.
294
+ # This is a side-effect of ActiveFedora's behavior that will try to create
295
+ # the object type specified with the pid given whether it is actually that
296
+ # object type or not.
297
+ #
298
+ # If hasmodel does not match than this will return false indicated it does not
299
+ # have the correct model.
300
+ ############################################################################
301
+ def kind_of_model?(model_class)
302
+ if self.kind_of?(model_class)
303
+ #check has model and class match
304
+ if relationships[:self].has_key?(:has_model)
305
+ r = ActiveFedora::Relationship.new(:subject=>:self, :predicate=>:has_model, :object=>ActiveFedora::ContentModel.pid_from_ruby_class(self.class))
306
+ if relationships[:self][:has_model].first.to_s.eql?(r.object.to_s)
307
+ return true
308
+ else
309
+ raise "has_model relationship check failed for model #{model_class} raising exception, expected: '#{r.object.to_s}' actual: '#{relationships[:self][:has_model].to_s}'"
310
+ end
311
+ else
312
+ raise "has_model relationship does not exist for model #{model_class} check raising exception"
313
+ end
314
+ else
315
+ raise "kind_of? check failed for model #{model_class}, actual #{self.class} raising exception"
316
+ end
317
+ return false
318
+ end
319
+
320
+ def class_from_name(name)
321
+ klass = name.to_s.split('::').inject(Kernel) {|scope, const_name|
322
+ scope.const_get(const_name)}
323
+ (!klass.nil? && klass.is_a?(::Class)) ? klass : nil
324
+ end
325
+
89
326
  # Creates a RELS-EXT datastream for insertion into a Fedora Object
90
327
  # @pid
91
328
  # Note: This method is implemented on SemanticNode instead of RelsExtDatastream because SemanticNode contains the relationships array
@@ -134,15 +371,53 @@ module ActiveFedora
134
371
  # :is_member_of, :has_member, :is_part_of, :has_part
135
372
  def has_relationship(name, predicate, opts = {})
136
373
  opts = {:singular => nil, :inbound => false}.merge(opts)
137
- opts[:inbound] == true ? register_predicate(:inbound, predicate) : register_predicate(:self, predicate)
138
-
139
374
  if opts[:inbound] == true
375
+ raise "Duplicate use of predicate for named inbound relationship not allowed" if named_predicate_exists_with_different_name?(:inbound,name,predicate)
376
+ register_named_relationship(:inbound, name, predicate, opts)
377
+ register_predicate(:inbound, predicate)
140
378
  create_inbound_relationship_finders(name, predicate, opts)
141
- else
379
+ else
380
+ raise "Duplicate use of predicate for named outbound relationship not allowed" if named_predicate_exists_with_different_name?(:self,name,predicate)
381
+ register_named_relationship(:self, name, predicate, opts)
382
+ register_predicate(:self, predicate)
383
+ create_named_relationship_methods(name)
142
384
  create_outbound_relationship_finders(name, predicate, opts)
143
385
  end
386
+ end
144
387
 
388
+ #allow duplicate has_relationship calls with same name and predicate
389
+ def named_predicate_exists_with_different_name?(subject,name,predicate)
390
+ if named_relationships_desc.has_key?(subject)
391
+ named_relationships_desc[subject].each_pair do |existing_name, args|
392
+ return true if !args[:predicate].nil? && args[:predicate] == predicate && existing_name != name
393
+ end
394
+ end
395
+ return false
396
+ end
397
+
398
+ # named relationships desc are tracked as a hash of structure {name => args}}
399
+ def named_relationships_desc
400
+ @class_named_relationships_desc ||= Hash[:self => {}]
401
+ end
402
+
403
+ def register_named_subject(subject)
404
+ unless named_relationships_desc.has_key?(subject)
405
+ named_relationships_desc[subject] = {}
406
+ end
407
+ end
408
+
409
+ def register_named_relationship(subject, name, predicate, opts)
410
+ register_named_subject(subject)
411
+ opts.merge!({:predicate=>predicate})
412
+ named_relationships_desc[subject][name] = opts
145
413
  end
414
+
415
+ def create_named_relationship_methods(name)
416
+ append_method_name = "#{name.to_s.downcase}_append"
417
+ remove_method_name = "#{name.to_s.downcase}_remove"
418
+ self.send(:define_method,:"#{append_method_name}") {|object| add_named_relationship(name,object)}
419
+ self.send(:define_method,:"#{remove_method_name}") {|object| remove_named_relationship(name,object)}
420
+ end
146
421
 
147
422
  def create_inbound_relationship_finders(name, predicate, opts = {})
148
423
  class_eval <<-END
@@ -158,6 +433,8 @@ module ActiveFedora
158
433
  id_array << hit[SOLR_DOCUMENT_ID]
159
434
  end
160
435
  return id_array
436
+ elsif opts[:response_format] == :load_from_solr || self.load_from_solr
437
+ return ActiveFedora::SolrService.reify_solr_results(solr_result,{:load_from_solr=>true})
161
438
  else
162
439
  return ActiveFedora::SolrService.reify_solr_results(solr_result)
163
440
  end
@@ -165,7 +442,10 @@ module ActiveFedora
165
442
  end
166
443
  def #{name}_ids
167
444
  #{name}(:response_format => :id_array)
168
- end
445
+ end
446
+ def #{name}_from_solr
447
+ #{name}(:response_format => :load_from_solr)
448
+ end
169
449
  END
170
450
  end
171
451
 
@@ -185,6 +465,8 @@ module ActiveFedora
185
465
  solr_result = SolrService.instance.conn.query(query)
186
466
  if opts[:response_format] == :solr
187
467
  return solr_result
468
+ elsif opts[:response_format] == :load_from_solr || self.load_from_solr
469
+ return ActiveFedora::SolrService.reify_solr_results(solr_result,{:load_from_solr=>true})
188
470
  else
189
471
  return ActiveFedora::SolrService.reify_solr_results(solr_result)
190
472
  end
@@ -193,6 +475,9 @@ module ActiveFedora
193
475
  def #{name}_ids
194
476
  #{name}(:response_format => :id_array)
195
477
  end
478
+ def #{name}_from_solr
479
+ #{name}(:response_format => :load_from_solr)
480
+ end
196
481
  END
197
482
  end
198
483