active-fedora 1.0.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/History.txt +4 -0
- data/Manifest.txt +19 -0
- data/PostInstall.txt +7 -0
- data/README.rdoc +48 -0
- data/lib/active-fedora.rb +1 -0
- data/lib/active_fedora.rb +10 -0
- data/lib/active_fedora/base.rb +362 -0
- data/lib/active_fedora/content_model.rb +22 -0
- data/lib/active_fedora/datastream.rb +109 -0
- data/lib/active_fedora/fedora_object.rb +78 -0
- data/lib/active_fedora/metadata_datastream.rb +120 -0
- data/lib/active_fedora/model.rb +125 -0
- data/lib/active_fedora/property.rb +15 -0
- data/lib/active_fedora/qualified_dublin_core_datastream.rb +80 -0
- data/lib/active_fedora/relationship.rb +43 -0
- data/lib/active_fedora/rels_ext_datastream.rb +42 -0
- data/lib/active_fedora/semantic_node.rb +224 -0
- data/lib/active_fedora/solr_service.rb +20 -0
- data/solr/config/schema.xml +229 -0
- metadata +135 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
PostInstall.txt
|
4
|
+
README.rdoc
|
5
|
+
lib/active_fedora.rb
|
6
|
+
lib/active-fedora.rb
|
7
|
+
lib/active_fedora/base.rb
|
8
|
+
lib/active_fedora/content_model.rb
|
9
|
+
lib/active_fedora/datastream.rb
|
10
|
+
lib/active_fedora/fedora_object.rb
|
11
|
+
lib/active_fedora/metadata_datastream.rb
|
12
|
+
lib/active_fedora/model.rb
|
13
|
+
lib/active_fedora/property.rb
|
14
|
+
lib/active_fedora/qualified_dublin_core_datastream.rb
|
15
|
+
lib/active_fedora/relationship.rb
|
16
|
+
lib/active_fedora/rels_ext_datastream.rb
|
17
|
+
lib/active_fedora/semantic_node.rb
|
18
|
+
lib/active_fedora/solr_service.rb
|
19
|
+
solr/config/schema.xml
|
data/PostInstall.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
= afed-regem
|
2
|
+
|
3
|
+
* FIX (url)
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
FIX (describe your package)
|
8
|
+
|
9
|
+
== FEATURES/PROBLEMS:
|
10
|
+
|
11
|
+
* FIX (list of features or problems)
|
12
|
+
|
13
|
+
== SYNOPSIS:
|
14
|
+
|
15
|
+
FIX (code sample of usage)
|
16
|
+
|
17
|
+
== REQUIREMENTS:
|
18
|
+
|
19
|
+
* FIX (list of requirements)
|
20
|
+
|
21
|
+
== INSTALL:
|
22
|
+
|
23
|
+
* FIX (sudo gem install, anything else)
|
24
|
+
|
25
|
+
== LICENSE:
|
26
|
+
|
27
|
+
(The MIT License)
|
28
|
+
|
29
|
+
Copyright (c) 2009 FIXME full name
|
30
|
+
|
31
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
32
|
+
a copy of this software and associated documentation files (the
|
33
|
+
'Software'), to deal in the Software without restriction, including
|
34
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
35
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
36
|
+
permit persons to whom the Software is furnished to do so, subject to
|
37
|
+
the following conditions:
|
38
|
+
|
39
|
+
The above copyright notice and this permission notice shall be
|
40
|
+
included in all copies or substantial portions of the Software.
|
41
|
+
|
42
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
43
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
44
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
45
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
46
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
47
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
48
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'active_fedora'
|
@@ -0,0 +1,362 @@
|
|
1
|
+
require 'util/class_level_inheritable_attributes'
|
2
|
+
require 'active_fedora/model'
|
3
|
+
require 'active_fedora/semantic_node'
|
4
|
+
module ActiveFedora
|
5
|
+
|
6
|
+
# This class ties together many of the lower-level modules, and
|
7
|
+
# implements something akin to an ActiveRecord-alike interface to
|
8
|
+
# fedora. If you want to represent a fedora object in the ruby
|
9
|
+
# space, this is the class you want to extend.
|
10
|
+
#
|
11
|
+
# =The Basics
|
12
|
+
# class Oralhistory < ActiveFedora::Base
|
13
|
+
# has_metadata :name => "properties", :type => ActiveFedora::MetadataDatastream do |m|
|
14
|
+
# m.field "narrator", :string
|
15
|
+
# m.field "narrator", :text
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# The above example creates a FedoraObject with a metadata datastream named "properties", which is composed of a
|
20
|
+
# narrator and bio field.
|
21
|
+
#
|
22
|
+
# Datastreams defined with +has_metadata+ are accessed via the +datastreams+ member hash.
|
23
|
+
#
|
24
|
+
# =Implementation
|
25
|
+
# This class is really a facade for a basic Fedora::FedoraObject, which is stored internally.
|
26
|
+
class Base
|
27
|
+
include MediaShelfClassLevelInheritableAttributes
|
28
|
+
ms_inheritable_attributes :ds_specs
|
29
|
+
include Model
|
30
|
+
include SemanticNode
|
31
|
+
|
32
|
+
# Has this object been saved?
|
33
|
+
def new_object?
|
34
|
+
@new_object
|
35
|
+
end
|
36
|
+
|
37
|
+
# Constructor. If +attrs+ does not comtain +:pid+, we assume we're making a new one,
|
38
|
+
# and call off to the Fedora Rest API for the next available Fedora pid, and mark as new object.
|
39
|
+
#
|
40
|
+
# If there is a pid, we're re-hydrating an existing object, and new object is false. Once the @inner_object is stored,
|
41
|
+
# we configure any defined datastreams.
|
42
|
+
def initialize(attrs = {})
|
43
|
+
unless attrs[:pid]
|
44
|
+
attrs = attrs.merge!({:pid=>Fedora::Repository.instance.nextid})
|
45
|
+
@new_object=true
|
46
|
+
else
|
47
|
+
@new_object=false;
|
48
|
+
end
|
49
|
+
@inner_object = Fedora::FedoraObject.new(attrs)
|
50
|
+
@datastreams = {}
|
51
|
+
configure_defined_datastreams
|
52
|
+
end
|
53
|
+
|
54
|
+
#This method is used to specify the details of a datastream.
|
55
|
+
#args must include :name. Note that this method doesn't actually
|
56
|
+
#execute the block, but stores it at the class level, to be executed
|
57
|
+
#by any future instantiations.
|
58
|
+
def self.has_metadata(args, &block)
|
59
|
+
@ds_specs ||= Hash.new
|
60
|
+
@ds_specs[args[:name]]= [args[:type], block]
|
61
|
+
end
|
62
|
+
|
63
|
+
#Saves a Base object, and any dirty datastreams, then updates
|
64
|
+
#the Solr index for this object.
|
65
|
+
def save
|
66
|
+
# If it's a new object, set the conformsTo relationship for Fedora CMA
|
67
|
+
if new_object?
|
68
|
+
add_relationship(:conforms_to, ActiveFedora::ContentModel.pid_from_ruby_class(self.class))
|
69
|
+
end
|
70
|
+
@new_object =false
|
71
|
+
Fedora::Repository.instance.save(@inner_object)
|
72
|
+
datastreams_in_memory.each do |k,ds|
|
73
|
+
if ds.dirty? || ds.new_object?
|
74
|
+
if ds.kind_of?(ActiveFedora::MetadataDatastream)
|
75
|
+
metadata_is_dirty = true
|
76
|
+
end
|
77
|
+
ds.save
|
78
|
+
end
|
79
|
+
self.update_index if metadata_is_dirty == true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
#Deletes a Base object, also deletes the info indexed in Solr, and
|
84
|
+
#the underlying inner_object.
|
85
|
+
def delete
|
86
|
+
Fedora::Repository.instance.delete(@inner_object)
|
87
|
+
escaped_pid = self.pid.gsub(/(:)/, '\\:')
|
88
|
+
SolrService.instance.conn.delete(escaped_pid)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns all known datastreams for the object. If the object has been
|
92
|
+
# saved to fedora, the persisted datastreams will be included.
|
93
|
+
# Datastreams that have been modified in memory are given preference over
|
94
|
+
# the copy in Fedora.
|
95
|
+
def datastreams
|
96
|
+
if @new_object
|
97
|
+
@datastreams = datastreams_in_memory
|
98
|
+
else
|
99
|
+
@datastreams = datastreams_in_fedora.merge(datastreams_in_memory)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
def datastreams_in_fedora #:nodoc:
|
105
|
+
mds = {}
|
106
|
+
self.datastreams_xml['datastream'].each do |ds|
|
107
|
+
ds.merge!({:pid => self.pid, :dsID => ds["dsid"]})
|
108
|
+
if ds["dsid"] == "RELS-EXT"
|
109
|
+
mds.merge!({ds["dsid"] => ActiveFedora::RelsExtDatastream.new(ds)})
|
110
|
+
else
|
111
|
+
mds.merge!({ds["dsid"] => ActiveFedora::Datastream.new(ds)})
|
112
|
+
end
|
113
|
+
mds[ds["dsid"]].new_object = false
|
114
|
+
end
|
115
|
+
mds
|
116
|
+
end
|
117
|
+
|
118
|
+
def datastreams_in_memory #:ndoc:
|
119
|
+
@datastreams ||= Hash.new
|
120
|
+
end
|
121
|
+
|
122
|
+
#return the datastream xml representation direclty from Fedora
|
123
|
+
def datastreams_xml
|
124
|
+
datastreams_xml = XmlSimple.xml_in(Fedora::Repository.instance.fetch_custom(self.pid, :datastreams))
|
125
|
+
end
|
126
|
+
|
127
|
+
# Adds datastream to the object. Saves the datastream to fedora upon adding.
|
128
|
+
def add_datastream(datastream)
|
129
|
+
datastream.pid = self.pid
|
130
|
+
datastreams[datastream.dsid] = datastream
|
131
|
+
return true
|
132
|
+
end
|
133
|
+
def add(datastream) # :nodoc:
|
134
|
+
warn "Warning: ActiveFedora::Base.add has been deprected. Use add_datastream"
|
135
|
+
add_datastream(datastream)
|
136
|
+
end
|
137
|
+
|
138
|
+
#return all datastreams of type ActiveFedora::MetadataDatastream
|
139
|
+
def metadata_streams
|
140
|
+
results = []
|
141
|
+
datastreams.each_value do |ds|
|
142
|
+
if ds.kind_of?(ActiveFedora::MetadataDatastream)
|
143
|
+
results<<ds
|
144
|
+
end
|
145
|
+
end
|
146
|
+
return results
|
147
|
+
end
|
148
|
+
|
149
|
+
#return all datastreams not of type ActiveFedora::MetadataDatastream
|
150
|
+
#(that aren't Dublin Core or RELS-EXT streams either)
|
151
|
+
def file_streams
|
152
|
+
results = []
|
153
|
+
datastreams.each_value do |ds|
|
154
|
+
if !ds.kind_of?(ActiveFedora::MetadataDatastream)
|
155
|
+
dsid = ds.dsid
|
156
|
+
if dsid != "DC" && dsid != "RELS-EXT"
|
157
|
+
results<<ds
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
return results
|
162
|
+
end
|
163
|
+
|
164
|
+
# Return the Dublin Core (DC) Datastream. You can also get at this via
|
165
|
+
# the +datastreams["DC"]+.
|
166
|
+
def dc
|
167
|
+
#dc = REXML::Document.new(datastreams["DC"].content)
|
168
|
+
return datastreams["DC"]
|
169
|
+
end
|
170
|
+
|
171
|
+
# Returns the RELS-EXT Datastream
|
172
|
+
# Tries to grab from in-memory datastreams first
|
173
|
+
# Failing that, attempts to load from Fedora and addst to in-memory datastreams
|
174
|
+
# Failing that, creates a new RelsExtDatastream and adds it to the object
|
175
|
+
def rels_ext
|
176
|
+
if !datastreams.has_key?("RELS-EXT")
|
177
|
+
add_datastream(ActiveFedora::RelsExtDatastream.new)
|
178
|
+
end
|
179
|
+
return datastreams["RELS-EXT"]
|
180
|
+
end
|
181
|
+
|
182
|
+
# @returns Hash of relationships, as defined by SemanticNode
|
183
|
+
# Rely on rels_ext datastream to track relationships array
|
184
|
+
# Overrides accessor for relationships array used by SemanticNode.
|
185
|
+
def relationships
|
186
|
+
return rels_ext.relationships
|
187
|
+
end
|
188
|
+
|
189
|
+
# Add a Rels-Ext relationship to the Object.
|
190
|
+
# @param predicate
|
191
|
+
# @param object Either a string URI or an object that responds to .pid
|
192
|
+
def add_relationship(predicate, obj)
|
193
|
+
#predicate = ActiveFedora::RelsExtDatastream.predicate_lookup(predicate)
|
194
|
+
r = ActiveFedora::Relationship.new(:subject=>:self, :predicate=>predicate, :object=>obj)
|
195
|
+
rels_ext.add_relationship(r)
|
196
|
+
rels_ext.dirty = true
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
def inner_object # :nodoc
|
201
|
+
@inner_object
|
202
|
+
end
|
203
|
+
|
204
|
+
#return the pid of the Fedora Object
|
205
|
+
def pid
|
206
|
+
@inner_object.pid
|
207
|
+
end
|
208
|
+
|
209
|
+
#For Rails compatibility with url generators.
|
210
|
+
def to_param
|
211
|
+
self.pid
|
212
|
+
end
|
213
|
+
#return the internal fedora URI
|
214
|
+
def internal_uri
|
215
|
+
"info:fedora/#{pid}"
|
216
|
+
end
|
217
|
+
|
218
|
+
#return the state of the inner object
|
219
|
+
def state
|
220
|
+
@inner_object.state
|
221
|
+
end
|
222
|
+
|
223
|
+
#return the owner id
|
224
|
+
def owner_id
|
225
|
+
@inner_object.owner_id
|
226
|
+
end
|
227
|
+
|
228
|
+
#return the create_date of the inner object (unless it's a new object)
|
229
|
+
def create_date
|
230
|
+
@inner_object.create_date unless new_object?
|
231
|
+
end
|
232
|
+
|
233
|
+
#return the modification date of the inner object (unless it's a new object)
|
234
|
+
def modified_date
|
235
|
+
@inner_object.modified_date unless new_object?
|
236
|
+
end
|
237
|
+
|
238
|
+
#return the error list of the inner object (unless it's a new object)
|
239
|
+
def errors
|
240
|
+
@inner_object.errors
|
241
|
+
end
|
242
|
+
|
243
|
+
#return the label of the inner object (unless it's a new object)
|
244
|
+
def label
|
245
|
+
@inner_object.label
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
def self.deserialize(doc) #:nodoc:
|
250
|
+
pid = doc.elements['/foxml:digitalObject'].attributes['PID']
|
251
|
+
proto = self.new(:pid=>pid)
|
252
|
+
proto.datastreams.each do |name,ds|
|
253
|
+
doc.elements.each("//foxml:datastream[@ID='#{name}']") do |el|
|
254
|
+
proto.datastreams[name]=ds.class.from_xml(ds, el)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
proto
|
258
|
+
end
|
259
|
+
|
260
|
+
#Return a hash of all available metadata fields for all
|
261
|
+
#ActiveFedora::MetadataDatastream datastreams, as well as
|
262
|
+
#system_create_date, system_modified_date, active_fedora_model_field,
|
263
|
+
#and the object id.
|
264
|
+
def fields
|
265
|
+
fields = {:id => {:values => [pid]}, :system_create_date => {:values => [self.create_date]}, :system_modified_date => {:values => [self.modified_date]}, :active_fedora_model_field => {:values => [self.class.inspect]}}
|
266
|
+
datastreams.values.each do |ds|
|
267
|
+
fields.merge!(ds.fields) if ds.kind_of?(ActiveFedora::MetadataDatastream)
|
268
|
+
end
|
269
|
+
return fields
|
270
|
+
end
|
271
|
+
|
272
|
+
#Returns the xml version of this object as a string.
|
273
|
+
def to_xml(xml=REXML::Document.new("<xml><fields/><content/></xml>"))
|
274
|
+
fields_xml = xml.root.elements['fields']
|
275
|
+
{:id => pid, :system_create_date => self.create_date, :system_modified_date => self.modified_date, :active_fedora_model_field => self.class.inspect}.each_pair do |attribute_name, value|
|
276
|
+
el = REXML::Element.new(attribute_name.to_s)
|
277
|
+
el.text = value
|
278
|
+
fields_xml << el
|
279
|
+
end
|
280
|
+
datastreams.each_value do |ds|
|
281
|
+
ds.to_xml(fields_xml) if ds.kind_of?(ActiveFedora::MetadataDatastream) || ds.kind_of?(ActiveFedora::RelsExtDatastream)
|
282
|
+
end
|
283
|
+
return xml.to_s
|
284
|
+
end
|
285
|
+
|
286
|
+
#Return a Solr::Document version of this object.
|
287
|
+
def to_solr(solr_doc = Solr::Document.new)
|
288
|
+
solr_doc << {:id => pid, :system_create_date => self.create_date, :system_modified_date => self.modified_date, :active_fedora_model_field => self.class.inspect}
|
289
|
+
datastreams.each_value do |ds|
|
290
|
+
solr_doc = ds.to_solr(solr_doc) if ds.kind_of?(ActiveFedora::MetadataDatastream) || ds.kind_of?(ActiveFedora::RelsExtDatastream)
|
291
|
+
end
|
292
|
+
return solr_doc
|
293
|
+
end
|
294
|
+
|
295
|
+
# Updates Solr index with self.
|
296
|
+
def update_index
|
297
|
+
SolrService.instance.conn.update(self.to_solr)
|
298
|
+
end
|
299
|
+
|
300
|
+
# An ActiveRecord-ism to udpate metadata values.
|
301
|
+
#
|
302
|
+
# Passing a hash into this method will attempt to set the values into the
|
303
|
+
# appropriate fields (for all datastreams, which means a.name and b.name
|
304
|
+
# fields are _both_ overwritten)
|
305
|
+
def update_attributes(params={})
|
306
|
+
params.each do |k,v|
|
307
|
+
datastreams.values.each do |d|
|
308
|
+
if d.fields[k.to_sym]
|
309
|
+
d.send("#{k}_values=", v)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
# A convenience method for updating indexed attributes. The passed in hash
|
316
|
+
# must look like this :
|
317
|
+
# {{:name=>{"0"=>"a","1"=>"b"}}
|
318
|
+
#
|
319
|
+
# This will result in any datastream field of name :name having the value [a,b]
|
320
|
+
#
|
321
|
+
# An index of -1 will insert a new value. any existing value at the relevant index
|
322
|
+
# will be overwritten.
|
323
|
+
#
|
324
|
+
# As in update_attributes, this overwrites _all_ available fields.
|
325
|
+
#
|
326
|
+
def update_indexed_attributes(params={})
|
327
|
+
params.each do |key,value|
|
328
|
+
datastreams.each do |dsn, dstream|
|
329
|
+
if dstream.fields[key.to_sym]
|
330
|
+
aname="#{key}_values"
|
331
|
+
curval = dstream.send("#{aname}")
|
332
|
+
cpv=value.dup#copy this, we'll need the original for the next ds
|
333
|
+
cpv.delete_if do |y,z|
|
334
|
+
if curval[y.to_i] and y.to_i > -1
|
335
|
+
curval[y.to_i]=z
|
336
|
+
true
|
337
|
+
else
|
338
|
+
false
|
339
|
+
end
|
340
|
+
end
|
341
|
+
cpv.each { |y,z| curval<<z}#just append everything left
|
342
|
+
dstream.send("#{aname}=", curval) #write it back to the ds
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
end
|
348
|
+
|
349
|
+
private
|
350
|
+
def configure_defined_datastreams
|
351
|
+
if self.class.ds_specs
|
352
|
+
self.class.ds_specs.each do |name,ar|
|
353
|
+
ds = ar.first.new(:dsid=>name)
|
354
|
+
ar.last.call(ds)
|
355
|
+
self.add_datastream(ds)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
|
361
|
+
end
|
362
|
+
end
|