active-fedora 2.2.0 → 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
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