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
@@ -0,0 +1,22 @@
|
|
1
|
+
module ActiveFedora
|
2
|
+
class ContentModel < Base
|
3
|
+
CMODEL_NAMESPACE = "afmodel"
|
4
|
+
CMODEL_PID_SUFFIX = ""
|
5
|
+
|
6
|
+
attr_accessor :pid_suffix, :namespace
|
7
|
+
|
8
|
+
def initialize(attrs={})
|
9
|
+
@pid_suffix = attrs.has_key?(:pid_suffix) ? attrs[:pid_suffix] : CMODEL_PID_SUFFIX
|
10
|
+
@namespace = attrs.has_key?(:namespace) ? attrs[:namespace] : CMODEL_NAMESPACE
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.pid_from_ruby_class(klass,attrs={})
|
15
|
+
sanitized_class_name = klass.name.gsub(/(::)/, '_')
|
16
|
+
pid_suffix = attrs.has_key?(:pid_suffix) ? attrs[:pid_suffix] : CMODEL_PID_SUFFIX
|
17
|
+
namespace = attrs.has_key?(:namespace) ? attrs[:namespace] : CMODEL_NAMESPACE
|
18
|
+
return "#{namespace}:#{sanitized_class_name}#{pid_suffix}"
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'fedora/datastream'
|
2
|
+
module ActiveFedora
|
3
|
+
|
4
|
+
#This class represents a Fedora datastream
|
5
|
+
class Datastream < Fedora::Datastream
|
6
|
+
|
7
|
+
attr_accessor :dirty, :last_modified, :fields
|
8
|
+
|
9
|
+
def initialize(attrs = nil)
|
10
|
+
@fields={}
|
11
|
+
@dirty = false
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
#Return the xml content representing this Datastream from Fedora
|
16
|
+
def content
|
17
|
+
result = Fedora::Repository.instance.fetch_custom(self.attributes[:pid], "datastreams/#{self.dsid}")
|
18
|
+
return result
|
19
|
+
end
|
20
|
+
|
21
|
+
#set this Datastream's content
|
22
|
+
def content=(content)
|
23
|
+
self.blob = content
|
24
|
+
end
|
25
|
+
|
26
|
+
#get this datastreams identifier
|
27
|
+
def pid
|
28
|
+
self.attributes[:pid]
|
29
|
+
end
|
30
|
+
|
31
|
+
#set this datastreams parent identifier
|
32
|
+
def pid=(pid)
|
33
|
+
self.attributes[:pid] = pid
|
34
|
+
end
|
35
|
+
|
36
|
+
#set this datastreams identifier (note: sets both dsID and dsid)
|
37
|
+
def dsid=(dsid)
|
38
|
+
self.attributes[:dsID] = dsid
|
39
|
+
self.attributes[:dsid] = dsid
|
40
|
+
end
|
41
|
+
|
42
|
+
#compatibility method for rails' url generators. This method will
|
43
|
+
#urlescape escape dots, which are apparently
|
44
|
+
#invalid characters in a dsid.
|
45
|
+
def to_param
|
46
|
+
dsid.gsub(/\./, '%2e')
|
47
|
+
end
|
48
|
+
|
49
|
+
#has this datastream been modified since it was last saved?
|
50
|
+
def dirty?
|
51
|
+
@dirty
|
52
|
+
end
|
53
|
+
|
54
|
+
#saves this datastream into fedora.
|
55
|
+
def save
|
56
|
+
before_save
|
57
|
+
result = Fedora::Repository.instance.save(self)
|
58
|
+
after_save
|
59
|
+
result
|
60
|
+
end
|
61
|
+
|
62
|
+
def before_save # :nodoc:
|
63
|
+
#check_concurrency
|
64
|
+
end
|
65
|
+
def self.from_xml(tmpl, el)
|
66
|
+
el.elements.each("foxml:xmlContent/fields") do |f|
|
67
|
+
tmpl.send("#{f.name}_append", f.text)
|
68
|
+
end
|
69
|
+
tmpl.instance_variable_set(:@dirty, false)
|
70
|
+
tmpl
|
71
|
+
end
|
72
|
+
|
73
|
+
def after_save
|
74
|
+
self.dirty = false
|
75
|
+
end
|
76
|
+
|
77
|
+
# returns a datetime in the standard W3C DateTime Format.
|
78
|
+
# ie 2008-10-17T00:17:18.194Z
|
79
|
+
def last_modified_in_repository
|
80
|
+
# A hack to get around the fact that you can't call getDatastreamHistory
|
81
|
+
# or API-M getDatasreams on Fedora 3.0 REST API
|
82
|
+
# grabs the CREATED attribute off of the last foxml:datastreamVersion
|
83
|
+
# within the appropriate datastream node in the objectXML
|
84
|
+
if self.pid != nil
|
85
|
+
object_xml = Fedora::FedoraObject.object_xml(self.pid).gsub("\n ","")
|
86
|
+
datastream_xml = REXML::Document.new(object_xml).root.elements["foxml:datastream[@ID='#{self.dsid}']"]
|
87
|
+
|
88
|
+
puts datastream_xml.length
|
89
|
+
if datastream_xml.length > 3
|
90
|
+
datastream_xml.elements.each do |el|
|
91
|
+
puts el.inspect
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
datastream_xml.elements[datastream_xml.length - 2].attributes["CREATED"]
|
96
|
+
else
|
97
|
+
return nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def check_concurrency # :nodoc:
|
102
|
+
return true
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
class DatastreamConcurrencyException < Exception # :nodoc:
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module ActiveFedora
|
2
|
+
|
3
|
+
#
|
4
|
+
# This is a module replacing the ActiveFedora::Base class.
|
5
|
+
#
|
6
|
+
module FedoraObject
|
7
|
+
def initialize
|
8
|
+
@inner_object = Fedora::FedoraObject.new
|
9
|
+
Fedora::Repository.instance.save @inner_object
|
10
|
+
end
|
11
|
+
|
12
|
+
def save
|
13
|
+
Fedora::Repository.instance.save(@inner_object)
|
14
|
+
end
|
15
|
+
|
16
|
+
def delete
|
17
|
+
Fedora::Repository.instance.delete(@inner_object)
|
18
|
+
end
|
19
|
+
|
20
|
+
def datastreams
|
21
|
+
datastreams = {}
|
22
|
+
self.datastreams_xml['datastream'].each do |ds|
|
23
|
+
ds.merge!({:pid => self.pid, :dsID => ds["dsid"]})
|
24
|
+
datastreams.merge!({ds["dsid"] => ActiveFedora::Datastream.new(ds)})
|
25
|
+
end
|
26
|
+
return datastreams
|
27
|
+
end
|
28
|
+
|
29
|
+
def datastreams_xml
|
30
|
+
datastreams_xml = XmlSimple.xml_in(Fedora::Repository.instance.fetch_custom(self.pid, :datastreams))
|
31
|
+
end
|
32
|
+
|
33
|
+
# Adds datastream to the object. Saves the datastream to fedora upon adding.
|
34
|
+
def add_datastream(datastream)
|
35
|
+
datastream.pid = self.pid
|
36
|
+
datastream.save
|
37
|
+
end
|
38
|
+
|
39
|
+
# DC Datastream
|
40
|
+
def dc
|
41
|
+
#dc = REXML::Document.new(datastreams["DC"].content)
|
42
|
+
return datastreams["DC"]
|
43
|
+
end
|
44
|
+
|
45
|
+
# RELS-EXT Datastream
|
46
|
+
def rels_ext
|
47
|
+
if !datastreams.has_key?("RELS-EXT")
|
48
|
+
add(ActiveFedora::RelsExtDatastream.new)
|
49
|
+
end
|
50
|
+
|
51
|
+
return datastreams["RELS-EXT"]
|
52
|
+
end
|
53
|
+
|
54
|
+
def inner_object
|
55
|
+
@inner_object
|
56
|
+
end
|
57
|
+
|
58
|
+
def pid
|
59
|
+
@inner_object.pid
|
60
|
+
end
|
61
|
+
|
62
|
+
def state
|
63
|
+
@inner_object.state
|
64
|
+
end
|
65
|
+
|
66
|
+
def owner_id
|
67
|
+
@inner_object.owner_id
|
68
|
+
end
|
69
|
+
|
70
|
+
def errors
|
71
|
+
@inner_object.errors
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module ActiveFedora
|
2
|
+
#this class represents a MetadataDatastream, a special case of ActiveFedora::Datastream
|
3
|
+
class MetadataDatastream < Datastream
|
4
|
+
attr_accessor :fields
|
5
|
+
|
6
|
+
#constructor, calls up to ActiveFedora::Datastream's constructor
|
7
|
+
def initialize(attrs=nil)
|
8
|
+
super
|
9
|
+
@fields={}
|
10
|
+
end
|
11
|
+
|
12
|
+
# sets the blob, which in this case is the xml version of self, then calls ActiveFedora::Datastream.save
|
13
|
+
def save
|
14
|
+
self.set_blob_for_save
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_blob_for_save # :nodoc:
|
19
|
+
self.blob = self.to_xml
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_solr(solr_doc = Solr::Document.new) # :nodoc:
|
23
|
+
fields.each do |field_key, field_info|
|
24
|
+
if field_info.has_key?(:values) && !field_info[:values].nil?
|
25
|
+
field_symbol = generate_solr_symbol(field_key, field_info[:type])
|
26
|
+
field_info[:values].each do |val|
|
27
|
+
solr_doc << Solr::Field.new(field_symbol => val)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
return solr_doc
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_xml(xml = REXML::Document.new("<fields />")) #:nodoc:
|
36
|
+
fields.each_pair do |field,field_info|
|
37
|
+
el = REXML::Element.new("#{field.to_s}")
|
38
|
+
if field_info[:element_attrs]
|
39
|
+
field_info[:element_attrs].each{|k,v| el.add_attribute(k.to_s, v.to_s)}
|
40
|
+
end
|
41
|
+
field_info[:values].each do |val|
|
42
|
+
el = el.clone
|
43
|
+
el.text = val.to_s
|
44
|
+
if xml.class == REXML::Document
|
45
|
+
xml.root.elements.add(el)
|
46
|
+
else
|
47
|
+
xml.add(el)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
return xml.to_s
|
52
|
+
end
|
53
|
+
def self.from_xml(tmpl, el) # :nodoc:
|
54
|
+
el.elements.each("./foxml:datastreamVersion[last()]/foxml:xmlContent/fields/node()")do |f|
|
55
|
+
tmpl.send("#{f.name}_append", f.text)
|
56
|
+
end
|
57
|
+
tmpl.send(:dirty=, false)
|
58
|
+
tmpl
|
59
|
+
end
|
60
|
+
|
61
|
+
# This method generates the various accessor and mutator methods on self for the datastream metadata attributes.
|
62
|
+
# each field will have the 3 magic methods:
|
63
|
+
# name_values=(arg)
|
64
|
+
# name_values
|
65
|
+
# name_append(arg)
|
66
|
+
#
|
67
|
+
#
|
68
|
+
# Calling any of the generated methods marks self as dirty.
|
69
|
+
#
|
70
|
+
# 'tupe' is a datatype, currently :string, :text and :date are supported.
|
71
|
+
#
|
72
|
+
# opts is an options hash, which will affect the generation of the xml representation of this datastream.
|
73
|
+
#
|
74
|
+
# Currently supported modifiers:
|
75
|
+
# For +QualifiedDublinCorDatastreams+:
|
76
|
+
# :element_attrs =>{:foo=>:bar} - hash of xml element attributes
|
77
|
+
# :xml_node => :nodename - The xml node to be used to represent this object (in dcterms namespace)
|
78
|
+
# :encoding=>foo, or encodings_scheme - causes an xsi:type attribute to be set to 'foo'
|
79
|
+
# :multiple=>true - mark this field as a multivalue field (on by default)
|
80
|
+
#
|
81
|
+
#At some point, these modifiers will be ported up to work for any +ActiveFedora::MetadataDatastream+.
|
82
|
+
#
|
83
|
+
#There is quite a good example of this class in use in spec/examples/oral_history.rb
|
84
|
+
def field(name, tupe, opts={})
|
85
|
+
@fields[name.to_s.to_sym]={:type=>tupe, :values=>[]}.merge(opts)
|
86
|
+
eval <<-EOS
|
87
|
+
def #{name}_values=(arg)
|
88
|
+
@fields["#{name.to_s}".to_sym][:values]=[arg].flatten
|
89
|
+
self.dirty=true
|
90
|
+
end
|
91
|
+
def #{name}_values
|
92
|
+
@fields["#{name}".to_sym][:values]
|
93
|
+
end
|
94
|
+
def #{name}_append(arg)
|
95
|
+
@fields["#{name}".to_sym][:values] << arg
|
96
|
+
self.dirty =true
|
97
|
+
end
|
98
|
+
EOS
|
99
|
+
end
|
100
|
+
|
101
|
+
#get the field list
|
102
|
+
def self.fields
|
103
|
+
@@classFields
|
104
|
+
end
|
105
|
+
|
106
|
+
protected
|
107
|
+
|
108
|
+
def generate_solr_symbol(field_name, field_type) # :nodoc:
|
109
|
+
if field_name.to_s[-field_type.to_s.length - 1 .. -1] == "_#{field_type.to_s}"
|
110
|
+
return field_name.to_sym
|
111
|
+
elsif field_type == :string
|
112
|
+
return "#{field_name.to_s}_field".to_sym
|
113
|
+
else
|
114
|
+
return "#{field_name.to_s}_#{field_type.to_s}".to_sym
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'active_fedora/fedora_object'
|
2
|
+
module ActiveFedora
|
3
|
+
# = ActiveFedora
|
4
|
+
# This module mixes various methods into the including class,
|
5
|
+
# much in the way ActiveRecord does.
|
6
|
+
module Model
|
7
|
+
extend ActiveFedora::FedoraObject
|
8
|
+
|
9
|
+
attr_accessor :properties
|
10
|
+
|
11
|
+
def self.included(klass) # :nodoc:
|
12
|
+
klass.extend(ClassMethods)
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_metadata
|
16
|
+
end
|
17
|
+
|
18
|
+
def datastream
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
#
|
23
|
+
# =Class Methods
|
24
|
+
# These methods are mixed into the inheriting class.
|
25
|
+
#
|
26
|
+
# Accessor and mutator methods are dynamically generated based
|
27
|
+
# on the contents of the @@field_spec hash, which stores the
|
28
|
+
# field specifications recorded during invocation of has_metadata.
|
29
|
+
#
|
30
|
+
# Each metadata field will generate 3 methods:
|
31
|
+
#
|
32
|
+
# fieldname_values
|
33
|
+
# *returns the current values array for this field
|
34
|
+
# fieldname_values=(val)
|
35
|
+
# *store val as the values array. val
|
36
|
+
# may be a single string, or an array of strings
|
37
|
+
# (single items become single element arrays).
|
38
|
+
# fieldname_append(val)
|
39
|
+
# *appends val to the values array.
|
40
|
+
module ClassMethods
|
41
|
+
|
42
|
+
# Load an instance with the following pid. Note that you can actually
|
43
|
+
# pass an pid into this method, regardless of Fedora model type, and
|
44
|
+
# ActiveFedora will try to parse the results into the current type
|
45
|
+
# of self, which may or may not be what you want.
|
46
|
+
def load_instance(pid)
|
47
|
+
Fedora::Repository.instance.find_model(pid, self)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Takes :all or a pid as arguments
|
51
|
+
# Returns an Array of objects of the Class that +find+ is being
|
52
|
+
# called on
|
53
|
+
def find(args)
|
54
|
+
if args == :all
|
55
|
+
escaped_class_name = self.name.gsub(/(:)/, '\\:')
|
56
|
+
q = "active_fedora_model_field:#{escaped_class_name}"
|
57
|
+
elsif args.class == String
|
58
|
+
escaped_id = args.gsub(/(:)/, '\\:')
|
59
|
+
q = "id:#{escaped_id}"
|
60
|
+
end
|
61
|
+
hits = SolrService.instance.conn.query(q).hits
|
62
|
+
results = hits.map do |hit|
|
63
|
+
obj = Fedora::Repository.instance.find_model(hit["id"], self)
|
64
|
+
end
|
65
|
+
results.first
|
66
|
+
end
|
67
|
+
|
68
|
+
#Sends a query directly to SolrService
|
69
|
+
def solr_search(query, args={})
|
70
|
+
SolrService.instance.conn.query(query, args)
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
# If query is :all, this method will query Solr for all instances
|
75
|
+
# of self.type (based on active_fedora_model_field as indexed
|
76
|
+
# by Solr). If the query is any other string, this method simply does
|
77
|
+
# a pid based search (id:query).
|
78
|
+
#
|
79
|
+
# Note that this method does _not_ return ActiveFedora::Model
|
80
|
+
# objects, but rather an array of SolrResults.
|
81
|
+
#
|
82
|
+
# Args is an options hash, which is passed into the SolrService
|
83
|
+
# connection instance.
|
84
|
+
def find_by_solr(query, args={})
|
85
|
+
if query == :all
|
86
|
+
escaped_class_name = self.name.gsub(/(:)/, '\\:')
|
87
|
+
SolrService.instance.conn.query("active_fedora_model_field:#{escaped_class_name}", args)
|
88
|
+
elsif query.class == String
|
89
|
+
escaped_id = query.gsub(/(:)/, '\\:')
|
90
|
+
SolrService.instance.conn.query("id:#{escaped_id}", args)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
#wrapper around instance_variable_set, sets @name to value
|
96
|
+
def attribute_set(name, value)
|
97
|
+
instance_variable_set("@#{name}", value)
|
98
|
+
end
|
99
|
+
|
100
|
+
#wrapper around instance_variable_get, returns current value of @name
|
101
|
+
def attribute_get(name)
|
102
|
+
#instance_variable_get(properties[":#{name}"].instance_variable_name)
|
103
|
+
instance_variable_get("@#{name}")
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
def create_property_getter(property) # :nodoc:
|
108
|
+
|
109
|
+
class_eval <<-END
|
110
|
+
def #{property.name}
|
111
|
+
attribute_get("#{property.name}")
|
112
|
+
end
|
113
|
+
END
|
114
|
+
end
|
115
|
+
|
116
|
+
def create_property_setter(property)# :nodoc:
|
117
|
+
class_eval <<-END
|
118
|
+
def #{property.name}=(value)
|
119
|
+
attribute_set("#{property.name}", value)
|
120
|
+
end
|
121
|
+
END
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|