active-fedora 7.0.0.rc1 → 7.0.0.rc2
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.
- checksums.yaml +4 -4
- data/lib/active_fedora/attributes.rb +15 -20
- data/lib/active_fedora/datastream.rb +9 -4
- data/lib/active_fedora/datastream_attribute.rb +33 -38
- data/lib/active_fedora/datastream_hash.rb +2 -2
- data/lib/active_fedora/datastreams.rb +43 -31
- data/lib/active_fedora/datastreams/nokogiri_datastreams.rb +146 -0
- data/lib/active_fedora/digital_object.rb +2 -2
- data/lib/active_fedora/indexing.rb +1 -1
- data/lib/active_fedora/nom_datastream.rb +7 -23
- data/lib/active_fedora/om_datastream.rb +3 -130
- data/lib/active_fedora/rdf_datastream.rb +4 -0
- data/lib/active_fedora/rdf_node.rb +1 -0
- data/lib/active_fedora/rdf_object.rb +1 -7
- data/lib/active_fedora/relation.rb +0 -29
- data/lib/active_fedora/relation/query_methods.rb +36 -0
- data/lib/active_fedora/simple_datastream.rb +1 -0
- data/lib/active_fedora/version.rb +1 -1
- data/spec/integration/base_spec.rb +2 -1
- data/spec/integration/om_datastream_spec.rb +2 -2
- data/spec/integration/scoped_query_spec.rb +13 -0
- data/spec/samples/hydra-mods_article_datastream.rb +1 -1
- data/spec/unit/datastream_spec.rb +0 -41
- data/spec/unit/datastreams_spec.rb +3 -3
- data/spec/unit/om_datastream_spec.rb +4 -1
- metadata +44 -43
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a37149e295c1dc3110b4fc70371e8bb25f980a8
|
4
|
+
data.tar.gz: b66fcc86fa8132818a1b40e8b1e9f06588d5d2a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c50f3f783e44031480f8f284e6bb3c18ec480e391785cfb000e03856b047bdc0d0aa45b481e30500a2f98b23a571e75b7922080edd4085f7abbb4d72675704a
|
7
|
+
data.tar.gz: 8b70aa1d294dc6768a4e9e25c2d39835d6a73ff0e2ec7cce6101ca314837c7c87c8bc6366f3f909eb70d3a5c87131de96bf35d585740d43988a505b1e030f655
|
@@ -40,6 +40,18 @@ module ActiveFedora
|
|
40
40
|
array_setter(key, value)
|
41
41
|
end
|
42
42
|
|
43
|
+
# @return [Boolean] true if there is an reader method and it returns a
|
44
|
+
# value different from the new_value.
|
45
|
+
def value_has_changed?(field, new_value)
|
46
|
+
new_value != array_reader(field)
|
47
|
+
end
|
48
|
+
|
49
|
+
def mark_as_changed(field)
|
50
|
+
self.send("#{field}_will_change!")
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
|
43
55
|
protected
|
44
56
|
|
45
57
|
# override activemodel so it doesn't trigger a load of all the attributes.
|
@@ -57,11 +69,8 @@ module ActiveFedora
|
|
57
69
|
return association.id_reader if association
|
58
70
|
end
|
59
71
|
raise UnknownAttributeError, "#{self.class} does not have an attribute `#{field}'" unless self.class.defined_attributes.key?(field)
|
60
|
-
|
61
|
-
|
62
|
-
else
|
63
|
-
instance_exec &self.class.defined_attributes[field].reader
|
64
|
-
end
|
72
|
+
|
73
|
+
self.class.defined_attributes[field].reader(self, *args)
|
65
74
|
end
|
66
75
|
|
67
76
|
def array_setter(field, args)
|
@@ -71,21 +80,7 @@ module ActiveFedora
|
|
71
80
|
return association.id_writer(args) if association
|
72
81
|
end
|
73
82
|
raise UnknownAttributeError, "#{self.class} does not have an attribute `#{field}'" unless self.class.defined_attributes.key?(field)
|
74
|
-
|
75
|
-
end
|
76
|
-
|
77
|
-
# @return [Boolean] true if there is an reader method and it returns a
|
78
|
-
# value different from the new_value.
|
79
|
-
def value_has_changed?(field, new_value)
|
80
|
-
new_value != array_reader(field)
|
81
|
-
end
|
82
|
-
|
83
|
-
def mark_as_changed(field)
|
84
|
-
self.send("#{field}_will_change!")
|
85
|
-
end
|
86
|
-
|
87
|
-
def datastream_for_attribute(dsid)
|
88
|
-
datastreams[dsid] || raise(ArgumentError, "Undefined datastream id: `#{dsid}' in has_attributes")
|
83
|
+
self.class.defined_attributes[field].writer(self, args)
|
89
84
|
end
|
90
85
|
|
91
86
|
module ClassMethods
|
@@ -35,10 +35,6 @@ module ActiveFedora
|
|
35
35
|
false
|
36
36
|
end
|
37
37
|
|
38
|
-
def validate_content_present
|
39
|
-
has_content?
|
40
|
-
end
|
41
|
-
|
42
38
|
def save
|
43
39
|
super
|
44
40
|
self
|
@@ -49,6 +45,15 @@ module ActiveFedora
|
|
49
45
|
self
|
50
46
|
end
|
51
47
|
|
48
|
+
# Freeze datastreams such that they can be loaded from Fedora, but can't be changed
|
49
|
+
def freeze
|
50
|
+
@frozen = true
|
51
|
+
end
|
52
|
+
|
53
|
+
def frozen?
|
54
|
+
!!@frozen
|
55
|
+
end
|
56
|
+
|
52
57
|
# serializes any changed data into the content field
|
53
58
|
def serialize!
|
54
59
|
end
|
@@ -2,7 +2,7 @@ module ActiveFedora
|
|
2
2
|
# Represents the mapping between a model attribute and a field in a datastream
|
3
3
|
class DatastreamAttribute
|
4
4
|
|
5
|
-
attr_accessor :dsid, :field, :datastream_class, :at, :
|
5
|
+
attr_accessor :dsid, :field, :datastream_class, :at, :multiple
|
6
6
|
|
7
7
|
def initialize(field, dsid, datastream_class, args={})
|
8
8
|
self.field = field
|
@@ -10,9 +10,6 @@ module ActiveFedora
|
|
10
10
|
self.datastream_class = datastream_class
|
11
11
|
self.multiple = args[:multiple].nil? ? false : args[:multiple]
|
12
12
|
self.at = args[:at]
|
13
|
-
|
14
|
-
initialize_reader!
|
15
|
-
initialize_writer!
|
16
13
|
end
|
17
14
|
|
18
15
|
# Gives the primary solr name for a column. If there is more than one indexer on the field definition, it gives the first
|
@@ -33,48 +30,46 @@ module ActiveFedora
|
|
33
30
|
end
|
34
31
|
end
|
35
32
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
ds.send("#{this.field}=", v)
|
45
|
-
else
|
46
|
-
terminology = this.at || [this.field]
|
47
|
-
ds.send(:update_indexed_attributes, {terminology => v})
|
48
|
-
end
|
33
|
+
def writer(obj, v)
|
34
|
+
ds = datastream_for_attribute(obj, dsid)
|
35
|
+
obj.mark_as_changed(field) if obj.value_has_changed?(field, v)
|
36
|
+
if ds.kind_of?(ActiveFedora::RDFDatastream)
|
37
|
+
ds.send("#{field}=", v)
|
38
|
+
else
|
39
|
+
terminology = at || [field]
|
40
|
+
ds.send(:update_indexed_attributes, {terminology => v})
|
49
41
|
end
|
50
42
|
end
|
51
43
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
# couldn't get it from solr, so try from fedora.
|
61
|
-
logger.info "Couldn't get #{this.field} out of solr, because #{e.message}. Trying another way."
|
62
|
-
end
|
44
|
+
def reader(obj, *opts)
|
45
|
+
if obj.inner_object.is_a? SolrDigitalObject
|
46
|
+
begin
|
47
|
+
# Look in the cache
|
48
|
+
return obj.inner_object.fetch(field)
|
49
|
+
rescue NoMethodError => e
|
50
|
+
# couldn't get it from solr, so try from fedora.
|
51
|
+
logger.info "Couldn't get #{field} out of solr, because #{e.message}. Trying another way."
|
63
52
|
end
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
53
|
+
end
|
54
|
+
# Load from fedora
|
55
|
+
ds = datastream_for_attribute(obj, dsid)
|
56
|
+
if ds.kind_of?(ActiveFedora::RDFDatastream)
|
57
|
+
ds.send(field)
|
58
|
+
else
|
59
|
+
terminology = at || [field]
|
60
|
+
if terminology.length == 1 && opts.present?
|
61
|
+
ds.send(terminology.first, *opts)
|
68
62
|
else
|
69
|
-
|
70
|
-
if terminology.length == 1 && opts.present?
|
71
|
-
ds.send(terminology.first, *opts)
|
72
|
-
else
|
73
|
-
ds.send(:term_values, *terminology)
|
74
|
-
end
|
63
|
+
ds.send(:term_values, *terminology)
|
75
64
|
end
|
76
65
|
end
|
77
66
|
end
|
78
67
|
|
68
|
+
private
|
69
|
+
|
70
|
+
def datastream_for_attribute(obj, dsid)
|
71
|
+
obj.datastreams[dsid] || raise(ArgumentError, "Undefined datastream id: `#{dsid}' in has_attributes")
|
72
|
+
end
|
73
|
+
|
79
74
|
end
|
80
75
|
end
|
@@ -2,6 +2,8 @@ module ActiveFedora
|
|
2
2
|
module Datastreams
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
+
autoload :NokogiriDatastreams, 'active_fedora/datastreams/nokogiri_datastreams'
|
6
|
+
|
5
7
|
included do
|
6
8
|
class_attribute :ds_specs
|
7
9
|
self.ds_specs = {'RELS-EXT'=> {:type=> ActiveFedora::RelsExtDatastream, :label=>"Fedora Object-to-Object Relationship Metadata", :block=>nil}}
|
@@ -62,7 +64,7 @@ module ActiveFedora
|
|
62
64
|
end
|
63
65
|
|
64
66
|
def datastream_from_spec(ds_spec, name)
|
65
|
-
inner_object.datastream_object_for name, ds_spec
|
67
|
+
inner_object.datastream_object_for name, {}, ds_spec
|
66
68
|
end
|
67
69
|
|
68
70
|
def load_datastreams
|
@@ -197,23 +199,16 @@ module ActiveFedora
|
|
197
199
|
# @option args [Boolean] :versionable Should versioned datastreams be stored
|
198
200
|
# @yield block executed by some kinds of datastreams
|
199
201
|
def has_metadata(*args, &block)
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
spec = {:autocreate => args.fetch(:autocreate, false), :type => args[:type], :label => args.fetch(:label,""), :control_group => args[:control_group], :disseminator => args.fetch(:disseminator,""), :url => args.fetch(:url,""),:block => block}
|
211
|
-
name = args[:name]
|
212
|
-
raise ArgumentError, "You must provide a name (dsid) for the metadata datastream" unless name
|
213
|
-
raise ArgumentError, "You must provide a :type property for the metadata datastream '#{name}'" unless spec[:type]
|
214
|
-
spec[:versionable] = args[:versionable] if args.has_key? :versionable
|
215
|
-
build_datastream_accessor(name)
|
216
|
-
ds_specs[name]= spec
|
202
|
+
@metadata_ds_defaults ||= {
|
203
|
+
:autocreate => false,
|
204
|
+
:type=>nil,
|
205
|
+
:label=>"",
|
206
|
+
:control_group=>nil,
|
207
|
+
:disseminator=>"",
|
208
|
+
:url=>"",
|
209
|
+
:name=>nil
|
210
|
+
}
|
211
|
+
spec_datastream(args, @metadata_ds_defaults, &block)
|
217
212
|
end
|
218
213
|
|
219
214
|
|
@@ -236,19 +231,14 @@ module ActiveFedora
|
|
236
231
|
# @option args [Boolean] :autocreate Always create this datastream on new objects
|
237
232
|
# @option args [Boolean] :versionable Should versioned datastreams be stored
|
238
233
|
def has_file_datastream(*args)
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
:label => args.fetch(:label,"File Datastream"), :control_group => args.fetch(:control_group,"M")}
|
248
|
-
spec[:versionable] = args[:versionable] if args.has_key? :versionable
|
249
|
-
name = args.fetch(:name, "content")
|
250
|
-
build_datastream_accessor(name)
|
251
|
-
ds_specs[name]= spec
|
234
|
+
@file_ds_defaults ||= {
|
235
|
+
:autocreate => false,
|
236
|
+
:type=>ActiveFedora::Datastream,
|
237
|
+
:label=>"File Datastream",
|
238
|
+
:control_group=>"M",
|
239
|
+
:name=>"content"
|
240
|
+
}
|
241
|
+
spec_datastream(args, @file_ds_defaults)
|
252
242
|
end
|
253
243
|
|
254
244
|
def build_datastream_accessor(dsid)
|
@@ -261,6 +251,28 @@ module ActiveFedora
|
|
261
251
|
|
262
252
|
private
|
263
253
|
|
254
|
+
# Creates a datastream spec combining the given args and default values
|
255
|
+
# @param args [Array] either [String, Hash] or [Hash]; the latter must .has_key? :name
|
256
|
+
# @param defaults [Hash] the default values for the datastream spec
|
257
|
+
# @yield block executed by some kinds of datastreams
|
258
|
+
def spec_datastream(args, defaults, &block)
|
259
|
+
if args.first.is_a? String
|
260
|
+
name = args.first
|
261
|
+
args = args[1] || {}
|
262
|
+
args[:name] = name
|
263
|
+
else
|
264
|
+
args = args.first || {}
|
265
|
+
end
|
266
|
+
spec = defaults.merge(args.select {|key, value| defaults.has_key? key})
|
267
|
+
name = spec.delete(:name)
|
268
|
+
raise ArgumentError, "You must provide a name (dsid) for the datastream" unless name
|
269
|
+
raise ArgumentError, "You must provide a :type property for the datastream '#{name}'" unless spec[:type]
|
270
|
+
spec[:versionable] = args[:versionable] if args.has_key? :versionable
|
271
|
+
spec[:block] = block if block
|
272
|
+
build_datastream_accessor(name)
|
273
|
+
ds_specs[name]= spec
|
274
|
+
end
|
275
|
+
|
264
276
|
## Given a dsid return a standard name
|
265
277
|
def name_for_dsid(dsid)
|
266
278
|
dsid.gsub('-', '_')
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module ActiveFedora
|
2
|
+
module Datastreams
|
3
|
+
module NokogiriDatastreams
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
|
8
|
+
# Create an instance of this class based on xml content
|
9
|
+
# @param [String, File, Nokogiri::XML::Node] xml the xml content to build from
|
10
|
+
# @param [ActiveFedora::OmDatastream] tmpl the Datastream object that you are building @default a new instance of this class
|
11
|
+
# Careful! If you call this from a constructor, be sure to provide something 'ie. self' as the @tmpl. Otherwise, you will get an infinite loop!
|
12
|
+
def from_xml(xml, tmpl=nil)
|
13
|
+
tmpl = self.new if tmpl.nil? ## This path is used only for unit testing (e.g. MarpaDCDatastream.from_xml(fixture("data.xml")) )
|
14
|
+
|
15
|
+
if !xml.present?
|
16
|
+
tmpl.ng_xml = self.xml_template
|
17
|
+
elsif xml.kind_of? Nokogiri::XML::Node || xml.kind_of?(Nokogiri::XML::Document)
|
18
|
+
tmpl.ng_xml = xml
|
19
|
+
else
|
20
|
+
tmpl.ng_xml = Nokogiri::XML::Document.parse(xml)
|
21
|
+
end
|
22
|
+
|
23
|
+
tmpl.ng_xml_doesnt_change!
|
24
|
+
|
25
|
+
return tmpl
|
26
|
+
end
|
27
|
+
|
28
|
+
def xml_template
|
29
|
+
Nokogiri::XML::Document.parse("<xml/>")
|
30
|
+
end
|
31
|
+
|
32
|
+
def decorate_ng_xml(xml)
|
33
|
+
xml
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def ng_xml
|
39
|
+
@ng_xml ||= begin
|
40
|
+
if new?
|
41
|
+
## Load up the template
|
42
|
+
xml = self.class.xml_template
|
43
|
+
else
|
44
|
+
xml = Nokogiri::XML::Document.parse(datastream_content)
|
45
|
+
end
|
46
|
+
self.class.decorate_ng_xml xml
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def ng_xml=(new_xml)
|
51
|
+
# before we set ng_xml, we load the datastream so we know if the new value differs.
|
52
|
+
local_or_remote_content(true)
|
53
|
+
|
54
|
+
case new_xml
|
55
|
+
when Nokogiri::XML::Document
|
56
|
+
self.content=new_xml.to_xml
|
57
|
+
when Nokogiri::XML::Node
|
58
|
+
## Cast a fragment to a document
|
59
|
+
self.content=new_xml.to_s
|
60
|
+
when String
|
61
|
+
self.content=new_xml
|
62
|
+
else
|
63
|
+
raise TypeError, "You passed a #{new_xml.class} into the ng_xml of the #{self.dsid} datastream. OmDatastream.ng_xml= only accepts Nokogiri::XML::Document, Nokogiri::XML::Element, Nokogiri::XML::Node, or raw XML (String) as inputs."
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# don't want content eagerly loaded by proxy, so implementing methods that would be implemented by define_attribute_methods
|
68
|
+
def ng_xml_will_change!
|
69
|
+
changed_attributes['ng_xml'] = nil
|
70
|
+
end
|
71
|
+
|
72
|
+
def ng_xml_doesnt_change!
|
73
|
+
changed_attributes.delete('ng_xml')
|
74
|
+
end
|
75
|
+
|
76
|
+
# don't want content eagerly loaded by proxy, so implementing methods that would be implemented by define_attribute_methods
|
77
|
+
def ng_xml_changed?
|
78
|
+
changed_attributes.has_key? 'ng_xml'
|
79
|
+
end
|
80
|
+
|
81
|
+
def datastream_content
|
82
|
+
@datastream_content ||= Nokogiri::XML(super).to_xml {|config| config.no_declaration}.strip
|
83
|
+
end
|
84
|
+
|
85
|
+
def content=(new_content)
|
86
|
+
if inline?
|
87
|
+
# inline datastreams may be transformed by fedora 3, so we test for equivalence instead of equality
|
88
|
+
if !EquivalentXml.equivalent?(datastream_content, new_content)
|
89
|
+
ng_xml_will_change!
|
90
|
+
@ng_xml = Nokogiri::XML::Document.parse(new_content)
|
91
|
+
super(@ng_xml.to_s)
|
92
|
+
end
|
93
|
+
else
|
94
|
+
if datastream_content != new_content
|
95
|
+
ng_xml_will_change!
|
96
|
+
@ng_xml = Nokogiri::XML::Document.parse(new_content)
|
97
|
+
super(@ng_xml.to_s)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
self.class.decorate_ng_xml @ng_xml
|
101
|
+
end
|
102
|
+
|
103
|
+
def content_changed?
|
104
|
+
return false if !xml_loaded
|
105
|
+
super
|
106
|
+
end
|
107
|
+
|
108
|
+
def to_xml(xml = nil)
|
109
|
+
xml = self.ng_xml if xml.nil?
|
110
|
+
ng_xml = self.ng_xml
|
111
|
+
if ng_xml.respond_to?(:root) && ng_xml.root.nil? && self.class.respond_to?(:root_property_ref) && !self.class.root_property_ref.nil?
|
112
|
+
ng_xml = self.class.generate(self.class.root_property_ref, "")
|
113
|
+
if xml.root.nil?
|
114
|
+
xml = ng_xml
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
unless xml == ng_xml || ng_xml.root.nil?
|
119
|
+
if xml.kind_of?(Nokogiri::XML::Document)
|
120
|
+
xml.root.add_child(ng_xml.root)
|
121
|
+
elsif xml.kind_of?(Nokogiri::XML::Node)
|
122
|
+
xml.add_child(ng_xml.root)
|
123
|
+
else
|
124
|
+
raise "You can only pass instances of Nokogiri::XML::Node into this method. You passed in #{xml}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
return xml.to_xml {|config| config.no_declaration}.strip
|
129
|
+
end
|
130
|
+
|
131
|
+
def local_or_remote_content(ensure_fetch = true)
|
132
|
+
@content = to_xml if ng_xml_changed? || autocreate?
|
133
|
+
super
|
134
|
+
end
|
135
|
+
|
136
|
+
def autocreate?
|
137
|
+
changed_attributes.has_key? :profile
|
138
|
+
end
|
139
|
+
|
140
|
+
def xml_loaded
|
141
|
+
instance_variable_defined? :@ng_xml
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|