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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 155ad613119b5eb0ce0333d32c7bb0fb67009847
4
- data.tar.gz: d02a5f3a7617168e35a90ff28dec878ef8ec46cf
3
+ metadata.gz: 7a37149e295c1dc3110b4fc70371e8bb25f980a8
4
+ data.tar.gz: b66fcc86fa8132818a1b40e8b1e9f06588d5d2a4
5
5
  SHA512:
6
- metadata.gz: 0ce6042eaaeca85fc1d000a6fdb593e8ef30cf7fd614e5f9a0a973fa5e33ff3b3a4c9d85ad02e2e9f4d5be3a7742d63f3634181842910aad93efb5d223557bbd
7
- data.tar.gz: 798590c4f7b93fe8b3cde0149162806bf7f6da20cbac133bdf11eb17ef18fce5cb3004a149c68243e29e3600b3c6ff1fbca9ddf126af320b1c6da4be1ac25702
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
- if args.present?
61
- instance_exec(*args, &self.class.defined_attributes[field].reader)
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
- instance_exec(args, &self.class.defined_attributes[field].writer)
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, :reader, :writer, :multiple
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
- private
37
-
38
- def initialize_writer!
39
- this = self
40
- self.writer = lambda do |v|
41
- ds = datastream_for_attribute(this.dsid)
42
- mark_as_changed(this.field) if value_has_changed?(this.field, v)
43
- if ds.kind_of?(ActiveFedora::RDFDatastream)
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 initialize_reader!
53
- this = self
54
- self.reader = lambda do |*opts|
55
- if inner_object.is_a? SolrDigitalObject
56
- begin
57
- # Look in the cache
58
- return inner_object.fetch(this.field)
59
- rescue NoMethodError => e
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
- # Load from fedora
65
- ds = datastream_for_attribute(this.dsid)
66
- if ds.kind_of?(ActiveFedora::RDFDatastream)
67
- ds.send(this.field)
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
- terminology = this.at || [this.field]
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
@@ -20,8 +20,8 @@ module ActiveFedora
20
20
  end
21
21
 
22
22
  def freeze
23
- each do |k, v|
24
- v.freeze
23
+ each_value do |datastream|
24
+ datastream.freeze
25
25
  end
26
26
  super
27
27
  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
- if args.first.is_a? String
202
- name = args.first
203
- args = args[1] || {}
204
- args[:name] = name
205
- else
206
- args = args.first
207
- end
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
- if args.first.is_a? String
240
- name = args.first
241
- args = args[1] || {}
242
- args[:name] = name
243
- else
244
- args = args.first || {}
245
- end
246
- spec = {:autocreate => args.fetch(:autocreate, false), :type => args.fetch(:type,ActiveFedora::Datastream),
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