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