pennmarc 1.0.12 → 1.0.15.pre

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +7 -12
  3. data/.rubocop_todo.yml +2 -2
  4. data/lib/pennmarc/enriched.rb +93 -0
  5. data/lib/pennmarc/helpers/access.rb +2 -2
  6. data/lib/pennmarc/helpers/citation.rb +4 -4
  7. data/lib/pennmarc/helpers/classification.rb +8 -8
  8. data/lib/pennmarc/helpers/creator.rb +70 -73
  9. data/lib/pennmarc/helpers/database.rb +6 -6
  10. data/lib/pennmarc/helpers/date.rb +3 -3
  11. data/lib/pennmarc/helpers/edition.rb +4 -2
  12. data/lib/pennmarc/helpers/format.rb +15 -14
  13. data/lib/pennmarc/helpers/helper.rb +1 -1
  14. data/lib/pennmarc/helpers/identifier.rb +16 -14
  15. data/lib/pennmarc/helpers/inventory.rb +92 -0
  16. data/lib/pennmarc/helpers/inventory_entry/base.rb +23 -0
  17. data/lib/pennmarc/helpers/inventory_entry/electronic.rb +20 -0
  18. data/lib/pennmarc/helpers/inventory_entry/physical.rb +38 -0
  19. data/lib/pennmarc/helpers/language.rb +3 -2
  20. data/lib/pennmarc/helpers/location.rb +19 -14
  21. data/lib/pennmarc/helpers/note.rb +10 -8
  22. data/lib/pennmarc/helpers/production.rb +9 -9
  23. data/lib/pennmarc/helpers/relation.rb +12 -9
  24. data/lib/pennmarc/helpers/series.rb +10 -8
  25. data/lib/pennmarc/helpers/subject.rb +12 -12
  26. data/lib/pennmarc/helpers/title.rb +20 -16
  27. data/lib/pennmarc/mappings/locations.yml +4 -0
  28. data/lib/pennmarc/util.rb +19 -4
  29. data/lib/pennmarc/version.rb +1 -1
  30. data/spec/lib/pennmarc/helpers/access_spec.rb +5 -5
  31. data/spec/lib/pennmarc/helpers/classification_spec.rb +6 -6
  32. data/spec/lib/pennmarc/helpers/creator_spec.rb +41 -7
  33. data/spec/lib/pennmarc/helpers/format_spec.rb +4 -4
  34. data/spec/lib/pennmarc/helpers/inventory_spec.rb +129 -0
  35. data/spec/lib/pennmarc/helpers/location_spec.rb +40 -9
  36. data/spec/lib/pennmarc/helpers/subject_spec.rb +37 -13
  37. metadata +10 -5
  38. data/lib/pennmarc/enriched_marc.rb +0 -49
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../enriched_marc'
3
+ require_relative '../enriched'
4
4
  require_relative '../mappers'
5
5
  require_relative '../util'
6
6
 
@@ -34,11 +34,12 @@ module PennMARC
34
34
  # @param [MARC::Record] record
35
35
  # @return [Array<String>]
36
36
  def isbn_show(record)
37
- isbn_values = record.fields('020').filter_map do |field|
37
+ values = record.fields('020').filter_map do |field|
38
38
  joined_isbn = join_subfields(field, &subfield_in?(%w[a]))
39
39
  joined_isbn.presence
40
40
  end
41
- isbn_values + linked_alternate(record, '020', &subfield_in?(%w[a]))
41
+ isbn_values = values + linked_alternate(record, '020', &subfield_in?(%w[a]))
42
+ isbn_values.uniq
42
43
  end
43
44
 
44
45
  # Get ISSN values for display from the {https://www.oclc.org/bibformats/en/0xx/022.html 022 field} and related
@@ -47,11 +48,12 @@ module PennMARC
47
48
  # @param [MARC::Record] record
48
49
  # @return [Array<String>]
49
50
  def issn_show(record)
50
- issn_values = record.fields('022').filter_map do |field|
51
+ values = record.fields('022').filter_map do |field|
51
52
  joined_issn = join_subfields(field, &subfield_in?(%w[a]))
52
53
  joined_issn.presence
53
54
  end
54
- issn_values + linked_alternate(record, '022', &subfield_in?(%w[a]))
55
+ issn_values = values + linked_alternate(record, '022', &subfield_in?(%w[a]))
56
+ issn_values.uniq
55
57
  end
56
58
 
57
59
  # Get numeric OCLC ID of first {https://www.oclc.org/bibformats/en/0xx/035.html 035 field}
@@ -82,7 +84,7 @@ module PennMARC
82
84
  # @param [MARC::Record] record
83
85
  # @return [Array<String>]
84
86
  def oclc_id_search(record)
85
- record.fields('035').flat_map do |field|
87
+ record.fields('035').flat_map { |field|
86
88
  field.filter_map do |subfield|
87
89
  # skip unless subfield 'a' or 'z'
88
90
  next unless subfield.code.in?(%w[a z])
@@ -98,7 +100,7 @@ module PennMARC
98
100
 
99
101
  match[1]
100
102
  end
101
- end
103
+ }.uniq
102
104
  end
103
105
 
104
106
  # Get publisher issued identifiers from fields {https://www.oclc.org/bibformats/en/0xx/024.html 024},
@@ -109,7 +111,7 @@ module PennMARC
109
111
  # @param [MARC::Record] record
110
112
  # @return [Array<string>]
111
113
  def publisher_number_show(record)
112
- record.fields(%w[024 028 880]).filter_map do |field|
114
+ record.fields(%w[024 028 880]).filter_map { |field|
113
115
  next if field.tag == '880' && subfield_value_not_in?(field, '6', %w[024 028])
114
116
 
115
117
  # do not return doi values from 024 ǂ2
@@ -118,7 +120,7 @@ module PennMARC
118
120
  else
119
121
  join_subfields(field, &subfield_not_in?(%w[5 6])).presence
120
122
  end
121
- end
123
+ }.uniq
122
124
  end
123
125
 
124
126
  # Get publisher issued identifiers for searching of a record. Values extracted from fields
@@ -127,19 +129,19 @@ module PennMARC
127
129
  # @param [MARC::Record] record
128
130
  # @return [Array<String>]
129
131
  def publisher_number_search(record)
130
- record.fields(%w[024 028]).filter_map do |field|
132
+ record.fields(%w[024 028]).filter_map { |field|
131
133
  joined_identifiers = join_subfields(field, &subfield_in?(%w[a]))
132
134
  joined_identifiers.presence
133
- end
135
+ }.uniq
134
136
  end
135
137
 
136
138
  # Retrieve fingerprint for display from the {https://www.oclc.org/bibformats/en/0xx/026.html 026} field
137
139
  # @param [MARC::Record] record
138
140
  # @return [Array<String>]
139
141
  def fingerprint_show(record)
140
- record.fields('026').map do |field|
142
+ record.fields('026').map { |field|
141
143
  join_subfields(field, &subfield_not_in?(%w[2 5 6 8]))
142
- end
144
+ }.uniq
143
145
  end
144
146
 
145
147
  # Retrieve DOI values stored in {https://www.oclc.org/bibformats/en/0xx/024.html 024}.
@@ -147,14 +149,14 @@ module PennMARC
147
149
  # @param [MARC::Record] record
148
150
  # @return [Array<String>]
149
151
  def doi_show(record)
150
- record.fields('024').filter_map do |field|
152
+ record.fields('024').filter_map { |field|
151
153
  # skip unless indicator1 is '7'
152
154
  next unless field.indicator1.in?(%w[7])
153
155
  # skip unless ǂ2 is the string literal 'doi'
154
156
  next unless subfield_value_in?(field, '2', %w[doi])
155
157
 
156
158
  join_subfields(field, &subfield_in?(%w[a]))
157
- end
159
+ }.uniq
158
160
  end
159
161
 
160
162
  private
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'inventory_entry/electronic'
4
+ require_relative 'inventory_entry/physical'
5
+
6
+ module PennMARC
7
+ # Methods for extracting holdings information ("inventory") when available
8
+ class Inventory < Helper
9
+ PHYSICAL_INVENTORY_TAGS = [
10
+ Enriched::Pub::PHYS_INVENTORY_TAG,
11
+ Enriched::Api::PHYS_INVENTORY_TAG
12
+ ].freeze
13
+
14
+ ELECTRONIC_INVENTORY_TAGS = [
15
+ Enriched::Pub::ELEC_INVENTORY_TAG,
16
+ Enriched::Api::ELEC_INVENTORY_TAG
17
+ ].freeze
18
+
19
+ class << self
20
+ # Hash of Physical holdings information
21
+ # @param [MARC::Record] record
22
+ # @return [Array, nil]
23
+ def physical(record)
24
+ source = enrichment_source(record)
25
+ return unless source
26
+
27
+ record.fields(PHYSICAL_INVENTORY_TAGS).map do |entry|
28
+ InventoryEntry::Physical.new(entry, source).to_h
29
+ end
30
+ end
31
+
32
+ # Hash of Electronic inventory information
33
+ # @param [MARC::Record] record
34
+ # @return [Array, nil]
35
+ def electronic(record)
36
+ source = enrichment_source(record)
37
+ return unless source
38
+
39
+ record.fields(ELECTRONIC_INVENTORY_TAGS).map do |entry|
40
+ InventoryEntry::Electronic.new(entry, source).to_h
41
+ end
42
+ end
43
+
44
+ # Count of all electronic portfolios
45
+ # @param [MARC::Record] record
46
+ # @return [Integer]
47
+ def electronic_portfolio_count(record)
48
+ record.count { |field| field.tag.in? %w[AVE prt] }
49
+ end
50
+
51
+ # Count of all physical holdings
52
+ # @param [MARC::Record] record
53
+ # @return [Integer]
54
+ def physical_holding_count(record)
55
+ record.count { |field| field.tag.in? %w[AVA hld] }
56
+ end
57
+
58
+ private
59
+
60
+ # Determine the source of the MARC inventory enrichment
61
+ # @param [MARC::Record] record
62
+ # @return [Symbol, nil]
63
+ def enrichment_source(record)
64
+ if pub_enrichment_tags?(record)
65
+ :pub
66
+ elsif api_enrichment_tags?(record)
67
+ :api
68
+ end
69
+ end
70
+
71
+ # Does the record include tags from Publishing inventory enrichment?
72
+ # @todo move to Util?
73
+ # @param [MARC::Record] record
74
+ # @return [Boolean]
75
+ def pub_enrichment_tags?(record)
76
+ record.tags.intersect?(
77
+ [Enriched::Pub::PHYS_INVENTORY_TAG, Enriched::Pub::ELEC_INVENTORY_TAG, Enriched::Pub::ITEM_TAG]
78
+ )
79
+ end
80
+
81
+ # Does the record include tags from API inventory enrichment?
82
+ # @todo move to Util?
83
+ # @param [MARC::Record] record
84
+ # @return [Boolean]
85
+ def api_enrichment_tags?(record)
86
+ record.tags.intersect?(
87
+ [Enriched::Api::PHYS_INVENTORY_TAG, Enriched::Api::ELEC_INVENTORY_TAG]
88
+ )
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PennMARC
4
+ module InventoryEntry
5
+ # Base class for InventoryEntry classes, defines required interface
6
+ class Base
7
+ attr_reader :source, :field, :mapper
8
+
9
+ # @param [MARC::DataField] inventory_field
10
+ # @param [Symbol] source
11
+ def initialize(inventory_field, source)
12
+ @source = source
13
+ @field = inventory_field
14
+ @mapper = @source == :api ? Enriched::Api : Enriched::Pub
15
+ end
16
+
17
+ # @return [Hash]
18
+ def to_h
19
+ raise NotImplementedError
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module PennMARC
6
+ module InventoryEntry
7
+ # Represent a Electronic inventory entry - simple because the subfield specification is identical across
8
+ # entries returned by the API and Alma Publishing enrichment
9
+ class Electronic < Base
10
+ # @return [Hash{Symbol->Unknown}]
11
+ def to_h
12
+ { portfolio_id: field[mapper::ELEC_PORTFOLIO_ID],
13
+ url: field[mapper::ELEC_SERVICE_URL],
14
+ collection_name: field[mapper::ELEC_COLLECTION_NAME],
15
+ coverage: field[mapper::ELEC_COVERAGE_STMT],
16
+ note: field[mapper::ELEC_PUBLIC_NOTE] }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module PennMARC
6
+ module InventoryEntry
7
+ # Represent a Physical inventory entry
8
+ class Physical < Base
9
+ # Call number from inventory entry
10
+ # @return [String (frozen)]
11
+ def call_num
12
+ if source == :pub
13
+ "#{field[mapper::HOLDING_CLASSIFICATION_PART]}#{field[mapper::HOLDING_ITEM_PART]}"
14
+ elsif source == :api
15
+ field[mapper::PHYS_CALL_NUMBER]
16
+ end
17
+ end
18
+
19
+ # Priority for inventory entry
20
+ # @note we currently don't return priority in our publishing enrichment
21
+ # @return [String, nil]
22
+ def priority
23
+ return nil if source == :pub
24
+
25
+ field[mapper::PHYS_PRIORITY]
26
+ end
27
+
28
+ # @return [Hash{Symbol->Unknown}]
29
+ def to_h
30
+ { holding_id: field[mapper::PHYS_HOLDING_ID],
31
+ location_name: field[mapper::PHYS_LOCATION_NAME],
32
+ location_code: field[mapper::PHYS_LOCATION_CODE],
33
+ call_num: call_num,
34
+ priority: priority }
35
+ end
36
+ end
37
+ end
38
+ end
@@ -18,7 +18,8 @@ module PennMARC
18
18
  values = record.fields('546').map do |field|
19
19
  join_subfields field, &subfield_not_in?(%w[6 8])
20
20
  end
21
- values + linked_alternate(record, '546', &subfield_not_in?(%w[6 8]))
21
+ language_values = values + linked_alternate(record, '546', &subfield_not_in?(%w[6 8]))
22
+ language_values.uniq
22
23
  end
23
24
 
24
25
  # Get language values for searching and faceting of a record. The values are extracted from subfields
@@ -33,7 +34,7 @@ module PennMARC
33
34
  # @param [MARC::Record] record
34
35
  # @param [Hash] iso_639_2_mapping iso-639-2 spec hash for language code translation
35
36
  # @param [Hash] iso_639_3_mapping iso-639-3 spec hash for language code translation
36
- # @return [Array] array of language values
37
+ # @return [Array<String>] array of language values
37
38
  def values(record, iso_639_2_mapping: Mappers.iso_639_2_language, iso_639_3_mapping: Mappers.iso_639_3_language)
38
39
  values = record.fields('041').filter_map { |field|
39
40
  mapper = subfield_value?(field, '2', /iso639-3/) ? iso_639_3_mapping : iso_639_2_mapping
@@ -3,10 +3,12 @@
3
3
  module PennMARC
4
4
  # Methods that return Library and Location values from Alma enhanced MARC fields
5
5
  class Location < Helper
6
+ ONLINE_LIBRARY = 'Online library'
7
+
6
8
  class << self
7
9
  # Retrieves library location from enriched marc 'itm' or 'hld' fields, giving priority to the item location over
8
10
  # the holdings location. Returns item's location if available. Otherwise, returns holding's location.
9
- # {PennMARC::EnrichedMarc} maps enriched marc fields and subfields created during Alma publishing.
11
+ # {PennMARC::Enriched} maps enriched marc fields and subfields created during Alma publishing.
10
12
  # @see https://developers.exlibrisgroup.com/alma/apis/docs/bibs/R0VUIC9hbG1hd3MvdjEvYmlicy97bW1zX2lkfQ==/
11
13
  # Alma documentation for these added fields
12
14
  # @param [MARC::Record] record
@@ -19,7 +21,7 @@ module PennMARC
19
21
  # Retrieves the specific location from enriched marc 'itm' or 'hld' fields, giving priority to the item location
20
22
  # over the holdings location. Returns item library location if available. Otherwise, returns holdings library
21
23
  # location.
22
- # {PennMARC::EnrichedMarc} maps enriched marc fields and subfields created during Alma publishing.
24
+ # {PennMARC::Enriched} maps enriched marc fields and subfields created during Alma publishing.
23
25
  # @see https://developers.exlibrisgroup.com/alma/apis/docs/bibs/R0VUIC9hbG1hd3MvdjEvYmlicy97bW1zX2lkfQ==/
24
26
  # Alma documentation for these added fields
25
27
  # @param [MARC::Record] record
@@ -31,7 +33,7 @@ module PennMARC
31
33
 
32
34
  # Base method to retrieve location data from enriched marc 'itm' or 'hld' fields, giving priority to the item
33
35
  # location over the holdings location. Returns item location if available. Otherwise, returns holdings location.
34
- # {PennMARC::EnrichedMarc} maps enriched marc fields and subfields created during Alma publishing.
36
+ # {PennMARC::Enriched} maps enriched marc fields and subfields created during Alma publishing.
35
37
  # @see https://developers.exlibrisgroup.com/alma/apis/docs/bibs/R0VUIC9hbG1hd3MvdjEvYmlicy97bW1zX2lkfQ==/
36
38
  # Alma documentation for these added fields
37
39
  # @param [MARC::Record] record
@@ -61,7 +63,9 @@ module PennMARC
61
63
  location_map[subfield.value.to_sym][display_value.to_sym]
62
64
  }.flatten.compact_blank
63
65
  }.uniq
64
- locations << 'Online library' if record.fields(PennMARC::EnrichedMarc::TAG_ELECTRONIC_INVENTORY).any?
66
+ if record.tags.intersect?([Enriched::Pub::ELEC_INVENTORY_TAG, Enriched::Api::ELEC_INVENTORY_TAG])
67
+ locations << ONLINE_LIBRARY
68
+ end
65
69
  locations
66
70
  end
67
71
 
@@ -82,17 +86,18 @@ module PennMARC
82
86
  # Since item records may reflect locations more accurately, we use them if they exist;
83
87
  # if not, we use the holdings.
84
88
 
85
- tag = PennMARC::EnrichedMarc::TAG_HOLDING
86
- subfield_code = PennMARC::EnrichedMarc::SUB_HOLDING_SHELVING_LOCATION
87
-
88
89
  # if the record has an enriched item field present, use it
89
- if field_defined?(record, PennMARC::EnrichedMarc::TAG_ITEM)
90
- tag = PennMARC::EnrichedMarc::TAG_ITEM
91
- subfield_code = PennMARC::EnrichedMarc::SUB_ITEM_CURRENT_LOCATION
92
- # otherwise, use the api enriched field value
93
- elsif field_defined?(record, EnrichedMarc::AlmaApi::TAG_PHYSICAL_INVENTORY)
94
- tag = EnrichedMarc::AlmaApi::TAG_PHYSICAL_INVENTORY
95
- subfield_code = EnrichedMarc::AlmaApi::SUB_HOLDING_LOCATION_CODE
90
+ if field_defined?(record, Enriched::Pub::ITEM_TAG)
91
+ tag = Enriched::Pub::ITEM_TAG
92
+ subfield_code = Enriched::Pub::ITEM_CURRENT_LOCATION
93
+ # if the record has API inventory tags, use them
94
+ elsif field_defined?(record, Enriched::Api::PHYS_INVENTORY_TAG)
95
+ tag = Enriched::Api::PHYS_INVENTORY_TAG
96
+ subfield_code = Enriched::Api::PHYS_LOCATION_CODE
97
+ # otherwise use Pub holding tags
98
+ else
99
+ tag = Enriched::Pub::PHYS_INVENTORY_TAG
100
+ subfield_code = Enriched::Pub::PHYS_LOCATION_CODE
96
101
  end
97
102
 
98
103
  { tag: tag, subfield_code: subfield_code }
@@ -16,11 +16,11 @@ module PennMARC
16
16
  # @return [Array<String>]
17
17
  def notes_show(record)
18
18
  notes_fields = %w[500 502 504 515 518 525 533 540 550 580 586 588]
19
- record.fields(notes_fields + ['880']).filter_map do |field|
19
+ record.fields(notes_fields + ['880']).filter_map { |field|
20
20
  next if field.tag == '880' && subfield_value_not_in?(field, '6', notes_fields)
21
21
 
22
22
  join_subfields(field, &subfield_not_in?(%w[5 6 8]))
23
- end
23
+ }.uniq
24
24
  end
25
25
 
26
26
  # Retrieve local notes for display from fields {https://www.oclc.org/bibformats/en/5xx/561.html 561},
@@ -38,11 +38,12 @@ module PennMARC
38
38
 
39
39
  additional_fields = %w[562 563 585 590]
40
40
 
41
- local_notes + record.fields(additional_fields + ['880']).filter_map do |field|
41
+ notes = local_notes + record.fields(additional_fields + ['880']).filter_map do |field|
42
42
  next if field.tag == '880' && subfield_value_not_in?(field, '6', additional_fields)
43
43
 
44
44
  join_subfields(field, &subfield_not_in?(%w[5 6 8]))
45
45
  end
46
+ notes.uniq
46
47
  end
47
48
 
48
49
  # Retrieve provenance notes for display from fields {https://www.oclc.org/bibformats/en/5xx/561.html 561} and
@@ -63,7 +64,8 @@ module PennMARC
63
64
 
64
65
  join_subfields(field, &subfield_in?(%w[a]))
65
66
  end
66
- provenance_notes + prefixed_subject_and_alternate(record, 'PRO')
67
+ notes = provenance_notes + prefixed_subject_and_alternate(record, 'PRO')
68
+ notes.uniq
67
69
  end
68
70
 
69
71
  # Retrieve contents notes for display from fields {https://www.oclc.org/bibformats/en/5xx/505.html 505} and
@@ -75,16 +77,16 @@ module PennMARC
75
77
  next if field.tag == '880' && subfield_value_not_in?(field, '6', %w[505])
76
78
 
77
79
  join_subfields(field, &subfield_not_in?(%w[6 8])).split('--')
78
- }.flatten
80
+ }.flatten.uniq
79
81
  end
80
82
 
81
83
  # Retrieve access restricted notes for display from field {https://www.oclc.org/bibformats/en/5xx/506.html 506}.
82
84
  # @param [MARC::Record] record
83
85
  # @return [Array<String>]
84
86
  def access_restriction_show(record)
85
- record.fields('506').filter_map do |field|
87
+ record.fields('506').filter_map { |field|
86
88
  join_subfields(field, &subfield_not_in?(%w[5 6]))
87
- end
89
+ }.uniq
88
90
  end
89
91
 
90
92
  # Retrieve finding aid notes for display from field {https://www.oclc.org/bibformats/en/5xx/555.html 555} and its
@@ -161,7 +163,7 @@ module PennMARC
161
163
 
162
164
  sub3_and_other_subs(field, &subfield_in?(%w[a b c d e f]))
163
165
  end
164
- system_details_notes
166
+ system_details_notes.uniq
165
167
  end
166
168
 
167
169
  private
@@ -8,21 +8,21 @@ module PennMARC
8
8
  # @param [MARC::Record] record
9
9
  # @return [Array<String>]
10
10
  def show(record)
11
- get_264_or_880_fields(record, '0')
11
+ get_264_or_880_fields(record, '0').uniq
12
12
  end
13
13
 
14
14
  # Retrieve distribution values for display from {https://www.oclc.org/bibformats/en/2xx/264.html 264 field}.
15
15
  # @param [MARC::Record] record
16
16
  # @return [Array<String>]
17
17
  def distribution_show(record)
18
- get_264_or_880_fields(record, '2')
18
+ get_264_or_880_fields(record, '2').uniq
19
19
  end
20
20
 
21
21
  # Retrieve manufacture values for display from {https://www.oclc.org/bibformats/en/2xx/264.html 264 field}.
22
22
  # @param [MARC::Record] record
23
23
  # @return [Array<String>]
24
24
  def manufacture_show(record)
25
- get_264_or_880_fields(record, '3')
25
+ get_264_or_880_fields(record, '3').uniq
26
26
  end
27
27
 
28
28
  # Retrieve publication values. Return publication values from
@@ -60,7 +60,7 @@ module PennMARC
60
60
 
61
61
  values += joined264
62
62
  end
63
- values.filter_map { |value| value&.strip }
63
+ values.filter_map { |value| value&.strip }.uniq
64
64
  end
65
65
 
66
66
  # Retrieve publication values for display from fields
@@ -68,7 +68,7 @@ module PennMARC
68
68
  # {https://www.oclc.org/bibformats/en/2xx/260.html 260}-262 and their linked alternates,
69
69
  # and {https://www.oclc.org/bibformats/en/2xx/264.html 264} and its linked alternate.
70
70
  # @param [MARC::Record] record
71
- # @return [Object]
71
+ # @return [Array<String>]
72
72
  def publication_show(record)
73
73
  values = record.fields('245').first(1).flat_map { |field| subfield_values(field, 'f') }
74
74
 
@@ -86,22 +86,22 @@ module PennMARC
86
86
  end
87
87
 
88
88
  values += get_264_or_880_fields(record, '1')
89
- values.compact_blank
89
+ values.compact_blank.uniq
90
90
  end
91
91
 
92
92
  # Retrieve place of publication for display from {https://www.oclc.org/bibformats/en/7xx/752.html 752 field} and
93
93
  # its linked alternate.
94
94
  # @note legacy version returns array of hash objects including data for display link
95
95
  # @param [MARC::Record] record
96
- # @return [Object]
96
+ # @return [Array<String>]
97
97
  def place_of_publication_show(record)
98
- record.fields(%w[752 880]).filter_map do |field|
98
+ record.fields(%w[752 880]).filter_map { |field|
99
99
  next if field.tag == '880' && subfield_values(field, '6').exclude?('752')
100
100
 
101
101
  place = join_subfields(field, &subfield_not_in?(%w[6 8 e w]))
102
102
  place_extra = join_subfields(field, &subfield_in?(%w[e w]))
103
103
  "#{place} #{place_extra}"
104
- end
104
+ }.uniq
105
105
  end
106
106
 
107
107
  private
@@ -14,11 +14,11 @@ module PennMARC
14
14
  # in this field should be sufficient to locate host item record.
15
15
  #
16
16
  # @param [MARC::Record] record
17
- # @return [Array] contained in values for display
17
+ # @return [Array<String>] contained in values for display
18
18
  def contained_in_show(record)
19
- record.fields('773').map do |field|
19
+ record.fields('773').map { |field|
20
20
  join_subfields(field, &subfield_not_in?(%w[6 7 8 w]))
21
- end
21
+ }.uniq
22
22
  end
23
23
 
24
24
  # Get "chronology" information from specially-prefixed 650 (subject) fields
@@ -56,7 +56,7 @@ module PennMARC
56
56
 
57
57
  values_with_title_prefix(field, sf_exclude: %w[0 4 6 8 i], relator_map: relator_map)
58
58
  end
59
- values + record.fields('880').filter_map do |field|
59
+ relation_values = values + record.fields('880').filter_map do |field|
60
60
  next if field.indicator2.present?
61
61
 
62
62
  next unless subfield_value?(field, '6', /^(#{RELATED_WORK_FIELDS.join('|')})/)
@@ -65,6 +65,7 @@ module PennMARC
65
65
 
66
66
  values_with_title_prefix(field, sf_exclude: %w[0 4 6 8 i], relator_map: relator_map)
67
67
  end
68
+ relation_values.uniq
68
69
  end
69
70
 
70
71
  # Get "Contains" values from {CONTAINS_FIELDS} in the 7XX range. Must have indicator 2 value of 2 indicating an
@@ -72,20 +73,21 @@ module PennMARC
72
73
  # values in sf 0, 5, 6, and 8.
73
74
  # @param [MARC::Record] record
74
75
  # @param [Hash] relator_map
75
- # @return [Array]
76
+ # @return [Array<String>]
76
77
  def contains_show(record, relator_map: Mappers.relator)
77
- acc = record.fields(CONTAINS_FIELDS).filter_map do |field|
78
+ values = record.fields(CONTAINS_FIELDS).filter_map do |field|
78
79
  next unless field.indicator2 == '2'
79
80
 
80
81
  values_with_title_prefix(field, sf_exclude: %w[0 4 5 6 8 i], relator_map: relator_map)
81
82
  end
82
- acc + record.fields('880').filter_map do |field|
83
+ contains_values = values + record.fields('880').filter_map do |field|
83
84
  next unless field.indicator2 == '2'
84
85
 
85
86
  next unless subfield_value?(field, '6', /^(#{CONTAINS_FIELDS.join('|')})/)
86
87
 
87
88
  values_with_title_prefix(field, sf_include: %w[0 5 6 8 i])
88
89
  end
90
+ contains_values.uniq
89
91
  end
90
92
 
91
93
  # Get "Constituent Unit" values from {https://www.oclc.org/bibformats/en/7xx/774.html MARC 774}. Include
@@ -93,10 +95,11 @@ module PennMARC
93
95
  # @param [MARC::Record] record
94
96
  # @return [Array]
95
97
  def constituent_unit_show(record)
96
- acc = record.fields('774').filter_map do |field|
98
+ values = record.fields('774').filter_map do |field|
97
99
  join_subfields(field, &subfield_in?(%w[i a s t]))
98
100
  end
99
- acc + linked_alternate(record, '774', &subfield_in?(%w[i a s t]))
101
+ constituent_values = values + linked_alternate(record, '774', &subfield_in?(%w[i a s t]))
102
+ constituent_values.uniq
100
103
  end
101
104
 
102
105
  # Get "Has Supplement" values from {https://www.oclc.org/bibformats/en/7xx/770.html MARC 770}. Ignore
@@ -31,7 +31,8 @@ module PennMARC
31
31
  end || []
32
32
 
33
33
  values += remaining_show_entries(record, tags_present)
34
- values + series_880_fields(record)
34
+ series_values = values + series_880_fields(record)
35
+ series_values.uniq
35
36
  end
36
37
 
37
38
  # Values from series fields for display.
@@ -75,7 +76,7 @@ module PennMARC
75
76
 
76
77
  filtered_values.map { |v| v.gsub(/\(|\)/, '') }.join(' ')
77
78
  end
78
- values
79
+ values.uniq
79
80
  end
80
81
 
81
82
  # Information concerning the immediate predecessor of the target item (chronological relationship). When a note
@@ -83,9 +84,9 @@ module PennMARC
83
84
  # indicator position for display.
84
85
  # https://www.loc.gov/marc/bibliographic/bd780.html
85
86
  # @param [MARC::Record] record
86
- # @return [String] continues fields string
87
+ # @return [Array<String>] continues fields string
87
88
  def get_continues_show(record)
88
- continues(record, '780')
89
+ continues(record, '780').uniq
89
90
  end
90
91
 
91
92
  # Information concerning the immediate successor to the target item (chronological relationship). When a note is
@@ -93,9 +94,9 @@ module PennMARC
93
94
  # position for display.
94
95
  # https://www.loc.gov/marc/bibliographic/bd785.html
95
96
  # @param [MARC::Record] record
96
- # @return [String] continued by fields string
97
+ # @return [Array<String>] continued by fields string
97
98
  def get_continued_by_show(record)
98
- continues(record, '785')
99
+ continues(record, '785').uniq
99
100
  end
100
101
 
101
102
  private
@@ -127,7 +128,7 @@ module PennMARC
127
128
  # @note added 2017/04/10: filter out 0 (authority record numbers) added by Alma
128
129
  # @param [MARC::Record] record
129
130
  # @param [String] first_tag
130
- # @return [Array<Hash>] array of author show entry hashes
131
+ # @return [Array<String>] array of author show entry strings
131
132
  def title_show_entries(record, first_tag)
132
133
  record.fields(first_tag).map do |field|
133
134
  series = join_subfields(field, &subfield_not_in?(%w[0 5 6 8 c e w v n]))
@@ -156,6 +157,7 @@ module PennMARC
156
157
  # links that field to the 880 field. The data in field 880 may be in more than one script. This function exists
157
158
  # because it differs than the tradition use of linked_alternate.
158
159
  # @param [MARC::Record] record
160
+ # @return [Array<String>]
159
161
  def series_880_fields(record)
160
162
  record.fields('880').filter_map do |field|
161
163
  next unless subfield_value?(field, '6', /^(800|810|811|830|400|410|411|440|490)/)
@@ -189,7 +191,7 @@ module PennMARC
189
191
  # Get subfields from a given field (continues or continued_by).
190
192
  # @param [MARC::Record] record
191
193
  # @param [String] tag
192
- # @return [String] joined subfields
194
+ # @return [Array<String>] joined subfields
193
195
  def continues(record, tag)
194
196
  record.fields.filter_map do |field|
195
197
  next unless field.tag == tag || (field.tag == '880' && subfield_value?(field, '6', /^#{tag}/))