mods_display 0.1.3 → 0.1.4

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 (37) hide show
  1. data/lib/mods_display.rb +7 -1
  2. data/lib/mods_display/configuration.rb +7 -3
  3. data/lib/mods_display/configuration/access_condition.rb +17 -0
  4. data/lib/mods_display/configuration/format.rb +5 -0
  5. data/lib/mods_display/configuration/genre.rb +5 -0
  6. data/lib/mods_display/configuration/imprint.rb +8 -0
  7. data/lib/mods_display/controller_extension.rb +1 -1
  8. data/lib/mods_display/country_codes.rb +385 -0
  9. data/lib/mods_display/fields/access_condition.rb +73 -0
  10. data/lib/mods_display/fields/description.rb +1 -3
  11. data/lib/mods_display/fields/format.rb +23 -16
  12. data/lib/mods_display/fields/genre.rb +7 -1
  13. data/lib/mods_display/fields/identifier.rb +1 -0
  14. data/lib/mods_display/fields/imprint.rb +55 -6
  15. data/lib/mods_display/fields/location.rb +25 -3
  16. data/lib/mods_display/fields/name.rb +82 -13
  17. data/lib/mods_display/fields/related_item.rb +73 -14
  18. data/lib/mods_display/fields/resource_type.rb +7 -0
  19. data/lib/mods_display/html.rb +15 -9
  20. data/lib/mods_display/model_extension.rb +1 -0
  21. data/lib/mods_display/relator_codes.rb +268 -0
  22. data/lib/mods_display/version.rb +1 -1
  23. data/spec/configuration/access_condition_spec.rb +10 -0
  24. data/spec/fields/access_condition_spec.rb +91 -0
  25. data/spec/fields/description_spec.rb +9 -9
  26. data/spec/fields/format_spec.rb +10 -14
  27. data/spec/fields/genre_spec.rb +9 -1
  28. data/spec/fields/imprint_spec.rb +63 -2
  29. data/spec/fields/location_spec.rb +21 -2
  30. data/spec/fields/name_spec.rb +30 -0
  31. data/spec/fields/related_item_spec.rb +13 -0
  32. data/spec/fields/resource_type_spec.rb +6 -0
  33. data/spec/fixtures/imprint_fixtures.rb +36 -2
  34. data/spec/integration/configuration_spec.rb +14 -2
  35. metadata +15 -7
  36. data/lib/mods_display/fields/related_location.rb +0 -16
  37. data/spec/fields/related_location_spec.rb +0 -35
@@ -0,0 +1,73 @@
1
+ class ModsDisplay::AccessCondition < ModsDisplay::Field
2
+
3
+ def fields
4
+ return_fields = @values.map do |value|
5
+ ModsDisplay::Values.new(:label => displayLabel(value) || access_label(value), :values => [process_access_statement(value)])
6
+ end
7
+ collapse_fields(return_fields)
8
+ end
9
+
10
+ private
11
+
12
+ def process_access_statement(element)
13
+ case normalize_type(element)
14
+ when "copyright"
15
+ copyright_statement(element)
16
+ when "license"
17
+ license_statement(element)
18
+ else
19
+ element.text
20
+ end
21
+ end
22
+
23
+ def copyright_statement(element)
24
+ element.text.gsub(/\(c\) copyright/i, "&copy;").gsub(/\(c\)/i, "&copy;")
25
+ end
26
+
27
+ def license_statement(element)
28
+ element.text[/^(.*) (.*):(.*)$/]
29
+ output = "<div class='#{[$1,$2].join('-').downcase}'>"
30
+ if license_link($1, $2)
31
+ output << "<a href='#{license_link($1, $2)}'>#{$3.strip}</a>"
32
+ else
33
+ output << $3.strip
34
+ end
35
+ output << "</div>"
36
+ end
37
+
38
+ def license_code_urls
39
+ {"cc" => "http://creativecommons.org/licenses/",
40
+ "odc" => "http://opendatacommons.org/licenses/"}
41
+ end
42
+
43
+ def license_link(code, type)
44
+ code = code.downcase
45
+ if license_code_urls.has_key?(code)
46
+ "#{license_code_urls[code]}#{type.downcase}#{"/#{@config.cc_license_version}/" if code == 'cc'}"
47
+ end
48
+ end
49
+
50
+ def access_label(element)
51
+ type = normalize_type(element)
52
+ if access_labels.has_key?(type)
53
+ return access_labels[type]
54
+ end
55
+ "Access condition"
56
+ end
57
+
58
+ def normalize_type(element)
59
+ type = element.attributes["type"]
60
+ if type.respond_to?(:value)
61
+ return type.value.strip.gsub(/\s*/, "").downcase
62
+ end
63
+ ""
64
+ end
65
+
66
+ def access_labels
67
+ {"useandreproduction" => "Use and reproduction",
68
+ "restrictiononaccess" => "Restriction on access",
69
+ "copyright" => "Copyright",
70
+ "license" => "License"
71
+ }
72
+ end
73
+ end
@@ -20,9 +20,7 @@ class ModsDisplay::Description < ModsDisplay::Field
20
20
  end
21
21
 
22
22
  def labels
23
- {:form => "Form",
24
- :extent => "Extent",
25
- :digitalOrigin => "Digital origin",
23
+ {:digitalOrigin => "Digital origin",
26
24
  :note => "Note"
27
25
  }
28
26
  end
@@ -1,29 +1,36 @@
1
1
  class ModsDisplay::Format < ModsDisplay::Field
2
2
 
3
3
  def fields
4
- return [] if @values.text.strip.empty?
5
- return_fields = @values.map do |value|
6
- ModsDisplay::Values.new(:label => displayLabel(value) || "Format", :values => [displayForm(value) || value.text])
4
+ return_fields = []
5
+ # if @values.respond_to?(:format) and
6
+ # !@values.format.nil? and
7
+ # !@values.format.empty?
8
+ # return_fields << ModsDisplay::Values.new(:label => "Format",
9
+ # :values => [decorate_formats(@values.format).join(", ")])
10
+ # end
11
+ unless @values.physical_description.nil?
12
+ @values.physical_description.each do |description|
13
+ unless description.form.nil? or description.form.empty?
14
+ return_fields << ModsDisplay::Values.new(:label => displayLabel(description) || "Format",
15
+ :values => [description.form.map{|f| f.text.strip }.uniq.join(", ")])
16
+ end
17
+ unless description.extent.nil? or description.extent.empty?
18
+ return_fields << ModsDisplay::Values.new(:label => displayLabel(description) || "Format",
19
+ :values => [description.extent.map{|e| e.text }.join(", ")])
20
+ end
21
+ end
7
22
  end
8
23
  collapse_fields(return_fields)
9
24
  end
10
25
 
11
- def to_html
12
- return nil if @config.ignore?
13
- output = ""
14
- fields.each do |field|
15
- output << "<dt#{label_class} title='#{field.label}'>#{field.label}:</dt>"
16
- output << "<dd#{value_class}>"
17
- field.values.map do |val|
18
- output << "<span class='#{self.class.format_class(val)}'>#{val}</span>"
19
- end.join(@config.delimiter)
20
- output << "</dd>"
26
+ private
27
+
28
+ def decorate_formats(formats)
29
+ formats.map do |format|
30
+ "<span data-mods-display-format='#{self.class.format_class(format)}'>#{format}</span>"
21
31
  end
22
- output
23
32
  end
24
33
 
25
- private
26
-
27
34
  def self.format_class(format)
28
35
  return format if format.nil?
29
36
  format.strip.downcase.gsub(/\/|\\|\s+/, "_")
@@ -1,8 +1,14 @@
1
1
  class ModsDisplay::Genre < ModsDisplay::Field
2
2
 
3
+ def fields
4
+ return_fields = @values.map do |value|
5
+ ModsDisplay::Values.new(:label => displayLabel(value) || label, :values => [value.text.strip.capitalize].flatten)
6
+ end
7
+ collapse_fields(return_fields)
8
+ end
3
9
 
4
10
  private
5
-
11
+
6
12
  def displayLabel(element)
7
13
  super(element) || "Genre"
8
14
  end
@@ -27,6 +27,7 @@ class ModsDisplay::Identifier < ModsDisplay::Field
27
27
  "ismn" => "ISMN",
28
28
  "issue number" => "Issue number",
29
29
  "lccn" => "LCCN",
30
+ "oclc" => "OCLC",
30
31
  "matrix number" => "Matrix number",
31
32
  "music publisher" => "Music publisher",
32
33
  "music plate" => "Music plate",
@@ -1,5 +1,5 @@
1
1
  class ModsDisplay::Imprint < ModsDisplay::Field
2
-
2
+ include ModsDisplay::CountryCodes
3
3
  def fields
4
4
  return_fields = []
5
5
  @values.each do |value|
@@ -58,7 +58,21 @@ class ModsDisplay::Imprint < ModsDisplay::Field
58
58
  end.compact
59
59
  end
60
60
  def parse_dates(date_field)
61
- apply_date_qualifier_decoration dedup_dates join_date_ranges date_field
61
+ apply_date_qualifier_decoration dedup_dates join_date_ranges process_encoded_dates ignore_bad_dates date_field
62
+ end
63
+ def ignore_bad_dates(date_fields)
64
+ date_fields.select do |date_field|
65
+ date_field.text.strip != "9999"
66
+ end
67
+ end
68
+ def process_encoded_dates(date_fields)
69
+ date_fields.map do |date_field|
70
+ if date_is_w3cdtf?(date_field)
71
+ process_w3cdtf_date(date_field)
72
+ else
73
+ date_field
74
+ end
75
+ end
62
76
  end
63
77
  def join_date_ranges(date_fields)
64
78
  if dates_are_range?(date_fields)
@@ -135,6 +149,22 @@ class ModsDisplay::Imprint < ModsDisplay::Field
135
149
  attributes.include?("start") and
136
150
  attributes.include?("end")
137
151
  end
152
+ def date_is_w3cdtf?(date_field)
153
+ date_field.attributes["encoding"] and
154
+ date_field.attributes["encoding"].respond_to?(:value) and
155
+ date_field.attributes["encoding"].value.downcase == "w3cdtf"
156
+ end
157
+ def process_w3cdtf_date(date_field)
158
+ date_field = date_field.clone
159
+ date_field.content = if date_field.text.strip =~ /^\d{4}-\d{2}-\d{2}$/
160
+ Date.parse(date_field.text).strftime(@config.full_date_format)
161
+ elsif date_field.text.strip =~ /^\d{4}-\d{2}$/
162
+ Date.parse("#{date_field.text}-01").strftime(@config.short_date_format)
163
+ else
164
+ date_field.content
165
+ end
166
+ date_field
167
+ end
138
168
  def dedup_dates(date_fields)
139
169
  date_text = date_fields.map{|d| normalize_date(d.text) }
140
170
  if date_text != date_text.uniq
@@ -160,11 +190,30 @@ class ModsDisplay::Imprint < ModsDisplay::Field
160
190
  def place_terms(element)
161
191
  return [] unless element.respond_to?(:place) and
162
192
  element.place.respond_to?(:placeTerm)
163
- element.place.placeTerm.select do |term|
164
- !term.attributes["type"] or
193
+ if unencoded_place_terms?(element)
194
+ element.place.placeTerm.select do |term|
195
+ !term.attributes["type"].respond_to?(:value) or
196
+ term.attributes["type"].value == "text"
197
+ end.compact
198
+ else
199
+ element.place.placeTerm.map do |term|
200
+ if term.attributes["type"].respond_to?(:value) and
201
+ term.attributes["type"].value == "code" and
202
+ term.attributes["authority"].respond_to?(:value) and
203
+ term.attributes["authority"].value == "marccountry" and
204
+ country_codes.include?(term.text.strip)
205
+ term = term.clone
206
+ term.content = country_codes[term.text.strip]
207
+ term
208
+ end
209
+ end.compact
210
+ end
211
+ end
212
+ def unencoded_place_terms?(element)
213
+ element.place.placeTerm.any? do |term|
165
214
  !term.attributes["type"].respond_to?(:value) or
166
- term.attributes["type"].value != "code"
167
- end.compact
215
+ term.attributes["type"].value == "text"
216
+ end
168
217
  end
169
218
  def imprint_display_form(element)
170
219
  display_form = element.children.find do |child|
@@ -1,13 +1,35 @@
1
1
  class ModsDisplay::Location < ModsDisplay::Field
2
2
 
3
+ def fields
4
+ return_fields = []
5
+ @values.each do |location|
6
+ location.children.each do |child|
7
+ if location_field_keys.include? child.name.to_sym
8
+ if child.name.to_sym == :url
9
+ loc_label = displayLabel(location) || "Location"
10
+ value = "<a href='#{child.text}'>#{displayLabel(child) || child.text}</a>"
11
+ else
12
+ loc_label = location_label(child) || displayLabel(location) || "Location"
13
+ value = child.text
14
+ end
15
+ return_fields << ModsDisplay::Values.new(:label => loc_label || displayLabel(location) || "Location",
16
+ :values => [value])
17
+ end
18
+ end
19
+ end
20
+ collapse_fields(return_fields)
21
+ end
22
+
3
23
  private
4
24
 
5
- def displayLabel(element)
6
- super(element) || location_label(element) || "Location"
25
+ def location_field_keys
26
+ [:physicalLocation, :url, :shelfLocation, :holdingSimple, :holdingExternal]
7
27
  end
8
28
 
9
29
  def location_label(element)
10
- if element.attributes["type"].respond_to?(:value) && location_labels.has_key?(element.attributes["type"].value)
30
+ if displayLabel(element)
31
+ displayLabel(element)
32
+ elsif element.attributes["type"].respond_to?(:value) && location_labels.has_key?(element.attributes["type"].value)
11
33
  location_labels[element.attributes["type"].value]
12
34
  end
13
35
  end
@@ -1,22 +1,13 @@
1
1
  class ModsDisplay::Name < ModsDisplay::Field
2
-
2
+ include ModsDisplay::RelatorCodes
3
3
  def fields
4
4
  return_fields = @values.map do |value|
5
- role = nil
5
+ role = process_role(value)
6
6
  person = nil
7
- if value.role.length > 0 and value.role.roleTerm.length > 0
8
- role = value.role.roleTerm.find do |term|
9
- term.attributes["type"].respond_to?(:value) and
10
- term.attributes["type"].value == "text"
11
- end
12
- end
13
7
  if value.displayForm.length > 0
14
8
  person = ModsDisplay::Name::Person.new(:name => value.displayForm.text, :role => role)
15
- else
16
- name_parts = value.namePart.map do |name_part|
17
- name_part.text
18
- end.join(", ")
19
- person = ModsDisplay::Name::Person.new(:name => name_parts, :role => role) unless name_parts.empty?
9
+ elsif !name_parts(value).empty?
10
+ person = ModsDisplay::Name::Person.new(:name => name_parts(value), :role => role)
20
11
  end
21
12
  ModsDisplay::Values.new(:label => displayLabel(value) || name_label(value), :values => [person]) if person
22
13
  end.compact
@@ -64,6 +55,84 @@ class ModsDisplay::Name < ModsDisplay::Field
64
55
  end
65
56
  end
66
57
 
58
+ def name_parts(element)
59
+ output = ""
60
+ output << [unqualified_name_parts(element),
61
+ qualified_name_parts(element, "family"),
62
+ qualified_name_parts(element, "given")].flatten.compact.join(", ")
63
+ terms = qualified_name_parts(element, "termsOfAddress")
64
+ unless terms.empty?
65
+ term_delimiter = ", "
66
+ if name_part_begins_with_roman_numeral?(terms.first)
67
+ term_delimiter = " "
68
+ end
69
+ output = [output, terms.join(", ")].flatten.compact.join(term_delimiter)
70
+ end
71
+ dates = qualified_name_parts(element, "date")
72
+ unless dates.empty?
73
+ output = [output, qualified_name_parts(element, "date")].flatten.compact.join(", ")
74
+ end
75
+ output
76
+ end
77
+
78
+ def unqualified_name_parts(element)
79
+ element.namePart.map do |part|
80
+ part.text unless part.attributes["type"]
81
+ end.compact
82
+ end
83
+
84
+ def qualified_name_parts(element, type)
85
+ element.namePart.map do |part|
86
+ if part.attributes["type"].respond_to?(:value) and
87
+ part.attributes["type"].value == type
88
+ part.text
89
+ end
90
+ end.compact
91
+ end
92
+
93
+ def name_part_begins_with_roman_numeral?(part)
94
+ first_part = part.split(/\s|,/).first.strip
95
+ first_part.chars.all? do |char|
96
+ ["I", "X", "C", "L", "V"].include? char
97
+ end
98
+ end
99
+
100
+ def process_role(element)
101
+ if element.role.length > 0 and element.role.roleTerm.length > 0
102
+ if unencoded_role_term?(element)
103
+ unencoded_role_term(element)
104
+ else
105
+ element.role.roleTerm.map do |term|
106
+ if term.attributes["type"].respond_to?(:value) and
107
+ term.attributes["type"].value == "code" and
108
+ term.attributes["authority"].respond_to?(:value) and
109
+ term.attributes["authority"].value == "marcrelator" and
110
+ relator_codes.include?(term.text.strip)
111
+ term = term.clone
112
+ term.content = relator_codes[term.text.strip]
113
+ term
114
+ end
115
+ end.compact.first
116
+ end
117
+ end
118
+ end
119
+
120
+ def unencoded_role_term(element)
121
+ element.role.roleTerm.find do |term|
122
+ term.attributes["type"].respond_to?(:value) and
123
+ term.attributes["type"].value == "text"
124
+ end || element.role.roleTerm.find do |term|
125
+ !term.attributes["type"].respond_to?(:value)
126
+ end
127
+ end
128
+
129
+ def unencoded_role_term?(element)
130
+ element.role.roleTerm.any? do |term|
131
+ !term.attributes["type"].respond_to?(:value) or
132
+ term.attributes["type"].value == "text"
133
+ end
134
+ end
135
+
67
136
  def name_labels
68
137
  {"personal" => "Author/Creator",
69
138
  "corporate" => "Corporate author",
@@ -2,24 +2,83 @@ class ModsDisplay::RelatedItem < ModsDisplay::Field
2
2
 
3
3
  def fields
4
4
  return_fields = @values.map do |value|
5
- unless (value.typeOfResource.length > 0 and
6
- value.typeOfResource.attributes.length > 0 and
7
- value.typeOfResource.attributes.first.has_key?("collection") and
8
- value.typeOfResource.attributes.first["collection"].value == "yes")
9
- if value.titleInfo.length > 0
10
- title = value.titleInfo.text.strip
11
- return_text = title
12
- location = nil
13
- location = value.location.url.text if (value.location.length > 0 and
14
- value.location.url.length > 0)
15
- return_text = "<a href='#{location}'>#{title}</a>" if location and !title.empty?
16
- unless return_text.empty?
17
- ModsDisplay::Values.new(:label => displayLabel(value) || "Related item", :values => [return_text])
18
- end
5
+ unless related_item_is_a_collection?(value)
6
+ case
7
+ when related_item_is_a_location?(value)
8
+ process_location value
9
+ when related_item_is_a_reference?(value)
10
+ process_reference value
11
+ else
12
+ process_related_item(value)
19
13
  end
20
14
  end
21
15
  end.compact
22
16
  collapse_fields(return_fields)
23
17
  end
24
18
 
19
+ private
20
+
21
+ def process_location(item)
22
+ ModsDisplay::Values.new(:label => related_item_label(item), :values => [item.location.text.strip])
23
+ end
24
+
25
+ def process_reference(item)
26
+ ModsDisplay::Values.new(:label => related_item_label(item), :values => [reference_title(item)])
27
+ end
28
+
29
+ def process_related_item(item)
30
+ if item.titleInfo.length > 0
31
+ title = item.titleInfo.text.strip
32
+ return_text = title
33
+ location = nil
34
+ location = item.location.url.text if (item.location.length > 0 and
35
+ item.location.url.length > 0)
36
+ return_text = "<a href='#{location}'>#{title}</a>" if location and !title.empty?
37
+ unless return_text.empty?
38
+ ModsDisplay::Values.new(:label => related_item_label(item), :values => [return_text])
39
+ end
40
+ end
41
+ end
42
+
43
+ def reference_title(item)
44
+ [item.titleInfo,
45
+ item.originInfo.dateOther,
46
+ item.part.detail.number,
47
+ item.note].flatten.compact.map!(&:text).map!(&:strip).join(" ")
48
+ end
49
+
50
+ def related_item_is_a_collection?(item)
51
+ item.respond_to?(:titleInfo) and
52
+ item.respond_to?(:typeOfResource) and
53
+ item.typeOfResource.attributes.length > 0 and
54
+ item.typeOfResource.attributes.first.has_key?("collection") and
55
+ item.typeOfResource.attributes.first["collection"].value == "yes"
56
+ end
57
+
58
+ def related_item_is_a_location?(item)
59
+ !related_item_is_a_collection?(item) and
60
+ !related_item_is_a_reference?(item) and
61
+ item.location.length > 0 and
62
+ item.titleInfo.length < 1
63
+ end
64
+
65
+ def related_item_is_a_reference?(item)
66
+ !related_item_is_a_collection?(item) and
67
+ item.attributes["type"].respond_to?(:value) and
68
+ item.attributes["type"].value == "isReferencedBy"
69
+ end
70
+
71
+ def related_item_label(item)
72
+ if displayLabel(item)
73
+ return displayLabel(item)
74
+ else
75
+ case
76
+ when related_item_is_a_location?(item)
77
+ return "Location"
78
+ when related_item_is_a_reference?(item)
79
+ return "Referenced by"
80
+ end
81
+ "Related item"
82
+ end
83
+ end
25
84
  end