active-fedora 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|