Class: ActiveFedora::MetadataDatastream

Inherits:
NokogiriDatastream show all
Includes:
MetadataDatastreamHelper
Defined in:
lib/active_fedora/metadata_datastream.rb

Overview

A legacy class that creates and updates simple xml documents For much greater flexibility, use NokogiriDatastream instead.

Examples:

The simple, flat xml structure used by these datastreams

<fields>
  <title>Foo</title>
  <author>Bar</author>
</fields>

Instance Attribute Summary

Attributes included from MetadataDatastreamHelper

#fields, #xml_loaded

Attributes inherited from NokogiriDatastream

#internal_solr_doc

Attributes inherited from Datastream

#digital_object, #dirty, #fields, #last_modified

Class Method Summary collapse

Instance Method Summary collapse

Methods included from MetadataDatastreamHelper

#ensure_xml_loaded, included, #serialize!

Methods inherited from NokogiriDatastream

#content=, #find_by_terms, #generate_solr_symbol, #get_values_from_solr, #has_solr_name?, #is_hierarchical_term_pointer?, #ng_xml, #ng_xml=, #om_term_values, #om_update_values, #term_values, #update_values, xml_template

Methods inherited from Datastream

#add_ds_location, #add_mime_type, #create, #dirty?, #inspect, #new_object?, #profile_from_hash, #save, #serialize!, #size, #solrize_profile, #to_param, #validate_content_present

Constructor Details

#initialize(digital_object, dsid) ⇒ MetadataDatastream

Returns a new instance of MetadataDatastream.



14
15
16
17
# File 'lib/active_fedora/metadata_datastream.rb', line 14

def initialize(digital_object, dsid)
  ActiveSupport::Deprecation.warn("MetadataDatastream is deprecated and will be removed in a future release. Create a NokogiriDatastream to structure your data")
  super
end

Class Method Details

.from_xml(xml, tmpl) ⇒ Object

Populate a MetadataDatastream object based on the “datastream” node from a FOXML file

Parameters:

  • tmpl (ActiveFedora::Datastream)

    the Datastream object that you are building

  • node (Nokogiri::XML::Node)

    the “foxml:datastream” node from a FOXML file. Assumes that the content of this datastream is that of an ActiveFedora MetadataDatastream (<fields>…</fields>)



189
190
191
192
193
194
195
196
# File 'lib/active_fedora/metadata_datastream.rb', line 189

def self.from_xml(xml, tmpl) # :nodoc:
  node = Nokogiri::XML::Document.parse(xml)
  node.xpath("fields/node()").each do |f|
      tmpl.send("#{f.name}_append", f.text) unless f.class == Nokogiri::XML::Text
  end
  tmpl.send(:dirty=, false)
  tmpl
end

Instance Method Details

#field(name, tupe, opts = {}) ⇒ Object

This method generates the various accessor and mutator methods on self for the datastream metadata attributes. each field will have the 3 magic methods:

name_values=(arg) 
name_values 
name_append(arg)

Calling any of the generated methods marks self as dirty.

‘tupe’ is a datatype, currently :string, :text and :date are supported.

opts is an options hash, which will affect the generation of the xml representation of this datastream.

Currently supported modifiers: For QualifiedDublinCorDatastreams:

:element_attrs =>{:foo=>:bar} -  hash of xml element attributes
:xml_node => :nodename  - The xml node to be used to represent this object (in dcterms namespace)
:encoding=>foo, or encodings_scheme  - causes an xsi:type attribute to be set to 'foo'
:multiple=>true -  mark this field as a multivalue field (on by default)

At some point, these modifiers will be ported up to work for any ActiveFedora::MetadataDatastream.

There is quite a good example of this class in use in spec/examples/oral_history.rb

!! Careful: If you declare two fields that correspond to the same xml node without any qualifiers to differentiate them, you will end up replicating the values in the underlying datastream, resulting in mysterious dubling, quadrupling, etc. whenever you edit the field’s values.



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/active_fedora/metadata_datastream.rb', line 248

def field(name, tupe, opts={})
  #TODO add term to terminology
  @fields[name.to_s.to_sym]={:type=>tupe, :values=>[]}.merge(opts)
  #eval <<-EOS
  (class << self; self; end).class_eval do
    define_method "#{name}_values=".to_sym do |arg|
      ensure_xml_loaded  
      @fields["#{name.to_s}".to_sym][:values]=[arg].flatten
      self.dirty=true
    end
    define_method "#{name}_values".to_sym do 
      ensure_xml_loaded  
      @fields["#{name}".to_sym][:values]
    end
    define_method "#{name}_append".to_sym do |arg|
      ensure_xml_loaded  
      @fields["#{name}".to_sym][:values] << arg
      self.dirty =true
    end
  end
end

#from_solr(solr_doc) ⇒ Object

** EXPERIMENTAL **

This is utilized by ActiveFedora::Base.load_instance_from_solr to set metadata values in this object using the Solr document passed in. Any keys in the solr document that map to a metadata field key within a MetadataDatastream object are set to the corresponding value. Any others are ignored. ActiveFedora::SolrService.solr_name is used to map solr key to field key name.

Warning

Solr must be synchronized with data in Fedora.
@content is initialized to the empty document template to satisfy #ensure_xml_loaded
  (called from #update_attributes and #update_indexed_attributes)


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/active_fedora/metadata_datastream.rb', line 46

def from_solr(solr_doc)
  @content = self.to_xml
  self.xml_loaded = true
  fields.each do |field_key, field_info|
    field_symbol = ActiveFedora::SolrService.solr_name(field_key, field_info[:type])
    value = (solr_doc[field_symbol].nil? ? solr_doc[field_symbol.to_s]: solr_doc[field_symbol]) 
    unless value.nil?
      if value.is_a? Array
        update_attributes({field_key=>value})
      else
        update_indexed_attributes({field_key=>{0=>value}})
      end
    end
  end
end

#get_values(field_name, default = []) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/active_fedora/metadata_datastream.rb', line 158

def get_values(field_name, default=[])
  ensure_xml_loaded  
  field_accessor_method = "#{field_name}_values"
  if respond_to? field_accessor_method
    values = self.send(field_accessor_method)
  else
    values = []
  end
  if values.empty?
    if default.nil?
      return default
    else
      return default
    end
  else
    return values
  end
end

#set_value(field_name, values) ⇒ Object



177
178
179
180
181
182
183
184
# File 'lib/active_fedora/metadata_datastream.rb', line 177

def set_value(field_name, values)
  ensure_xml_loaded  
  field_accessor_method = "#{field_name}_values="
  if respond_to? field_accessor_method
    values = self.send(field_accessor_method, values)
    return self.send("#{field_name}_values")
  end
end

#to_solr(solr_doc = Hash.new) ⇒ Object

:nodoc:



19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/active_fedora/metadata_datastream.rb', line 19

def to_solr(solr_doc = Hash.new) # :nodoc:
  fields.each do |field_key, field_info|
    if field_info.has_key?(:values) && !field_info[:values].nil?
      field_symbol = ActiveFedora::SolrService.solr_name(field_key, field_info[:type])
      values = field_info[:values]
      values = [values] unless values.respond_to? :each
      values.each do |val|    
        ::Solrizer::Extractor.insert_solr_field_value(solr_doc, field_symbol, val )         
      end
    end
  end

  return solr_doc
end

#to_xml(xml = Nokogiri::XML::Document.parse("<fields />")) ⇒ Object

:nodoc:



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/active_fedora/metadata_datastream.rb', line 198

def to_xml(xml = Nokogiri::XML::Document.parse("<fields />")) #:nodoc:
  if xml.instance_of?(Nokogiri::XML::Builder)
    xml_insertion_point = xml.doc.root 
  elsif xml.instance_of?(Nokogiri::XML::Document) 
    xml_insertion_point = xml.root
  else
    xml_insertion_point = xml
  end
  
  builder = Nokogiri::XML::Builder.with(xml_insertion_point) do |xml|
    fields.each do |field,field_info|
      element_attrs = field_info[:element_attrs].nil? ? {} : field_info[:element_attrs]
      values = field_info[:values]
      values = [values] unless values.respond_to? :each
      values.each do |val|
        builder_arg = "xml.#{field}(val, element_attrs)"
        eval(builder_arg)
      end
    end
  end
  return builder.to_xml
end

#update_attributes(params = {}, opts = {}) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/active_fedora/metadata_datastream.rb', line 65

def update_attributes(params={},opts={})
  result = params.dup
  params.each do |k,v|
    if v == :delete || v == "" || v == nil
      v = []
    end
    if self.fields.has_key?(k.to_sym)
      result[k] = set_value(k, v)
    else
      result.delete(k)
    end
  end
  return result
end

#update_indexed_attributes(params = {}, opts = {}) ⇒ Object

An ActiveRecord-ism to udpate metadata values.

The passed in hash must look like this :

{:name=>{"0"=>"a","1"=>"b"}}

This will attempt to set the values for any field named fubar in the datastream. If there is no field by that name, it returns an empty hash and doesn’t change the object at all. If there is a field by that name, it will set the values for field of name :name having the value [a,b] and it returns a hash with the field name, value index, and the value as it was set.

An index of -1 will insert a new value. any existing value at the relevant index will be overwritten.

As in update_attributes, this overwrites all available fields by default.

Example Usage:

ds.update_attributes(:myfield=>{“0”=>“a”,“1”=>“b”,:myotherfield=>“-1”=>“c”})



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/active_fedora/metadata_datastream.rb', line 99

def update_indexed_attributes(params={}, opts={})
  ensure_xml_loaded
  
  ##FIX this bug, it should delete it from a copy of params in case passed to another datastream for update since this will modify params
  ##for subsequent calls if updating more than one datastream in a single update_indexed_attributes call
  current_params = params.clone
  
  # remove any fields from params that this datastream doesn't recognize
  current_params.delete_if do |field_name,new_values| 
    if field_name.kind_of?(Array) then field_name = field_name.first end
    !self.fields.include?(field_name.to_sym) 
  end
        
  result = current_params.dup
  current_params.each do |field_name,new_values|
    if field_name.kind_of?(Array) then field_name = field_name.first end
    
    ##FIX this bug, it should delete it from a copy of params in case passed to another datastream for update
    #if field does not exist just skip it
    next if !self.fields.include?(field_name.to_sym)
    field_accessor_method = "#{field_name}_values"
    
    if new_values.kind_of?(Hash)
        result[field_name] = new_values.dup
  
        current_values = instance_eval(field_accessor_method)
    
        # current_values = get_values(field_name) # for some reason this leaves current_values unset?
            
        new_values.delete_if do |y,z| 
          if current_values[y.to_i] and y.to_i > -1
            current_values[y.to_i]=z
            true
          else
            false
          end
        end 
    
        new_values.keys.sort { |a,b| a.to_i <=> b.to_i }.each do |y| 
          z = new_values[y]
          result[field_name].delete(y)
          current_values<<z #just append everything left
          new_array_index = current_values.length - 1
          result[field_name][new_array_index.to_s] = z
        end
        current_values.delete_if {|x| x == :delete || x == "" || x == nil}
        #set_value(field_name, current_values)
        instance_eval("#{field_accessor_method}=(current_values)") #write it back to the ds
        # result[field_name].delete("-1")
    else
      values = instance_eval("#{field_name}_values=(new_values)")
      result[field_name] = {"0"=>values}           
    end
    self.dirty = true
  end
  return result
end