active-fedora 5.2.1 → 5.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -10,4 +10,4 @@ group :development, :test do
10
10
  end
11
11
 
12
12
  gem 'jruby-openssl', :platform=> :jruby
13
- gem 'nom-xml', :git => 'git://github.com/cbeer/nom-xml.git'
13
+ gem 'nom-xml', '>=0.5.1'
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
22
22
  s.add_dependency("activesupport", '>= 3.0.0')
23
23
  s.add_dependency("builder", '~> 3.0.0')
24
24
  s.add_dependency("mediashelf-loggable")
25
- s.add_dependency("rubydora", '~>1.0')
25
+ s.add_dependency("rubydora", '~>1.2')
26
26
  s.add_dependency("rdf")
27
27
  s.add_dependency("rdf-rdfxml", '~>0.3.8')
28
28
  s.add_dependency("deprecation")
@@ -304,7 +304,6 @@ module ActiveFedora
304
304
  solrize_profile(solr_doc)
305
305
  end
306
306
  datastreams.each_value do |ds|
307
- ds.ensure_xml_loaded if ds.respond_to? :ensure_xml_loaded ### Can't put this in the model because it's often implemented in Solrizer::XML::TerminologyBasedSolrizer
308
307
  solr_doc = ds.to_solr(solr_doc)
309
308
  end
310
309
  solr_doc = solrize_relationships(solr_doc) unless opts[:model_only]
@@ -5,7 +5,7 @@ module ActiveFedora
5
5
  extend Deprecation
6
6
  attr_writer :digital_object
7
7
  attr_accessor :last_modified, :fields
8
-
8
+
9
9
  def initialize(digital_object=nil, dsid=nil, options={})
10
10
  ## When you use the versions feature of rubydora (0.5.x), you need to have a 3 argument constructor
11
11
  self.fields={}
@@ -59,7 +59,7 @@ module ActiveFedora
59
59
  define_method field do
60
60
  ds = self.send(args[:to])
61
61
  val = if ds.kind_of?(ActiveFedora::RDFDatastream)
62
- ds.send(:get_values, field)
62
+ ds.send(field)
63
63
  else
64
64
  terminology = args[:at] || [field]
65
65
  ds.send(:term_values, *terminology)
@@ -72,7 +72,7 @@ module ActiveFedora
72
72
  define_method "#{field}=".to_sym do |v|
73
73
  ds = self.send(args[:to])
74
74
  if ds.kind_of?(ActiveFedora::RDFDatastream)
75
- ds.send(:set_value, field, v)
75
+ ds.send("#{field}=", v)
76
76
  else
77
77
  terminology = args[:at] || [field]
78
78
  ds.send(:update_indexed_attributes, {terminology => v})
@@ -5,20 +5,24 @@ require "solrizer/xml"
5
5
  #this class represents a xml metadata datastream
6
6
  module ActiveFedora
7
7
  class NokogiriDatastream < Datastream
8
-
9
- before_save do
10
- return unless xml_loaded
11
- content_will_change! if ng_xml_changed?
12
- end
13
8
 
9
+ before_save do
10
+ if content.blank?
11
+ logger.warn "Cowardly refusing to save a datastream with empty content: #{self.inspect}"
12
+ false
13
+ end
14
+ end
15
+
16
+ extend Deprecation
17
+ # include MetadataDatastreamHelper
14
18
  include OM::XML::Document
15
19
  include Solrizer::XML::TerminologyBasedSolrizer # this adds support for calling .to_solr
16
20
 
17
21
  alias_method(:om_term_values, :term_values) unless method_defined?(:om_term_values)
18
22
  alias_method(:om_update_values, :update_values) unless method_defined?(:om_update_values)
19
23
 
20
- attr_accessor :internal_solr_doc, :xml_loaded
21
-
24
+ attr_accessor :internal_solr_doc
25
+
22
26
  def self.default_attributes
23
27
  super.merge(:controlGroup => 'X', :mimeType => 'text/xml')
24
28
  end
@@ -47,16 +51,8 @@ module ActiveFedora
47
51
  Nokogiri::XML::Document.parse("<xml/>")
48
52
  end
49
53
 
50
- def save
51
- @content = to_xml if ng_xml_changed? and content_will_change!
52
- super
53
- end
54
-
55
-
56
54
  def ng_xml
57
55
  @ng_xml ||= begin
58
- self.xml_loaded = true
59
-
60
56
  if new?
61
57
  ## Load up the template
62
58
  self.class.xml_template
@@ -67,26 +63,16 @@ module ActiveFedora
67
63
  end
68
64
 
69
65
  def ng_xml=(new_xml)
70
-
71
- nokogiri_document = case new_xml
66
+ case new_xml
72
67
  when Nokogiri::XML::Document
73
- new_xml
68
+ @ng_xml = new_xml
74
69
  when Nokogiri::XML::Node
75
- Nokogiri::XML(new_xml.to_s) ## Cast a fragment to a document
70
+ @ng_xml = Nokogiri::XML(new_xml.to_s) ## Cast a fragment to a document
76
71
  when String
77
- Nokogiri::XML::Document.parse(new_xml)
72
+ @ng_xml = Nokogiri::XML::Document.parse(new_xml)
78
73
  else
79
74
  raise TypeError, "You passed a #{new_xml.class} into the ng_xml of the #{self.dsid} datastream. NokogiriDatastream.ng_xml= only accepts Nokogiri::XML::Document, Nokogiri::XML::Element, Nokogiri::XML::Node, or raw XML (String) as inputs."
80
75
  end
81
-
82
-
83
- new_xml_string = nokogiri_document.to_xml {|config| config.no_declaration}
84
-
85
- ng_xml_will_change! unless (xml_loaded && (new_xml_string.to_s.strip == (datastream_content || '').strip))
86
- self.xml_loaded=true
87
-
88
- @ng_xml = nokogiri_document
89
-
90
76
  end
91
77
 
92
78
  # don't want content eagerly loaded by proxy, so implementing methods that would be implemented by define_attribute_methods
@@ -100,27 +86,7 @@ module ActiveFedora
100
86
 
101
87
  # don't want content eagerly loaded by proxy, so implementing methods that would be implemented by define_attribute_methods
102
88
  def ng_xml_changed?
103
- return true if changed_attributes.has_key?('ng_xml')
104
-
105
- return false unless xml_loaded
106
-
107
- if new?
108
- !to_xml.empty?
109
- else
110
- (to_xml.strip != (datastream_content || '').strip)
111
- end
112
- end
113
-
114
- def changed?
115
- ng_xml_changed? || super
116
- end
117
-
118
- def content_changed?
119
- ng_xml_changed? || super
120
- end
121
-
122
- def has_content?
123
- xml_loaded || super
89
+ changed_attributes.has_key? 'ng_xml'
124
90
  end
125
91
 
126
92
  # Indicates that this datastream has metadata content.
@@ -129,21 +95,23 @@ module ActiveFedora
129
95
  true
130
96
  end
131
97
 
132
- def content=(content)
133
- content_will_change! unless (xml_loaded && (content == datastream_content))
134
- @content = content
135
- self.xml_loaded=true
136
- self.ng_xml = Nokogiri::XML::Document.parse(datastream_content)
98
+ def content
99
+ to_xml
137
100
  end
138
101
 
139
- alias :datastream_content :content
140
-
141
- def content
142
- return to_xml if xml_loaded or new?
143
-
144
- datastream_content
102
+ def datastream_content
103
+ @datastream_content ||= Nokogiri::XML(super).to_xml {|config| config.no_declaration}.strip
145
104
  end
146
105
 
106
+ def content=(content)
107
+ @ng_xml = Nokogiri::XML::Document.parse(content)
108
+ end
109
+
110
+ def content_changed?
111
+ return false if new? and !xml_loaded
112
+ super
113
+ end
114
+
147
115
  def to_xml(xml = nil)
148
116
  xml = self.ng_xml if xml.nil?
149
117
  ng_xml = self.ng_xml
@@ -164,7 +132,7 @@ module ActiveFedora
164
132
  end
165
133
  end
166
134
 
167
- return xml.to_xml {|config| config.no_declaration}
135
+ return xml.to_xml {|config| config.no_declaration}.strip
168
136
  end
169
137
 
170
138
  # ** Experimental **
@@ -427,6 +395,18 @@ module ActiveFedora
427
395
  om_term_values(*term_pointer)
428
396
  end
429
397
  end
398
+
399
+ # Deprecated methods left here for backwards compatibility
400
+ def ensure_xml_loaded; end
401
+ deprecation_deprecate :ensure_xml_loaded
402
+
403
+ def serialize!
404
+ end
405
+
406
+ def xml_loaded
407
+ instance_variable_defined? :@ng_xml
408
+ end
409
+
430
410
  end
431
411
  end
432
412
 
@@ -2,10 +2,12 @@ module ActiveFedora
2
2
  class RDFDatastream < Datastream
3
3
 
4
4
  before_save do
5
- return unless graph_changed?
6
- content_will_change!
7
- end
8
-
5
+ if content.blank?
6
+ logger.warn "Cowardly refusing to save a datastream with empty content: #{self.inspect}"
7
+ false
8
+ end
9
+ end
10
+
9
11
  # this enables a cleaner API for solr integration
10
12
  class IndexObject
11
13
  attr_accessor :data_type, :behaviors
@@ -24,154 +26,173 @@ module ActiveFedora
24
26
  end
25
27
  end
26
28
 
27
- module ModelMethods
28
- extend ActiveSupport::Concern
29
- module ClassMethods
30
- attr_accessor :vocabularies
31
- def config
32
- ActiveFedora::Predicates.predicate_config
33
- end
34
- def prefix(name)
35
- name = name.to_s unless name.is_a? String
36
- pre = self.to_s.sub(/RDFDatastream$/i, '').underscore
37
- return "#{pre}__#{name}".to_sym
38
- end
39
-
40
- ##
41
- # Register a ruby block that evaluates to the subject of the graph
42
- # By default, the block returns the current object's pid
43
- # @yield [ds] 'ds' is the datastream instance
44
- def rdf_subject &block
45
- if block_given?
46
- return @subject_block = block
47
- end
29
+ class << self
30
+ attr_accessor :vocabularies
31
+ def config
32
+ ActiveFedora::Predicates.predicate_config
33
+ end
34
+ def prefix(name)
35
+ name = name.to_s unless name.is_a? String
36
+ pre = self.to_s.sub(/RDFDatastream$/i, '').underscore
37
+ return "#{pre}__#{name}".to_sym
38
+ end
48
39
 
49
- @subject_block ||= lambda { |ds| "info:fedora/#{ds.pid}" }
40
+ ##
41
+ # Register a ruby block that evaluates to the subject of the graph
42
+ # By default, the block returns the current object's pid
43
+ # @yield [ds] 'ds' is the datastream instance
44
+ def rdf_subject &block
45
+ if block_given?
46
+ return @subject_block = block
50
47
  end
51
48
 
52
- def register_vocabularies(*vocabs)
53
- @vocabularies ||= {}
54
- vocabs.each do |v|
55
- if v.is_a?(RDF::Vocabulary) or (v.respond_to? :property and v.respond_to? :to_uri)
56
- @vocabularies[v.to_uri] = v
57
- else
58
- raise "not an RDF vocabulary: #{v}"
59
- end
49
+ @subject_block ||= lambda { |ds| RDF::URI.new("info:fedora/#{ds.pid}") }
50
+ end
51
+
52
+ def register_vocabularies(*vocabs)
53
+ @vocabularies ||= {}
54
+ vocabs.each do |v|
55
+ if v.is_a?(RDF::Vocabulary) or (v.respond_to? :property and v.respond_to? :to_uri)
56
+ @vocabularies[v.to_uri] = v
57
+ else
58
+ raise "not an RDF vocabulary: #{v}"
60
59
  end
61
- ActiveFedora::Predicates.vocabularies(@vocabularies)
62
- @vocabularies
63
60
  end
61
+ ActiveFedora::Predicates.vocabularies(@vocabularies)
62
+ @vocabularies
63
+ end
64
64
 
65
- def map_predicates(&block)
66
- yield self
65
+ def map_predicates(&block)
66
+ yield self
67
+ end
68
+ def method_missing(name, *args, &block)
69
+ args = args.first if args.respond_to? :first
70
+ raise "mapping must specify RDF vocabulary as :in argument" unless args.has_key? :in
71
+ vocab = args[:in]
72
+ predicate = args.fetch(:to, name)
73
+ raise "Vocabulary '#{vocab.inspect}' does not define property '#{predicate.inspect}'" unless vocab.respond_to? predicate
74
+ indexing = false
75
+ if block_given?
76
+ # needed for solrizer integration
77
+ indexing = true
78
+ iobj = IndexObject.new
79
+ yield iobj
80
+ data_type = iobj.data_type
81
+ behaviors = iobj.behaviors
67
82
  end
68
- def method_missing(name, *args, &block)
69
- args = args.first if args.respond_to? :first
70
- raise "mapping must specify RDF vocabulary as :in argument" unless args.has_key? :in
71
- vocab = args[:in]
72
- predicate = args.fetch(:to, name)
73
- raise "Vocabulary '#{vocab.inspect}' does not define property '#{predicate.inspect}'" unless vocab.respond_to? predicate
74
- indexing = false
75
- if block_given?
76
- # needed for solrizer integration
77
- indexing = true
78
- iobj = IndexObject.new
79
- yield iobj
80
- data_type = iobj.data_type
81
- behaviors = iobj.behaviors
82
- end
83
- # needed for AF::Predicates integration & drives all other
84
- # functionality below
85
- vocab = vocab.to_s
86
- name = self.prefix(name)
87
- if config
88
- if config[:predicate_mapping].has_key? vocab
89
- config[:predicate_mapping][vocab][name] = predicate
90
- else
91
- config[:predicate_mapping][vocab] = { name => predicate }
92
- end
93
- # stuff data_type and behaviors in there for to_solr support
94
- config[:predicate_mapping][vocab]["#{name}__type".to_sym] = data_type if indexing
95
- config[:predicate_mapping][vocab]["#{name}__behaviors".to_sym] = behaviors if indexing
83
+ # needed for AF::Predicates integration & drives all other
84
+ # functionality below
85
+ vocab = vocab.to_s
86
+ name = self.prefix(name)
87
+ if config
88
+ if config[:predicate_mapping].has_key? vocab
89
+ config[:predicate_mapping][vocab][name] = predicate
96
90
  else
97
- config = {
98
- :default_namespace => vocab,
99
- :predicate_mapping => {
100
- vocab => { name => predicate }
101
- }
102
- }
103
- # stuff data_type and behaviors in there for to_solr support
104
- config[:predicate_mapping][vocab]["#{name}__type".to_sym] = data_type if indexing
105
- config[:predicate_mapping][vocab]["#{name}__behaviors".to_sym] = behaviors if indexing
91
+ config[:predicate_mapping][vocab] = { name => predicate }
106
92
  end
93
+ # stuff data_type and behaviors in there for to_solr support
94
+ config[:predicate_mapping][vocab]["#{name}__type".to_sym] = data_type if indexing
95
+ config[:predicate_mapping][vocab]["#{name}__behaviors".to_sym] = behaviors if indexing
96
+ else
97
+ config = {
98
+ :default_namespace => vocab,
99
+ :predicate_mapping => {
100
+ vocab => { name => predicate }
101
+ }
102
+ }
103
+ # stuff data_type and behaviors in there for to_solr support
104
+ config[:predicate_mapping][vocab]["#{name}__type".to_sym] = data_type if indexing
105
+ config[:predicate_mapping][vocab]["#{name}__behaviors".to_sym] = behaviors if indexing
107
106
  end
108
107
  end
109
108
  end
109
+
110
+
110
111
  class TermProxy
111
- # @param [Symbol, RDF::URI] predicate the predicate to insert into the graph
112
- # @param [ActiveFedora::RelationshipGraph] graph the graph
113
- # @param [Array] values an array of object values
114
- instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to|proxy_/ }
115
-
116
- def initialize(graph, predicate, values=[])
112
+
113
+ attr_reader :graph, :subject, :predicate
114
+ delegate :class, :to_s, :==, :kind_of?, :each, :map, :empty?, :to => :values
115
+
116
+ def initialize(graph, subject, predicate)
117
117
  @graph = graph
118
+
119
+ @subject = subject
118
120
  @predicate = predicate
119
- @values = values
120
121
  end
122
+
121
123
  def <<(*values)
122
- @values.concat(values)
123
- values.each { |value| @graph.add(@predicate, value, true) }
124
- @graph.dirty = true
125
- @values
124
+ values.each { |value| graph.append(subject, predicate, value) }
125
+ values
126
126
  end
127
+
127
128
  def delete(*values)
128
129
  values.each do |value|
129
- unless @values.delete(value).nil?
130
- @graph.delete(@predicate, value)
131
- @graph.dirty = true
132
- end
130
+ graph.delete_predicate(subject, predicate, value)
133
131
  end
134
- @values
132
+
133
+ values
135
134
  end
136
- def method_missing(method, *args)
137
- unless @values.respond_to?(method)
138
- message = "undefined method `#{method.to_s}' for \"#{@values}\":#{@values.class.to_s}"
139
- raise NoMethodError, message
135
+
136
+ def values
137
+ values = []
138
+
139
+ graph.query(subject, predicate).each do |solution|
140
+ v = solution.value
141
+ v = v.to_s if v.is_a? RDF::Literal
142
+ values << v
140
143
  end
141
144
 
142
- if block_given?
143
- @values.send(method, *args) { |*block_args| yield(*block_args) }
145
+ values
146
+ end
147
+
148
+ def method_missing(method, *args, &block)
149
+
150
+ if values.respond_to? method
151
+ values.send(method, *args, &block)
144
152
  else
145
- @values.send(method, *args)
153
+ super
146
154
  end
147
-
148
155
  end
149
156
  end
150
157
 
151
- include ModelMethods
152
158
  attr_accessor :loaded
153
-
154
159
  def metadata?
155
160
  true
156
161
  end
162
+ def ensure_loaded
163
+ end
157
164
 
158
- def save
159
- @content = serialize if graph_changed? and content_will_change!
160
-
161
- super
165
+ def content
166
+ serialize
162
167
  end
163
168
 
164
169
  def content=(content)
165
- content_will_change!
166
170
  self.loaded = true
167
171
  @graph = deserialize(content)
168
172
  end
169
173
 
174
+ def content_changed?
175
+ return false if new? and !loaded
176
+ super
177
+ end
178
+
179
+ def changed?
180
+ super || content_changed?
181
+ end
182
+
170
183
  # returns a Hash, e.g.: {field => {:values => [], :type => :something, :behaviors => []}, ...}
171
184
  def fields
172
-
173
185
  field_map = {}
174
- graph.relationships.each do |predicate, values|
186
+
187
+ rdf_subject = self.rdf_subject
188
+ query = RDF::Query.new do
189
+ pattern [rdf_subject, :predicate, :value]
190
+ end
191
+
192
+ query.execute(graph).each do |solution|
193
+ predicate = solution.predicate
194
+ value = solution.value
195
+
175
196
  vocab_sym, name = predicate.qname
176
197
  uri, vocab = self.class.vocabularies.select { |ns, v| v.__prefix__ == vocab_sym }.first
177
198
  next unless vocab
@@ -182,13 +203,13 @@ module ActiveFedora
182
203
  next unless name and config.has_key?("#{name}__type".to_sym) and config.has_key?("#{name}__behaviors".to_sym)
183
204
  type = config["#{name}__type".to_sym]
184
205
  behaviors = config["#{name}__behaviors".to_sym]
185
- field_map[name.to_sym] = {:values => values.map {|v| v.to_s}, :type => type, :behaviors => behaviors}
206
+ field_map[name.to_sym] ||= {:values => [], :type => type, :behaviors => behaviors}
207
+ field_map[name.to_sym][:values] << value.to_s
186
208
  end
187
209
  field_map
188
210
  end
189
211
 
190
212
  def to_solr(solr_doc = Hash.new) # :nodoc:
191
-
192
213
  fields.each do |field_key, field_info|
193
214
  values = field_info.fetch(:values, false)
194
215
  if values
@@ -215,100 +236,90 @@ module ActiveFedora
215
236
  # Assumes that the datastream contains RDF content
216
237
  # @param [String] data the "rdf" node
217
238
  def deserialize(data = nil)
218
- graph = RelationshipGraph.new
219
- return graph if new? and data.nil?
239
+ repository = RDF::Repository.new
240
+ return repository if new? and data.nil?
220
241
 
221
242
  data ||= datastream_content
222
243
 
223
- RDF::Reader.for(serialization_format).new(data) do |reader|
224
- reader.each_statement do |statement|
225
- begin
226
- next unless statement.subject == rdf_subject
227
- rescue
228
- end
229
- literal = statement.object.kind_of?(RDF::Literal)
230
- object = literal ? statement.object.value : statement.object.to_s
231
- graph.add(statement.predicate, object, literal)
232
- end
244
+ RDF::Reader.for(serialization_format).new(data) do |reader|
245
+ reader.each_statement do |statement|
246
+ repository << statement
233
247
  end
248
+ end
234
249
 
235
- graph
250
+ repository
236
251
  end
237
252
 
238
253
  def graph
239
254
  @graph ||= begin
240
255
  self.loaded = true
241
256
  deserialize
242
- end
243
- end
244
-
245
- # alias the original datastream content
246
- alias :datastream_content :content
247
-
248
- def content
249
- # return the RDF graph content if it's available (or all we have)
250
- return serialize if loaded or new?
251
-
252
- # otherwise, grab the data from the datastore
253
- super
257
+ end
254
258
  end
255
-
256
- def graph_will_change!
257
- changed_attributes['graph'] = nil
258
- end
259
-
260
- def graph_changed?
261
- return true if changed_attributes.has_key?('graph')
262
- return false unless loaded
263
259
 
264
- if new?
265
- !serialize.empty?
266
- else
267
- serialize.strip != (datastream_content || '').strip
260
+ def query subject, predicate, &block
261
+ predicate = find_predicate(predicate) unless predicate.kind_of? RDF::URI
262
+
263
+ q = RDF::Query.new do
264
+ pattern [subject, predicate, :value]
268
265
  end
269
266
 
270
- end
271
-
272
- def changed?
273
- graph_changed? || super
267
+ q.execute(graph, &block)
274
268
  end
275
269
 
276
270
  # @param [Symbol, RDF::URI] predicate the predicate to insert into the graph
277
- def get_values(predicate)
278
-
271
+ def get_values(subject, predicate)
272
+
279
273
  predicate = find_predicate(predicate) unless predicate.kind_of? RDF::URI
280
- results = graph[predicate]
281
- return if results.nil?
282
- values = []
283
- results.each do |object|
284
- values << (object.kind_of?(RDF::Literal) ? object.value : object.to_s)
285
- end
286
- TermProxy.new(graph, predicate, values)
274
+
275
+ return TermProxy.new(self, subject, predicate)
287
276
  end
288
277
 
289
278
  # if there are any existing statements with this predicate, replace them
290
279
  # @param [Symbol, RDF::URI] predicate the predicate to insert into the graph
291
- def set_value(predicate, args)
280
+
281
+ def set_value(subject, predicate, values)
292
282
 
293
283
  predicate = find_predicate(predicate) unless predicate.kind_of? RDF::URI
294
- graph.delete(predicate)
295
- args = [args] unless args.respond_to? :each
296
- args.each do |arg|
284
+
285
+ delete_predicate(subject, predicate)
286
+
287
+ Array(values).each do |arg|
297
288
  arg = arg.to_s if arg.kind_of? RDF::Literal
298
- graph.add(predicate, arg, true) unless arg.empty?
289
+ next if arg.empty?
290
+
291
+ graph.insert([subject, predicate, arg])
299
292
  end
300
- graph.dirty = true
301
- return TermProxy.new(graph, predicate, args)
293
+
294
+ return TermProxy.new(self, subject, predicate)
295
+ end
296
+
297
+ def delete_predicate(subject, predicate, values = nil)
298
+ predicate = find_predicate(predicate) unless predicate.kind_of? RDF::URI
299
+
300
+ if values.nil?
301
+ query = RDF::Query.new do
302
+ pattern [subject, predicate, :value]
303
+ end
304
+
305
+ query.execute(graph).each do |solution|
306
+ graph.delete [subject, predicate, solution.value]
307
+ end
308
+ else
309
+ Array(values).each do |v|
310
+ graph.delete [subject, predicate, v]
311
+ end
312
+ end
313
+
302
314
  end
303
315
 
304
316
  # append a value
305
317
  # @param [Symbol, RDF::URI] predicate the predicate to insert into the graph
306
- def append(predicate, args)
307
-
308
- predicate = find_predicate(predicate) unless predicate.kind_of? RDF::URI
309
- graph.add(predicate, args, true)
310
- graph.dirty = true
311
- return TermProxy.new(graph, predicate, args)
318
+ def append(subject, predicate, args)
319
+ graph.insert([subject, predicate, args])
320
+
321
+
322
+ return TermProxy.new(self, subject, predicate)
312
323
  end
313
324
 
314
325
  def serialization_format
@@ -317,29 +328,54 @@ module ActiveFedora
317
328
 
318
329
  def method_missing(name, *args)
319
330
  if (md = /^([^=]+)=$/.match(name.to_s)) && pred = find_predicate(md[1])
320
- set_value(pred, *args)
331
+ set_value(rdf_subject, pred, *args)
321
332
  elsif pred = find_predicate(name)
322
- get_values(name)
333
+ get_values(rdf_subject, name)
323
334
  else
324
335
  super
325
336
  end
337
+ rescue ActiveFedora::UnregisteredPredicateError
338
+ super
326
339
  end
327
340
 
328
341
  ##
329
342
  # Get the subject for this rdf/xml datastream
330
343
  def rdf_subject
331
- @subject ||= self.class.rdf_subject.call(self)
344
+ @subject ||= begin
345
+ s = self.class.rdf_subject.call(self)
346
+ s &&= RDF::URI.new(s) if s.is_a? String
347
+ s
348
+ end
332
349
  end
333
350
 
351
+ def reset_rdf_subject!
352
+ @subject = nil
353
+ end
354
+
334
355
  # Creates a RDF datastream for insertion into a Fedora Object
335
356
  # Note: This method is implemented on SemanticNode instead of RelsExtDatastream because SemanticNode contains the relationships array
336
357
  def serialize
337
- out = RDF::Writer.for(serialization_format).buffer do |writer|
338
- graph.to_graph(rdf_subject).each_statement do |statement|
339
- writer << statement
340
- end
358
+ update_subjects_to_use_a_real_pid!
359
+ RDF::Writer.for(serialization_format).dump(graph)
360
+ end
361
+
362
+ def update_subjects_to_use_a_real_pid!
363
+ return unless new?
364
+
365
+ bad_subject = rdf_subject
366
+ reset_rdf_subject!
367
+ new_subject = rdf_subject
368
+
369
+ new_repository = RDF::Repository.new
370
+
371
+ graph.each_statement do |statement|
372
+ subject = statement.subject
373
+
374
+ subject &&= new_subject if subject == bad_subject
375
+ new_repository << [subject, statement.predicate, statement.object]
341
376
  end
342
- out
377
+
378
+ @graph = new_repository
343
379
  end
344
380
  end
345
381
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveFedora
2
- VERSION = "5.2.1"
2
+ VERSION = "5.3.0"
3
3
  end
@@ -48,11 +48,11 @@ describe ActiveFedora::Datastream do
48
48
  ds.content = fixture('dino.jpg')
49
49
  @test_object.add_datastream(ds).should be_true
50
50
  @test_object.save
51
+ @test_object.datastreams[dsid].should_not be_changed
51
52
  to = ActiveFedora::Base.find(@test_object.pid)
52
53
  to.should_not be_nil
53
54
  to.datastreams[dsid].should_not be_nil
54
55
  to.datastreams[dsid].content.should == fixture('dino.jpg').read
55
-
56
56
  end
57
57
 
58
58
  it "should be able to set the versionable attribute" do
@@ -26,20 +26,13 @@ describe ActiveFedora::NtriplesRDFDatastream do
26
26
  Object.send(:remove_const, :MyDatastream)
27
27
  end
28
28
 
29
- it "should set and recall" do
30
- @subject.title = "John Doe"
29
+ it "should not try to send an empty datastream" do
31
30
  @subject.save
32
- f = RdfTest.find(@subject.pid)
33
- f.title.should == "John Doe"
34
- f.title = "Jane Doe"
35
- f.save
36
- new_object = RdfTest.find(@subject.pid)
37
- new_object.title.should == "Jane Doe"
38
-
39
31
  end
40
32
 
41
33
  it "should set and recall values" do
42
34
  @subject.title = 'War and Peace'
35
+ @subject.rdf.should be_changed
43
36
  @subject.based_near = "Moscow, Russia"
44
37
  @subject.related_url = "http://en.wikipedia.org/wiki/War_and_Peace"
45
38
  @subject.part = "this is a part"
@@ -55,6 +48,7 @@ describe ActiveFedora::NtriplesRDFDatastream do
55
48
  @subject.related_url = "http://en.wikipedia.org/wiki/War_and_Peace"
56
49
  @subject.part = "this is a part"
57
50
  @subject.save
51
+
58
52
  loaded = RdfTest.find(@subject.pid)
59
53
  loaded.title.should == 'War and Peace'
60
54
  loaded.based_near.should == ['Moscow, Russia']
@@ -82,6 +76,13 @@ describe ActiveFedora::NtriplesRDFDatastream do
82
76
  @subject.save
83
77
  @subject.title.should be_nil
84
78
  end
79
+
80
+ it "should be able to save a blank document" do
81
+ @subject.title = ""
82
+ @subject.save
83
+ end
84
+
85
+
85
86
  it "should delete values" do
86
87
  @subject.title = "Hamlet"
87
88
  @subject.related_url = "http://psu.edu/"
@@ -22,7 +22,6 @@ describe ActiveFedora::OmDatastream do
22
22
 
23
23
  describe "#changed?" do
24
24
  it "should not be changed if the new xml matches the old xml" do
25
-
26
25
  @pid = "hydrangea:fixture_mods_article2"
27
26
  @test_object = HydrangeaArticle2.find(@pid)
28
27
 
@@ -30,6 +29,27 @@ describe ActiveFedora::OmDatastream do
30
29
 
31
30
  @test_object.descMetadata.should_not be_changed
32
31
  end
32
+
33
+
34
+ it "should not be changed if there are minor differences in whitespace" do
35
+
36
+ obj = HydrangeaArticle2.new
37
+ obj.descMetadata.content = "<a>1</a>"
38
+ obj.save
39
+ obj.descMetadata.should_not be_changed
40
+ obj.descMetadata.content = "<a>1</a>\n"
41
+ obj.descMetadata.should_not be_changed
42
+
43
+ end
44
+ end
45
+
46
+ describe "empty datastream content" do
47
+ it "should not break when there is empty datastream content" do
48
+ obj = HydrangeaArticle2.new
49
+ obj.descMetadata.content = ""
50
+ obj.save
51
+
52
+ end
33
53
  end
34
54
 
35
55
  describe '.term_values' do
@@ -81,6 +81,12 @@ describe ActiveFedora::DatastreamCollections do
81
81
  @test_object2.add_named_datastream("thumbnail",{:content_type=>"image/jpeg",:file=>@f})
82
82
  end
83
83
 
84
+ it "should allow access to file content" do
85
+ @test_object2.add_named_datastream("thumbnail",{:content_type=>"image/jpeg",:file=>@f})
86
+ @test_object2.save!
87
+ @test_object2.thumbnail[0].content
88
+ end
89
+
84
90
  it "should raise an error if neither a blob nor file is set" do
85
91
  expect { @test_object2.add_named_datastream("thumbnail",{:content_type=>"image/jpeg"}) }.to raise_error
86
92
  end
@@ -61,30 +61,6 @@ describe ActiveFedora::Datastream do
61
61
  end
62
62
  end
63
63
 
64
- describe '#save' do
65
- it "should set changed" do
66
- mock_repo = mock('repository')
67
- mock_repo.stub(:config).and_return({})
68
- mock_repo.stub(:add_datastream).with(:versionable => true, :pid => @test_object.pid, :dsid => 'abcd', :controlGroup => 'M', :dsState => 'A', :content => 'hi there')
69
- mock_repo.stub(:datastream).with(:dsid => 'abcd', :pid => @test_object.pid).and_return('')
70
- @test_object.inner_object.stub(:repository).and_return(mock_repo)
71
- @test_datastream.save
72
- @test_datastream.should_not be_changed
73
- end
74
- end
75
-
76
- describe '.content=' do
77
- it "should update the content and ng_xml, marking the datastream as changed" do
78
- sample_xml = "<foo><xmlelement/></foo>"
79
- @test_datastream.instance_variable_get(:@changed_attributes).clear
80
- @test_datastream.should_not be_changed
81
- @test_datastream.content.should_not be_equivalent_to(sample_xml)
82
- @test_datastream.content = sample_xml
83
- @test_datastream.should be_changed
84
- @test_datastream.content.should be_equivalent_to(sample_xml)
85
- end
86
- end
87
-
88
64
  it "should have mimeType accessors" do
89
65
  ds1 = ActiveFedora::Datastream.new
90
66
  ds1.mimeType = "text/foo"
@@ -53,7 +53,7 @@ describe ActiveFedora::NtriplesRDFDatastream do
53
53
  @subject.created.should be_kind_of Array
54
54
  end
55
55
  it "should have method missing" do
56
- lambda{@subject.frank}.should raise_exception ActiveFedora::UnregisteredPredicateError
56
+ lambda{@subject.frank}.should raise_exception NoMethodError
57
57
  end
58
58
 
59
59
  it "should set fields" do
@@ -69,7 +69,7 @@ describe ActiveFedora::NtriplesRDFDatastream do
69
69
  @subject.publisher.should == ["Penn State", "St. Martin's Press"]
70
70
  end
71
71
  it "should delete fields" do
72
- @subject.related_url.delete("http://google.com/")
72
+ @subject.related_url.delete(RDF::URI("http://google.com/"))
73
73
  @subject.related_url.should == []
74
74
  end
75
75
  end
@@ -166,10 +166,10 @@ describe ActiveFedora::NtriplesRDFDatastream do
166
166
  map.rights(:in => RDF::DC)
167
167
  end
168
168
  end
169
- end
170
- before(:each) do
171
169
  @subject = MyDatastream.new(@inner_object, 'solr_rdf')
172
170
  @subject.content = File.new('spec/fixtures/solr_rdf_descMetadata.nt').read
171
+ end
172
+ before(:each) do
173
173
  @subject.stub(:pid => 'test:1')
174
174
  @subject.stub(:new? => false)
175
175
  @sample_fields = {:my_datastream__publisher => {:values => ["publisher1"], :type => :string, :behaviors => [:facetable, :sortable, :searchable, :displayable]},
@@ -272,15 +272,6 @@ describe ActiveFedora::NtriplesRDFDatastream do
272
272
  @obj.save
273
273
  end
274
274
 
275
- describe '.content=' do
276
- it "should update the content and graph, marking the datastream as changed" do
277
- sample_rdf = File.new('spec/fixtures/mixed_rdf_descMetadata.nt').read
278
- @obj.stub(:new? => false)
279
- @obj.should_not be_changed
280
- @obj.content = sample_rdf
281
- @obj.should be_changed
282
- end
283
- end
284
275
  it "should save content properly upon save" do
285
276
  foo = Foo.new(:pid => 'test:1')
286
277
  foo.title = 'Hamlet'
@@ -22,6 +22,7 @@ describe ActiveFedora::OmDatastream do
22
22
  @test_ds = ActiveFedora::OmDatastream.new(@mock_inner, "descMetadata")
23
23
  @test_ds.stub(:new? => false, :profile => {}, :datastream_content => '<test_xml/>')
24
24
  @test_ds.content="<test_xml/>"
25
+ @test_ds.stub(:new? => false)
25
26
  end
26
27
 
27
28
  after(:each) do
@@ -46,6 +47,7 @@ describe ActiveFedora::OmDatastream do
46
47
  it "should initialize from #xml_template if no xml is provided" do
47
48
  ActiveFedora::OmDatastream.should_receive(:xml_template).and_return("<fake template/>")
48
49
  n = ActiveFedora::OmDatastream.new
50
+ n.ensure_xml_loaded
49
51
  n.ng_xml.should be_equivalent_to("<fake template/>")
50
52
  end
51
53
  end
@@ -158,15 +160,6 @@ describe ActiveFedora::OmDatastream do
158
160
  @mods_ds.get_values("--my xpath--").should == ["abstract1", "abstract2"]
159
161
  end
160
162
  end
161
-
162
- describe '#from_xml' do
163
- it "should work when a template datastream is passed in" do
164
- mods_xml = Nokogiri::XML::Document.parse( fixture(File.join("mods_articles", "hydrangea_article1.xml")) )
165
- tmpl = Hydra::ModsArticleDatastream.new
166
- Hydra::ModsArticleDatastream.from_xml(mods_xml,tmpl).ng_xml.root.to_xml.should == mods_xml.root.to_xml
167
- end
168
- end
169
-
170
163
 
171
164
  it 'should provide .fields' do
172
165
  @test_ds.should respond_to(:fields)
@@ -177,6 +170,7 @@ describe ActiveFedora::OmDatastream do
177
170
  @test_ds.should respond_to(:save)
178
171
  end
179
172
  it "should persist the product of .to_xml in fedora" do
173
+ @mock_repo.stub(:datastream).and_return('')
180
174
  @test_ds.stub(:new? => true)
181
175
  @test_ds.stub(:to_xml => "fake xml")
182
176
  @mock_repo.should_receive(:add_datastream).with(:pid => nil, :dsid => 'descMetadata', :versionable => true, :content => 'fake xml', :controlGroup => 'X', :dsState => 'A', :mimeType=>'text/xml')
@@ -192,11 +186,11 @@ describe ActiveFedora::OmDatastream do
192
186
  it "should update the content" do
193
187
  subject.stub(:new? => false )
194
188
  subject.content = "<a />"
195
- subject.datastream_content.should == '<a />'
189
+ subject.content.should == '<a/>'
196
190
  end
197
191
 
198
192
  it "should mark the object as changed" do
199
- subject.stub(:new? => false )
193
+ subject.stub(:new? => false, :controlGroup => 'M')
200
194
  subject.content = "<a />"
201
195
  subject.should be_changed
202
196
  end
@@ -211,6 +205,7 @@ describe ActiveFedora::OmDatastream do
211
205
 
212
206
  describe 'ng_xml=' do
213
207
  before do
208
+ @mock_inner.stub(:new? => true)
214
209
  @test_ds2 = ActiveFedora::OmDatastream.new(@mock_inner, "descMetadata")
215
210
  end
216
211
  it "should parse raw xml for you" do
@@ -225,9 +220,9 @@ describe ActiveFedora::OmDatastream do
225
220
  @test_ds2.ng_xml.to_xml.should be_equivalent_to("<xmlelement/>")
226
221
  end
227
222
  it "should mark the datastream as changed" do
223
+ @test_ds2.stub(:new? => false, :controlGroup => 'M')
228
224
  @test_ds2.should_not be_changed
229
225
  @test_ds2.ng_xml = @sample_raw_xml
230
- @test_ds2.ng_xml_changed?.should be_true
231
226
  @test_ds2.should be_changed
232
227
  @test_ds2.instance_variable_get(:@content).should be_nil
233
228
  end
@@ -240,7 +235,7 @@ describe ActiveFedora::OmDatastream do
240
235
 
241
236
  it "should ng_xml.to_xml" do
242
237
  @test_ds.stub(:ng_xml => Nokogiri::XML::Document.parse("<text_document/>"))
243
- @test_ds.to_xml.should == "<text_document/>\n"
238
+ @test_ds.to_xml.should == "<text_document/>"
244
239
  end
245
240
 
246
241
  it 'should accept an optional Nokogiri::XML Document as an argument and insert its fields into that (mocked test)' do
@@ -53,16 +53,6 @@ describe ActiveFedora::QualifiedDublinCoreDatastream do
53
53
 
54
54
  end
55
55
 
56
- it "should parse dcterms and dcelements from xml" do
57
- doc = Nokogiri::XML::Document.parse(File.open( File.dirname(__FILE__)+'/../fixtures/changeme155.xml') )
58
- stream = doc.xpath('//foxml:datastream[@ID=\'dublin_core\']/foxml:datastreamVersion/foxml:xmlContent/dc')
59
- ds = ActiveFedora::QualifiedDublinCoreDatastream.new
60
- n = ActiveFedora::QualifiedDublinCoreDatastream.from_xml(stream.to_xml, ds)
61
- n.spatial.should == ["Boston [7013445]", "Dorchester [7013575]", "Roxbury [7015002]"]
62
- n.title.should == ["Oral history with Frances Addelson, 1997 November 14"]
63
- end
64
-
65
-
66
56
  it "should default dc elements to :multiple=>true" do
67
57
  @test_ds.fields.values.each do |s|
68
58
  s.has_key?(:multiple).should == true
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active-fedora
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.1
4
+ version: 5.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2012-12-12 00:00:00.000000000 Z
14
+ date: 2012-12-20 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rsolr
@@ -132,7 +132,7 @@ dependencies:
132
132
  requirements:
133
133
  - - ~>
134
134
  - !ruby/object:Gem::Version
135
- version: '1.0'
135
+ version: '1.2'
136
136
  type: :runtime
137
137
  prerelease: false
138
138
  version_requirements: !ruby/object:Gem::Requirement
@@ -140,7 +140,7 @@ dependencies:
140
140
  requirements:
141
141
  - - ~>
142
142
  - !ruby/object:Gem::Version
143
- version: '1.0'
143
+ version: '1.2'
144
144
  - !ruby/object:Gem::Dependency
145
145
  name: rdf
146
146
  requirement: !ruby/object:Gem::Requirement
@@ -547,7 +547,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
547
547
  version: '0'
548
548
  segments:
549
549
  - 0
550
- hash: -1381095155313457398
550
+ hash: 1953384027412583445
551
551
  requirements: []
552
552
  rubyforge_project: rubyfedora
553
553
  rubygems_version: 1.8.23