active-fedora 2.2.0 → 2.2.1

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.
Files changed (48) hide show
  1. data/.gitignore +30 -0
  2. data/.gitmodules +3 -0
  3. data/.rvmrc +33 -0
  4. data/CONSOLE_GETTING_STARTED.textile +337 -0
  5. data/Gemfile +6 -1
  6. data/Gemfile.lock +39 -23
  7. data/NOKOGIRI_DATASTREAMS.textile +107 -0
  8. data/README.textile +41 -17
  9. data/Rakefile +5 -30
  10. data/active-fedora.gemspec +34 -496
  11. data/lib/active_fedora.rb +6 -1
  12. data/lib/active_fedora/base.rb +7 -5
  13. data/lib/active_fedora/datastream.rb +9 -8
  14. data/lib/active_fedora/metadata_datastream.rb +10 -3
  15. data/lib/active_fedora/model.rb +8 -4
  16. data/lib/active_fedora/nokogiri_datastream.rb +30 -24
  17. data/lib/active_fedora/qualified_dublin_core_datastream.rb +3 -2
  18. data/lib/active_fedora/rels_ext_datastream.rb +14 -5
  19. data/lib/active_fedora/samples.rb +3 -0
  20. data/lib/active_fedora/samples/hydra-mods_article_datastream.rb +517 -0
  21. data/lib/active_fedora/samples/hydra-rights_metadata_datastream.rb +206 -0
  22. data/lib/active_fedora/samples/marpa-dc_datastream.rb +97 -0
  23. data/lib/active_fedora/samples/special_thing.rb +45 -0
  24. data/lib/active_fedora/semantic_node.rb +16 -13
  25. data/lib/active_fedora/version.rb +3 -0
  26. data/lib/fedora/base.rb +5 -5
  27. data/lib/fedora/datastream.rb +1 -1
  28. data/lib/fedora/fedora_object.rb +1 -1
  29. data/lib/fedora/repository.rb +4 -0
  30. data/lib/tasks/active_fedora.rake +126 -0
  31. data/lib/tasks/active_fedora_dev.rake +127 -0
  32. data/solr/conf/schema.xml +278 -0
  33. data/solr/conf/solrconfig.xml +840 -0
  34. data/spec/integration/full_featured_model_spec.rb +2 -2
  35. data/spec/integration/mods_article_integration_spec.rb +2 -2
  36. data/spec/integration/nokogiri_datastream_spec.rb +2 -2
  37. data/spec/rcov.opts +2 -0
  38. data/spec/samples/models/hydrangea_article.rb +12 -0
  39. data/spec/spec_helper.rb +1 -1
  40. data/spec/unit/nokogiri_datastream_spec.rb +10 -7
  41. metadata +189 -886
  42. data/NG_XML_DATASTREAM.textile +0 -25
  43. data/USING_OM_DATASTREAMS.textile +0 -60
  44. data/VERSION +0 -1
  45. data/lib/hydra.rb +0 -2
  46. data/lib/hydra/sample_mods_datastream.rb +0 -63
  47. data/tasks/hoe.rake +0 -0
  48. data/tasks/rspec.rake +0 -29
@@ -21,6 +21,7 @@ require 'active_fedora/qualified_dublin_core_datastream.rb'
21
21
  require 'active_fedora/relationship.rb'
22
22
  require 'active_fedora/rels_ext_datastream.rb'
23
23
  require 'active_fedora/semantic_node.rb'
24
+ require 'active_fedora/version.rb'
24
25
 
25
26
  SOLR_DOCUMENT_ID = ActiveFedora::SolrService.id_field unless defined?(SOLR_DOCUMENT_ID)
26
27
  ENABLE_SOLR_UPDATES = true unless defined?(ENABLE_SOLR_UPDATES)
@@ -28,7 +29,7 @@ ENABLE_SOLR_UPDATES = true unless defined?(ENABLE_SOLR_UPDATES)
28
29
  module ActiveFedora #:nodoc:
29
30
 
30
31
  class << self
31
- attr_accessor :solr_config, :fedora_config, :predicate_config_path
32
+ attr_accessor :solr_config, :fedora_config
32
33
  end
33
34
 
34
35
  # The configuration hash that gets used by RSolr.connect
@@ -104,6 +105,10 @@ module ActiveFedora #:nodoc:
104
105
  def self.predicate_config
105
106
  @predicate_config_path
106
107
  end
108
+
109
+ def self.version
110
+ ActiveFedora::VERSION
111
+ end
107
112
  end
108
113
 
109
114
 
@@ -178,7 +178,7 @@ module ActiveFedora
178
178
  # Adds datastream to the object. Saves the datastream to fedora upon adding.
179
179
  # If datastream does not have a DSID, a unique DSID is generated
180
180
  # :prefix option will set the prefix on auto-generated DSID
181
- # @returns DSID of the added datastream
181
+ # @return [String] dsid of the added datastream
182
182
  def add_datastream(datastream, opts={})
183
183
  datastream.pid = self.pid
184
184
  if datastream.dsid == nil || datastream.dsid.empty?
@@ -700,7 +700,7 @@ module ActiveFedora
700
700
  # Relationships Management
701
701
  #
702
702
 
703
- # @returns Hash of relationships, as defined by SemanticNode
703
+ # @return [Hash] relationships hash, as defined by SemanticNode
704
704
  # Rely on rels_ext datastream to track relationships array
705
705
  # Overrides accessor for relationships array used by SemanticNode.
706
706
  # If outbound_only is false, inbound relationships will be included.
@@ -790,7 +790,9 @@ module ActiveFedora
790
790
  @inner_object.label = new_label
791
791
  end
792
792
 
793
-
793
+ # Create an instance of the current Model from the given FOXML
794
+ # This method is used when you call load_instance on a Model
795
+ # @param [Nokogiri::XML::Document] doc the FOXML of the object
794
796
  def self.deserialize(doc) #:nodoc:
795
797
  if doc.instance_of?(REXML::Document)
796
798
  pid = doc.elements['/foxml:digitalObject'].attributes['PID']
@@ -864,8 +866,8 @@ module ActiveFedora
864
866
  end
865
867
 
866
868
  # Return a Hash representation of this object where keys in the hash are appropriate Solr field names.
867
- # @solr_doc (optional) Hash to insert the fields into
868
- # @opts (optional) Hash
869
+ # @param [Hash] solr_doc (optional) Hash to insert the fields into
870
+ # @param [Hash] opts (optional)
869
871
  # If opts[:model_only] == true, the base object metadata and the RELS-EXT datastream will be omitted. This is mainly to support shelver, which calls .to_solr for each model an object subscribes to.
870
872
  def to_solr(solr_doc = Hash.new, opts={})
871
873
  unless opts[:model_only]
@@ -54,12 +54,13 @@ module ActiveFedora
54
54
  dsid.gsub(/\./, '%2e')
55
55
  end
56
56
 
57
- #has this datastream been modified since it was last saved?
57
+ # Test whether this datastream been modified since it was last saved?
58
58
  def dirty?
59
59
  @dirty
60
60
  end
61
61
 
62
- #saves this datastream into fedora.
62
+ # Save the datastream into fedora.
63
+ # Also triggers {#before_save} and {#after_save} callbacks
63
64
  def save
64
65
  before_save
65
66
  result = Fedora::Repository.instance.save(self)
@@ -67,21 +68,21 @@ module ActiveFedora
67
68
  result
68
69
  end
69
70
 
70
- def before_save # :nodoc:
71
+ # Callback. Does nothing by default. Override this to insert behaviors before the save method.
72
+ def before_save
71
73
  #check_concurrency
72
74
  end
73
75
 
74
- # @tmpl ActiveFedora::Datastream
75
- # @node Nokogiri::XML::Node
76
+ # Populate a Datastream object based on the "datastream" node from a FOXML file
77
+ # @param [ActiveFedora::Datastream] tmpl the Datastream object that you are building
78
+ # @param [Nokogiri::XML::Node] node the "foxml:datastream" node from a FOXML file
76
79
  def self.from_xml(tmpl, node)
77
- node.xpath("foxml:xmlContent/fields").each do |f|
78
- # tmpl.send("#{f.name}_append", f.content)
79
- end
80
80
  tmpl.instance_variable_set(:@dirty, false)
81
81
  tmpl.control_group= node['CONTROL_GROUP']
82
82
  tmpl
83
83
  end
84
84
 
85
+ # Callback. Override this to insert behaviors after the save method. By default, sets self.dirty = false
85
86
  def after_save
86
87
  self.dirty = false
87
88
  end
@@ -1,5 +1,11 @@
1
1
  module ActiveFedora
2
- #this class represents a MetadataDatastream, a special case of ActiveFedora::Datastream
2
+ # A legacy class that creates and updates simple xml documents
3
+ # For much greater flexibility, use {ActiveFedora::NokogiriDatastream} instead.
4
+ # @example The simple, flat xml structure used by these datastreams
5
+ # <fields>
6
+ # <title>Foo</title>
7
+ # <author>Bar</author>
8
+ # </fields>
3
9
  class MetadataDatastream < Datastream
4
10
 
5
11
  include ActiveFedora::MetadataDatastreamHelper
@@ -124,8 +130,9 @@ module ActiveFedora
124
130
  end
125
131
  end
126
132
 
127
- # @tmpl ActiveFedora::MetadataDatastream
128
- # @node Nokogiri::XML::Node
133
+ # Populate a MetadataDatastream object based on the "datastream" node from a FOXML file
134
+ # @param [ActiveFedora::Datastream] tmpl the Datastream object that you are building
135
+ # @param [Nokogiri::XML::Node] node the "foxml:datastream" node from a FOXML file. Assumes that the content of this datastream is that of an ActiveFedora MetadataDatastream (<fields>...</fields>)
129
136
  def self.from_xml(tmpl, node) # :nodoc:
130
137
  node.xpath("./foxml:datastreamVersion[last()]/foxml:xmlContent/fields/node()").each do |f|
131
138
  tmpl.send("#{f.name}_append", f.text) unless f.class == Nokogiri::XML::Text
@@ -42,10 +42,14 @@ module ActiveFedora
42
42
  # *appends val to the values array.
43
43
  module ClassMethods
44
44
 
45
- # Load an instance with the following pid. Note that you can actually
46
- # pass an pid into this method, regardless of Fedora model type, and
47
- # ActiveFedora will try to parse the results into the current type
48
- # of self, which may or may not be what you want.
45
+ # Retrieve the Fedora object with the given pid and deserialize it as an instance of the current model
46
+ # Note that you can actually pass a pid into this method, regardless of Fedora model type, and
47
+ # ActiveFedora will try to parse the results into the current type of self, which may or may not be what you want.
48
+ #
49
+ # @param [String] pid of the object to load
50
+ #
51
+ # @example this will return an instance of Book, even if the object hydra:dataset1 asserts that it is a Dataset
52
+ # Book.load_instance("hydra:dataset1")
49
53
  def load_instance(pid)
50
54
  Fedora::Repository.instance.find_model(pid, self)
51
55
  end
@@ -23,8 +23,9 @@ class ActiveFedora::NokogiriDatastream < ActiveFedora::Datastream
23
23
  self.class.from_xml(blob, self)
24
24
  end
25
25
 
26
- # @xml String, File or Nokogiri::XML::Node
27
- # @tmpl ActiveFedora::MetadataDatastream
26
+ # Create an instance of this class based on xml content
27
+ # @param [String, File, Nokogiri::XML::Node] xml the xml content to build from
28
+ # @param [ActiveFedora::MetadataDatastream] tmpl the Datastream object that you are building @default a new instance of this class
28
29
  # 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!
29
30
  def self.from_xml(xml, tmpl=self.new) # :nodoc:
30
31
  if xml.nil?
@@ -264,35 +265,37 @@ class ActiveFedora::NokogiriDatastream < ActiveFedora::Datastream
264
265
  return false
265
266
  end
266
267
 
267
- # Update field values within the current datastream
268
- # @param [Hash] params The params specifying which fields to update and their new values. Format: term_pointer => {index => value, ...}
269
- # term_pointer must be a valind OM Term pointer (ie. [:name]). Strings will be ignored.
268
+ # Update field values within the current datastream using {#update_values}, which is a wrapper for {http://rdoc.info/gems/om/1.2.4/OM/XML/TermValueOperators#update_values-instance_method OM::TermValueOperators#update_values}
269
+ # Ignores any fields from params that this datastream's Terminology doesn't recognize
270
+ #
271
+ # @param [Hash] params The params specifying which fields to update and their new values. The syntax of the params Hash is the same as that expected by
272
+ # term_pointers must be a valid OM Term pointers (ie. [:name]). Strings will be ignored.
270
273
  # @param [Hash] opts This is not currently used by the datastream-level update_indexed_attributes method
271
274
  #
272
275
  # Example:
273
- # @mods_ds.update_indexed_attributes( {[{":person"=>"0"}, "role"]=>{"0"=>"role1", "1"=>"role2", "2"=>"role3"} })
274
- # => {"person_0_role"=>{"0"=>"role1", "1"=>"role2", "2"=>"role3"}}
276
+ # @mods_ds.update_indexed_attributes( {[{":person"=>"0"}, "role"]=>{"0"=>"role1", "1"=>"role2", "2"=>"role3"} })
277
+ # => {"person_0_role"=>{"0"=>"role1", "1"=>"role2", "2"=>"role3"}}
275
278
  #
276
- # @mods_ds.to_xml will return something like this:
277
- # <mods>
278
- # <mods:name type="person">
279
- # <mods:role>
280
- # <mods:roleTerm>role1</mods:roleTerm>
281
- # </mods:role>
282
- # <mods:role>
283
- # <mods:roleTerm>role2</mods:roleTerm>
284
- # </mods:role>
285
- # <mods:role>
286
- # <mods:roleTerm>role3</mods:roleTerm>
287
- # </mods:role>
288
- # </mods:name>
289
- # </mods>
279
+ # @mods_ds.to_xml # (the following is an approximation)
280
+ # <mods>
281
+ # <mods:name type="person">
282
+ # <mods:role>
283
+ # <mods:roleTerm>role1</mods:roleTerm>
284
+ # </mods:role>
285
+ # <mods:role>
286
+ # <mods:roleTerm>role2</mods:roleTerm>
287
+ # </mods:role>
288
+ # <mods:role>
289
+ # <mods:roleTerm>role3</mods:roleTerm>
290
+ # </mods:role>
291
+ # </mods:name>
292
+ # </mods>
290
293
  def update_indexed_attributes(params={}, opts={})
291
294
  if self.class.terminology.nil?
292
295
  raise "No terminology is set for this NokogiriDatastream class. Cannot perform update_indexed_attributes"
293
296
  end
294
297
  # remove any fields from params that this datastream doesn't recognize
295
- #make sure to make a copy of params so not to modify hash that might be passed to other methods
298
+ # make sure to make a copy of params so not to modify hash that might be passed to other methods
296
299
  current_params = params.clone
297
300
  current_params.delete_if do |term_pointer,new_values|
298
301
  if term_pointer.kind_of?(String)
@@ -316,9 +319,12 @@ class ActiveFedora::NokogiriDatastream < ActiveFedora::Datastream
316
319
  term_values(*field_key)
317
320
  end
318
321
 
319
- # Override the method in OM::XML::TermValueOperators so that returns an error if we have loaded from solr since it should be read-only
322
+ # Update values in the datastream's xml
323
+ # This wraps {http://rdoc.info/gems/om/1.2.4/OM/XML/TermValueOperators#update_values-instance_method OM::TermValueOperators#update_values} so that returns an error if we have loaded from solr since datastreams loaded that way should be read-only
320
324
  #
321
- # example term values hash: {[{":person"=>"0"}, "role", "text"]=>{"0"=>"role1", "1"=>"role2", "2"=>"role3"}, [{:person=>1}, :family_name]=>"Andronicus", [{"person"=>"1"},:given_name]=>["Titus"],[{:person=>1},:role,:text]=>["otherrole1","otherrole2"] }
325
+ # @example Updating multiple values with a Hash of Term pointers and values
326
+ # ds.update_values( {[{":person"=>"0"}, "role", "text"]=>{"0"=>"role1", "1"=>"role2", "2"=>"role3"}, [{:person=>1}, :family_name]=>"Andronicus", [{"person"=>"1"},:given_name]=>["Titus"],[{:person=>1},:role,:text]=>["otherrole1","otherrole2"] } )
327
+ # => {"person_0_role_text"=>{"0"=>"role1", "1"=>"role2", "2"=>"role3"}, "person_1_role_text"=>{"0"=>"otherrole1", "1"=>"otherrole2"}}
322
328
  def update_values(params={})
323
329
  if @internal_solr_doc
324
330
  raise "No update performed, this object was initialized via Solr instead of Fedora and is therefore read-only. Please utilize ActiveFedora::Base.load_instance to first load object via Fedora instead."
@@ -27,8 +27,9 @@ module ActiveFedora
27
27
  self.blob = self.to_dc_xml
28
28
  end
29
29
 
30
- # @tmpl ActiveFedora::Datastream
31
- # @node Nokogiri::XML::Node
30
+ # Populate a QualifiedDublinCoreDatastream object based on the "datastream" node from a FOXML file
31
+ # @param [ActiveFedora::Datastream] tmpl the Datastream object that you are building
32
+ # @param [Nokogiri::XML::Node] node the "foxml:datastream" node from a FOXML file. Assumes that the content of this datastream is that of an ActiveFedora QualifiedDublinCoreDatastream
32
33
  def self.from_xml(tmpl, node) # :nodoc:
33
34
  tmpl.fields.each do |z|
34
35
  fname = z.first
@@ -29,14 +29,21 @@ module ActiveFedora
29
29
  EOL
30
30
  end
31
31
 
32
- # @tmpl ActiveFedora::MetadataDatastream
33
- # @node Nokogiri::XML::Node
32
+ # Populate a RelsExtDatastream object based on the "datastream" node from a FOXML file
33
+ # Assumes that the datastream contains RDF XML from a Fedora RELS-EXT datastream
34
+ # @param [ActiveFedora::MetadataDatastream] tmpl the Datastream object that you are populating
35
+ # @param [Nokogiri::XML::Node] node the "foxml:datastream" node from a FOXML file
34
36
  def self.from_xml(tmpl, node)
35
37
  # node.xpath("./foxml:datastreamVersion[last()]/foxml:xmlContent/rdf:RDF/rdf:Description/*").each do |f|
36
38
  node.xpath("./foxml:datastreamVersion[last()]/foxml:xmlContent/rdf:RDF/rdf:Description/*", {"rdf"=>"http://www.w3.org/1999/02/22-rdf-syntax-ns#", "foxml"=>"info:fedora/fedora-system:def/foxml#"}).each do |f|
37
- ns_mapping = self.predicate_mappings[f.namespace.href]
38
- predicate = ns_mapping ? ns_mapping.invert[f.name] : nil
39
- predicate = "#{f.namespace.prefix}_#{f.name}" if predicate.nil?
39
+ if f.namespace
40
+ ns_mapping = self.predicate_mappings[f.namespace.href]
41
+ predicate = ns_mapping ? ns_mapping.invert[f.name] : nil
42
+ predicate = "#{f.namespace.prefix}_#{f.name}" if predicate.nil?
43
+ else
44
+ logger.warn "You have a predicate without a namespace #{f.name}. Verify your rels-ext is correct."
45
+ predicate = "#{f.name}"
46
+ end
40
47
  object = f["resource"] ? f["resource"] : f.inner_text
41
48
  r = ActiveFedora::Relationship.new(:subject=>:self, :predicate=>predicate, :object=>object)
42
49
  tmpl.add_relationship(r)
@@ -45,6 +52,8 @@ module ActiveFedora
45
52
  tmpl
46
53
  end
47
54
 
55
+ # Serialize the datastream's RDF relationships to solr
56
+ # @param [Hash] solr_doc @deafult an empty Hash
48
57
  def to_solr(solr_doc = Hash.new)
49
58
  self.relationships.each_pair do |subject, predicates|
50
59
  if subject == :self || subject == "info:fedora/#{self.pid}"
@@ -0,0 +1,3 @@
1
+ # require all of the files in the samples directory
2
+ module Hydra;end
3
+ Dir[File.join(File.dirname(__FILE__), "samples", "*.rb")].each {|f| require f}
@@ -0,0 +1,517 @@
1
+ require "active-fedora"
2
+ module Hydra
3
+
4
+ # This is an example of a NokogiriDatastream that defines a terminology for MODS xml
5
+ # It focuses on the aspects of MODS that deal with descriptive metadata for published articles
6
+ # The real version of this Terminology is part of the hydra-head plugin. See https://github.com/projecthydra/hydra-head/blob/master/lib/hydra/mods_article.rb
7
+ #
8
+ # Things to note about the Terminology it defines:
9
+ #
10
+ # * Uses :ref terms to repeat the structures of a mods:name with a different @type on the mods:name element
11
+ # * Defines a term lang_code that maps to "languageTerm[@type=code]"
12
+ # * Defines a variety of terms, date, last_name, first_name & terms_of_address, that all map to mods:namePart but use varying attributes to distinguish themselves
13
+ # * Uses proxy terms to define familar terms like start_page and end_page that map to non-intuitive xml structures "extent[@unit=pages]/start" and "extent[@unit=pages]/end"
14
+ # * Uses proxy terms, publication_url, peer_reviewed, and title, to allow convenient access to frequently used terms
15
+ #
16
+ # Things to note about the additional methods it defines:
17
+ #
18
+ # * Defines a series of templates, person_template, organization_template, etc. for generating a whole set of xml nodes to insert into the document (note: the new OM::TemplateRegistry provides an even better way to do this)
19
+ # * Defines a custom method, insert_contributor, that uses the Terminology to manipulate xml documents in specialized ways
20
+ # * Defines a series of relator_term Hashes that can then be used when generating views, etc. In this case, the Hashes are hard-coded into the Class. Ideally, they might be read from a configuration file or mixed into the class using a module
21
+ class ModsArticleDatastream < ActiveFedora::NokogiriDatastream
22
+
23
+ set_terminology do |t|
24
+ t.root(:path=>"mods", :xmlns=>"http://www.loc.gov/mods/v3", :schema=>"http://www.loc.gov/standards/mods/v3/mods-3-2.xsd")
25
+
26
+
27
+ t.title_info(:path=>"titleInfo") {
28
+ t.main_title(:index_as=>[:facetable],:path=>"title", :label=>"title")
29
+ t.language(:index_as=>[:facetable],:path=>{:attribute=>"lang"})
30
+ }
31
+ t.language{
32
+ t.lang_code(:index_as=>[:facetable], :path=>"languageTerm", :attributes=>{:type=>"code"})
33
+ }
34
+ t.abstract
35
+ t.subject {
36
+ t.topic(:index_as=>[:facetable])
37
+ }
38
+ t.topic_tag(:proxy=>[:subject, :topic])
39
+ # t.topic_tag(:index_as=>[:facetable],:path=>"subject", :default_content_path=>"topic")
40
+ # This is a mods:name. The underscore is purely to avoid namespace conflicts.
41
+ t.name_ {
42
+ # this is a namepart
43
+ t.namePart(:type=>:string, :label=>"generic name")
44
+ # affiliations are great
45
+ t.affiliation
46
+ t.institution(:path=>"affiliation", :index_as=>[:facetable], :label=>"organization")
47
+ t.displayForm
48
+ t.role(:ref=>[:role])
49
+ t.description(:index_as=>[:facetable])
50
+ t.date(:path=>"namePart", :attributes=>{:type=>"date"})
51
+ t.last_name(:path=>"namePart", :attributes=>{:type=>"family"})
52
+ t.first_name(:path=>"namePart", :attributes=>{:type=>"given"}, :label=>"first name")
53
+ t.terms_of_address(:path=>"namePart", :attributes=>{:type=>"termsOfAddress"})
54
+ t.computing_id
55
+ }
56
+ # lookup :person, :first_name
57
+ t.person(:ref=>:name, :attributes=>{:type=>"personal"}, :index_as=>[:facetable])
58
+ t.department(:proxy=>[:person,:description],:index_as=>[:facetable])
59
+ t.organization(:ref=>:name, :attributes=>{:type=>"corporate"}, :index_as=>[:facetable])
60
+ t.conference(:ref=>:name, :attributes=>{:type=>"conference"}, :index_as=>[:facetable])
61
+ t.role {
62
+ t.text(:path=>"roleTerm",:attributes=>{:type=>"text"})
63
+ t.code(:path=>"roleTerm",:attributes=>{:type=>"code"})
64
+ }
65
+ t.journal(:path=>'relatedItem', :attributes=>{:type=>"host"}) {
66
+ t.title_info(:index_as=>[:facetable],:ref=>[:title_info])
67
+ t.origin_info(:path=>"originInfo") {
68
+ t.publisher
69
+ t.date_issued(:path=>"dateIssued")
70
+ t.issuance(:index_as=>[:facetable])
71
+ }
72
+ t.issn(:path=>"identifier", :attributes=>{:type=>"issn"})
73
+ t.issue(:path=>"part") {
74
+ t.volume(:path=>"detail", :attributes=>{:type=>"volume"}, :default_content_path=>"number")
75
+ t.level(:path=>"detail", :attributes=>{:type=>"number"}, :default_content_path=>"number")
76
+ t.extent
77
+ t.pages(:path=>"extent", :attributes=>{:unit=>"pages"}) {
78
+ t.start
79
+ t.end
80
+ }
81
+ t.start_page(:proxy=>[:pages, :start])
82
+ t.end_page(:proxy=>[:pages, :end])
83
+ t.publication_date(:path=>"date")
84
+ }
85
+ }
86
+ t.note
87
+ t.location(:path=>"location") {
88
+ t.url(:path=>"url")
89
+ }
90
+ t.publication_url(:proxy=>[:location,:url])
91
+ t.peer_reviewed(:proxy=>[:journal,:origin_info,:issuance], :index_as=>[:facetable])
92
+ t.title(:proxy=>[:mods,:title_info, :main_title])
93
+ t.journal_title(:proxy=>[:journal, :title_info, :main_title])
94
+ end
95
+
96
+ # Generates an empty Mods Article (used when you call ModsArticle.new without passing in existing xml)
97
+ def self.xml_template
98
+ builder = Nokogiri::XML::Builder.new do |xml|
99
+ xml.mods(:version=>"3.3", "xmlns:xlink"=>"http://www.w3.org/1999/xlink",
100
+ "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
101
+ "xmlns"=>"http://www.loc.gov/mods/v3",
102
+ "xsi:schemaLocation"=>"http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-3.xsd") {
103
+ xml.titleInfo(:lang=>"") {
104
+ xml.title
105
+ }
106
+ xml.name(:type=>"personal") {
107
+ xml.namePart(:type=>"given")
108
+ xml.namePart(:type=>"family")
109
+ xml.affiliation
110
+ xml.computing_id
111
+ xml.description
112
+ xml.role {
113
+ xml.roleTerm("Author", :authority=>"marcrelator", :type=>"text")
114
+ }
115
+ }
116
+ xml.typeOfResource
117
+ xml.genre(:authority=>"marcgt")
118
+ xml.language {
119
+ xml.languageTerm(:authority=>"iso639-2b", :type=>"code")
120
+ }
121
+ xml.abstract
122
+ xml.subject {
123
+ xml.topic
124
+ }
125
+ xml.relatedItem(:type=>"host") {
126
+ xml.titleInfo {
127
+ xml.title
128
+ }
129
+ xml.identifier(:type=>"issn")
130
+ xml.originInfo {
131
+ xml.publisher
132
+ xml.dateIssued
133
+ xml.issuance
134
+ }
135
+ xml.part {
136
+ xml.detail(:type=>"volume") {
137
+ xml.number
138
+ }
139
+ xml.detail(:type=>"number") {
140
+ xml.number
141
+ }
142
+ xml.extent(:unit=>"pages") {
143
+ xml.start
144
+ xml.end
145
+ }
146
+ xml.date
147
+ }
148
+ }
149
+ xml.location {
150
+ xml.url
151
+ }
152
+ }
153
+ end
154
+ return builder.doc
155
+ end
156
+
157
+ # Generates a new Person node
158
+ def self.person_template
159
+ builder = Nokogiri::XML::Builder.new do |xml|
160
+ xml.name(:type=>"personal") {
161
+ xml.namePart(:type=>"family")
162
+ xml.namePart(:type=>"given")
163
+ xml.affiliation
164
+ xml.computing_id
165
+ xml.description
166
+ xml.role {
167
+ xml.roleTerm("Author", :type=>"text")
168
+ }
169
+ }
170
+ end
171
+ return builder.doc.root
172
+ end
173
+
174
+ def self.full_name_template
175
+ builder = Nokogiri::XML::Builder.new do |xml|
176
+ xml.full_name(:type => "personal")
177
+ end
178
+ return builder.doc.root
179
+ end
180
+
181
+ # Generates a new Organization node
182
+ # Uses mods:name[@type="corporate"]
183
+ def self.organization_template
184
+ builder = Nokogiri::XML::Builder.new do |xml|
185
+ xml.name(:type=>"corporate") {
186
+ xml.namePart
187
+ xml.role {
188
+ xml.roleTerm(:authority=>"marcrelator", :type=>"text")
189
+ }
190
+ }
191
+ end
192
+ return builder.doc.root
193
+ end
194
+
195
+ # Generates a new Conference node
196
+ def self.conference_template
197
+ builder = Nokogiri::XML::Builder.new do |xml|
198
+ xml.name(:type=>"conference") {
199
+ xml.namePart
200
+ xml.role {
201
+ xml.roleTerm(:authority=>"marcrelator", :type=>"text")
202
+ }
203
+ }
204
+ end
205
+ return builder.doc.root
206
+ end
207
+
208
+ # Inserts a new contributor (mods:name) into the mods document
209
+ # creates contributors of type :person, :organization, or :conference
210
+ def insert_contributor(type, opts={})
211
+ case type.to_sym
212
+ when :person
213
+ node = Hydra::ModsArticleDatastream.person_template
214
+ nodeset = self.find_by_terms(:person)
215
+ when :organization
216
+ node = Hydra::ModsArticleDatastream.organization_template
217
+ nodeset = self.find_by_terms(:organization)
218
+ when :conference
219
+ node = Hydra::ModsArticleDatastream.conference_template
220
+ nodeset = self.find_by_terms(:conference)
221
+ else
222
+ ActiveFedora.logger.warn("#{type} is not a valid argument for Hydra::ModsArticleDatastream.insert_contributor")
223
+ node = nil
224
+ index = nil
225
+ end
226
+
227
+ unless nodeset.nil?
228
+ if nodeset.empty?
229
+ self.ng_xml.root.add_child(node)
230
+ index = 0
231
+ else
232
+ nodeset.after(node)
233
+ index = nodeset.length
234
+ end
235
+ self.dirty = true
236
+ end
237
+
238
+ return node, index
239
+ end
240
+
241
+ # Remove the contributor entry identified by @contributor_type and @index
242
+ def remove_contributor(contributor_type, index)
243
+ self.find_by_terms( {contributor_type.to_sym => index.to_i} ).first.remove
244
+ self.dirty = true
245
+ end
246
+
247
+ def self.common_relator_terms
248
+ {"aut" => "Author",
249
+ "clb" => "Collaborator",
250
+ "com" => "Compiler",
251
+ "ctb" => "Contributor",
252
+ "cre" => "Creator",
253
+ "edt" => "Editor",
254
+ "ill" => "Illustrator",
255
+ "oth" => "Other",
256
+ "trl" => "Translator",
257
+ }
258
+ end
259
+
260
+ def self.person_relator_terms
261
+ {"aut" => "Author",
262
+ "clb" => "Collaborator",
263
+ "com" => "Compiler",
264
+ "cre" => "Creator",
265
+ "ctb" => "Contributor",
266
+ "edt" => "Editor",
267
+ "ill" => "Illustrator",
268
+ "res" => "Researcher",
269
+ "rth" => "Research team head",
270
+ "rtm" => "Research team member",
271
+ "trl" => "Translator"
272
+ }
273
+ end
274
+
275
+ def self.conference_relator_terms
276
+ {
277
+ "hst" => "Host"
278
+ }
279
+ end
280
+
281
+ def self.organization_relator_terms
282
+ {
283
+ "fnd" => "Funder",
284
+ "hst" => "Host"
285
+ }
286
+ end
287
+
288
+ def self.dc_relator_terms
289
+ {"acp" => "Art copyist",
290
+ "act" => "Actor",
291
+ "adp" => "Adapter",
292
+ "aft" => "Author of afterword, colophon, etc.",
293
+ "anl" => "Analyst",
294
+ "anm" => "Animator",
295
+ "ann" => "Annotator",
296
+ "ant" => "Bibliographic antecedent",
297
+ "app" => "Applicant",
298
+ "aqt" => "Author in quotations or text abstracts",
299
+ "arc" => "Architect",
300
+ "ard" => "Artistic director ",
301
+ "arr" => "Arranger",
302
+ "art" => "Artist",
303
+ "asg" => "Assignee",
304
+ "asn" => "Associated name",
305
+ "att" => "Attributed name",
306
+ "auc" => "Auctioneer",
307
+ "aud" => "Author of dialog",
308
+ "aui" => "Author of introduction",
309
+ "aus" => "Author of screenplay",
310
+ "aut" => "Author",
311
+ "bdd" => "Binding designer",
312
+ "bjd" => "Bookjacket designer",
313
+ "bkd" => "Book designer",
314
+ "bkp" => "Book producer",
315
+ "bnd" => "Binder",
316
+ "bpd" => "Bookplate designer",
317
+ "bsl" => "Bookseller",
318
+ "ccp" => "Conceptor",
319
+ "chr" => "Choreographer",
320
+ "clb" => "Collaborator",
321
+ "cli" => "Client",
322
+ "cll" => "Calligrapher",
323
+ "clt" => "Collotyper",
324
+ "cmm" => "Commentator",
325
+ "cmp" => "Composer",
326
+ "cmt" => "Compositor",
327
+ "cng" => "Cinematographer",
328
+ "cnd" => "Conductor",
329
+ "cns" => "Censor",
330
+ "coe" => "Contestant -appellee",
331
+ "col" => "Collector",
332
+ "com" => "Compiler",
333
+ "cos" => "Contestant",
334
+ "cot" => "Contestant -appellant",
335
+ "cov" => "Cover designer",
336
+ "cpc" => "Copyright claimant",
337
+ "cpe" => "Complainant-appellee",
338
+ "cph" => "Copyright holder",
339
+ "cpl" => "Complainant",
340
+ "cpt" => "Complainant-appellant",
341
+ "cre" => "Creator",
342
+ "crp" => "Correspondent",
343
+ "crr" => "Corrector",
344
+ "csl" => "Consultant",
345
+ "csp" => "Consultant to a project",
346
+ "cst" => "Costume designer",
347
+ "ctb" => "Contributor",
348
+ "cte" => "Contestee-appellee",
349
+ "ctg" => "Cartographer",
350
+ "ctr" => "Contractor",
351
+ "cts" => "Contestee",
352
+ "ctt" => "Contestee-appellant",
353
+ "cur" => "Curator",
354
+ "cwt" => "Commentator for written text",
355
+ "dfd" => "Defendant",
356
+ "dfe" => "Defendant-appellee",
357
+ "dft" => "Defendant-appellant",
358
+ "dgg" => "Degree grantor",
359
+ "dis" => "Dissertant",
360
+ "dln" => "Delineator",
361
+ "dnc" => "Dancer",
362
+ "dnr" => "Donor",
363
+ "dpc" => "Depicted",
364
+ "dpt" => "Depositor",
365
+ "drm" => "Draftsman",
366
+ "drt" => "Director",
367
+ "dsr" => "Designer",
368
+ "dst" => "Distributor",
369
+ "dtc" => "Data contributor ",
370
+ "dte" => "Dedicatee",
371
+ "dtm" => "Data manager ",
372
+ "dto" => "Dedicator",
373
+ "dub" => "Dubious author",
374
+ "edt" => "Editor",
375
+ "egr" => "Engraver",
376
+ "elg" => "Electrician ",
377
+ "elt" => "Electrotyper",
378
+ "eng" => "Engineer",
379
+ "etr" => "Etcher",
380
+ "exp" => "Expert",
381
+ "fac" => "Facsimilist",
382
+ "fld" => "Field director ",
383
+ "flm" => "Film editor",
384
+ "fmo" => "Former owner",
385
+ "fpy" => "First party",
386
+ "fnd" => "Funder",
387
+ "frg" => "Forger",
388
+ "gis" => "Geographic information specialist ",
389
+ "grt" => "Graphic technician",
390
+ "hnr" => "Honoree",
391
+ "hst" => "Host",
392
+ "ill" => "Illustrator",
393
+ "ilu" => "Illuminator",
394
+ "ins" => "Inscriber",
395
+ "inv" => "Inventor",
396
+ "itr" => "Instrumentalist",
397
+ "ive" => "Interviewee",
398
+ "ivr" => "Interviewer",
399
+ "lbr" => "Laboratory ",
400
+ "lbt" => "Librettist",
401
+ "ldr" => "Laboratory director ",
402
+ "led" => "Lead",
403
+ "lee" => "Libelee-appellee",
404
+ "lel" => "Libelee",
405
+ "len" => "Lender",
406
+ "let" => "Libelee-appellant",
407
+ "lgd" => "Lighting designer",
408
+ "lie" => "Libelant-appellee",
409
+ "lil" => "Libelant",
410
+ "lit" => "Libelant-appellant",
411
+ "lsa" => "Landscape architect",
412
+ "lse" => "Licensee",
413
+ "lso" => "Licensor",
414
+ "ltg" => "Lithographer",
415
+ "lyr" => "Lyricist",
416
+ "mcp" => "Music copyist",
417
+ "mfr" => "Manufacturer",
418
+ "mdc" => "Metadata contact",
419
+ "mod" => "Moderator",
420
+ "mon" => "Monitor",
421
+ "mrk" => "Markup editor",
422
+ "msd" => "Musical director",
423
+ "mte" => "Metal-engraver",
424
+ "mus" => "Musician",
425
+ "nrt" => "Narrator",
426
+ "opn" => "Opponent",
427
+ "org" => "Originator",
428
+ "orm" => "Organizer of meeting",
429
+ "oth" => "Other",
430
+ "own" => "Owner",
431
+ "pat" => "Patron",
432
+ "pbd" => "Publishing director",
433
+ "pbl" => "Publisher",
434
+ "pdr" => "Project director",
435
+ "pfr" => "Proofreader",
436
+ "pht" => "Photographer",
437
+ "plt" => "Platemaker",
438
+ "pma" => "Permitting agency",
439
+ "pmn" => "Production manager",
440
+ "pop" => "Printer of plates",
441
+ "ppm" => "Papermaker",
442
+ "ppt" => "Puppeteer",
443
+ "prc" => "Process contact",
444
+ "prd" => "Production personnel",
445
+ "prf" => "Performer",
446
+ "prg" => "Programmer",
447
+ "prm" => "Printmaker",
448
+ "pro" => "Producer",
449
+ "prt" => "Printer",
450
+ "pta" => "Patent applicant",
451
+ "pte" => "Plaintiff -appellee",
452
+ "ptf" => "Plaintiff",
453
+ "pth" => "Patent holder",
454
+ "ptt" => "Plaintiff-appellant",
455
+ "rbr" => "Rubricator",
456
+ "rce" => "Recording engineer",
457
+ "rcp" => "Recipient",
458
+ "red" => "Redactor",
459
+ "ren" => "Renderer",
460
+ "res" => "Researcher",
461
+ "rev" => "Reviewer",
462
+ "rps" => "Repository",
463
+ "rpt" => "Reporter",
464
+ "rpy" => "Responsible party",
465
+ "rse" => "Respondent-appellee",
466
+ "rsg" => "Restager",
467
+ "rsp" => "Respondent",
468
+ "rst" => "Respondent-appellant",
469
+ "rth" => "Research team head",
470
+ "rtm" => "Research team member",
471
+ "sad" => "Scientific advisor",
472
+ "sce" => "Scenarist",
473
+ "scl" => "Sculptor",
474
+ "scr" => "Scribe",
475
+ "sds" => "Sound designer",
476
+ "sec" => "Secretary",
477
+ "sgn" => "Signer",
478
+ "sht" => "Supporting host",
479
+ "sng" => "Singer",
480
+ "spk" => "Speaker",
481
+ "spn" => "Sponsor",
482
+ "spy" => "Second party",
483
+ "srv" => "Surveyor",
484
+ "std" => "Set designer",
485
+ "stl" => "Storyteller",
486
+ "stm" => "Stage manager",
487
+ "stn" => "Standards body",
488
+ "str" => "Stereotyper",
489
+ "tcd" => "Technical director",
490
+ "tch" => "Teacher",
491
+ "ths" => "Thesis advisor",
492
+ "trc" => "Transcriber",
493
+ "trl" => "Translator",
494
+ "tyd" => "Type designer",
495
+ "tyg" => "Typographer",
496
+ "vdg" => "Videographer",
497
+ "voc" => "Vocalist",
498
+ "wam" => "Writer of accompanying material",
499
+ "wdc" => "Woodcutter",
500
+ "wde" => "Wood -engraver",
501
+ "wit" => "Witness"}
502
+ end
503
+
504
+ def self.valid_child_types
505
+ ["data", "supporting file", "profile", "lorem ipsum", "dolor"]
506
+ end
507
+
508
+ def to_solr(solr_doc=Hash.new)
509
+ super(solr_doc)
510
+
511
+ ::Solrizer::Extractor.insert_solr_field_value(solr_doc, "object_type_facet", "Article")
512
+ ::Solrizer::Extractor.insert_solr_field_value(solr_doc, "mods_journal_title_info_facet", "Unknown") if solr_doc["mods_journal_title_info_facet"].nil?
513
+
514
+ solr_doc
515
+ end
516
+ end
517
+ end