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.
- 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
|
|