active-fedora 1.1.13 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/README.textile +8 -1
- data/Rakefile +4 -3
- data/VERSION +1 -1
- data/active-fedora.gemspec +17 -13
- data/lib/active_fedora.rb +2 -2
- data/lib/active_fedora/base.rb +295 -10
- data/lib/active_fedora/metadata_datastream.rb +10 -3
- data/lib/active_fedora/metadata_datastream_helper.rb +23 -7
- data/lib/active_fedora/model.rb +104 -2
- data/lib/active_fedora/nokogiri_datastream.rb +23 -18
- data/lib/active_fedora/qualified_dublin_core_datastream.rb +1 -0
- data/lib/active_fedora/rels_ext_datastream.rb +22 -1
- data/lib/active_fedora/semantic_node.rb +293 -8
- data/lib/active_fedora/solr_service.rb +21 -36
- data/lib/fedora/connection.rb +7 -3
- data/lib/hydra/sample_mods_datastream.rb +57 -94
- data/spec/integration/base_find_by_fields_spec.rb +215 -0
- data/spec/integration/base_spec.rb +756 -1
- data/spec/integration/metadata_datastream_helper_spec.rb +103 -0
- data/spec/integration/model_spec.rb +10 -9
- data/spec/integration/rels_ext_datastream_spec.rb +85 -0
- data/spec/integration/semantic_node_spec.rb +1 -2
- data/spec/integration/solr_service_spec.rb +36 -0
- data/spec/unit/base_file_management_spec.rb +0 -1
- data/spec/unit/base_named_datastream_spec.rb +580 -0
- data/spec/unit/base_spec.rb +98 -7
- data/spec/unit/nokogiri_datastream_spec.rb +29 -145
- data/spec/unit/semantic_node_spec.rb +479 -2
- metadata +39 -23
- data/README.rdoc +0 -17
- data/lib/active_fedora/solr_mapper.rb +0 -21
- data/spec/unit/solr_mapper_spec.rb +0 -31
@@ -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
|
-
|
50
|
+
current_params.delete_if {|field_name,new_values| !self.fields.include?(field_name.to_sym) }
|
47
51
|
|
48
|
-
result =
|
49
|
-
|
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,
|
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 =
|
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
|
-
|
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
|
data/lib/active_fedora/model.rb
CHANGED
@@ -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 = "#{
|
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("#{
|
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
|
-
|
79
|
-
|
80
|
-
|
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 =
|
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 =
|
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
|
133
|
-
|
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.
|
139
|
+
!self.class.terminology.has_term?(*OM.destringify(term_pointer))
|
137
140
|
end
|
138
141
|
end
|
142
|
+
|
139
143
|
result = {}
|
140
|
-
unless
|
141
|
-
result =
|
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
|
-
|
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
|
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, :
|
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
|
-
|
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
|
|