cocina_display 0.7.0 → 1.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 575cbfb8b11f3cf71950bfb5234ada4a916a5e55d21116422de84361854f0713
4
- data.tar.gz: 4e06ee33d664822bdb6a3057ad8c40539f1f8363350f4648a463c7be0cb533ad
3
+ metadata.gz: 7faaded226886ab37cfbff854b64721c55964959089a1675706ae6160d4a1a5c
4
+ data.tar.gz: 111b995bfdf70805eb7a613bf5f221bacf101be1ed3f9ad6fb2407266c7a35ab
5
5
  SHA512:
6
- metadata.gz: 76ecb30d8a60bbb42bab880b0c3c5288d8daa7504cc044fb06c157e1b5e2fe21fd3e56668c3479d291f1e683eab4d719c11b2ac5d185b6e98a062e917f5ea74a
7
- data.tar.gz: ce4c61a927f9bc034aeebcd9cfc0cb56c8cd1b3c4ee13f7d8d0fcd0cc83290e8d2d1fe31ead879bdb4b1d0090ee60164158ba24d9c8eeb4a7499dfd315b9cf07
6
+ metadata.gz: 6a8586d41059d8880ac29c48f564907a51b718dc7812f2314e564032e1f19722d97620755cd53f52aa927e17e45dbfab79b56936c022255f2c22c3147e2c110c
7
+ data.tar.gz: 58ddd5de3cfe25d78904c24c1edaed72d4212eb7149b58b3b6e5c216390db75acc0c8ad81c039c9c712da99480842df2cab27c70573c848e2cb9377f0a31b909
@@ -15,6 +15,8 @@ require_relative "concerns/access"
15
15
  require_relative "concerns/subjects"
16
16
  require_relative "concerns/forms"
17
17
  require_relative "concerns/languages"
18
+ require_relative "concerns/geospatial"
19
+ require_relative "concerns/structural"
18
20
  require_relative "utils"
19
21
 
20
22
  module CocinaDisplay
@@ -28,6 +30,8 @@ module CocinaDisplay
28
30
  include CocinaDisplay::Concerns::Subjects
29
31
  include CocinaDisplay::Concerns::Forms
30
32
  include CocinaDisplay::Concerns::Languages
33
+ include CocinaDisplay::Concerns::Geospatial
34
+ include CocinaDisplay::Concerns::Structural
31
35
 
32
36
  # Fetch a public Cocina document from PURL and create a CocinaRecord.
33
37
  # @note This is intended to be used in development or testing only.
@@ -107,16 +111,27 @@ module CocinaDisplay
107
111
  content_type == "collection"
108
112
  end
109
113
 
110
- # Traverse nested FileSets and return an enumerator over their files.
111
- # Each file is a +Hash+.
112
- # @return [Enumerator] Enumerator over file hashes
113
- # @example
114
- # record.files.each do |file|
115
- # puts file["filename"] #=> "image1.jpg"
116
- # puts file["size"] #=> 123456
117
- # end
118
- def files
119
- path("$.structural.contains.*.structural.contains[*]")
114
+ # Resources related to the object.
115
+ # @return [Array<CocinaDisplay::RelatedResource>]
116
+ def related_resources
117
+ @related_resources ||= path("$.description.relatedResource[*]").map { |res| RelatedResource.new(res) }
118
+ end
119
+ end
120
+
121
+ # A resource related to the record; behaves like a CocinaRecord.
122
+ # @note Related resources have no structural metadata.
123
+ class RelatedResource < CocinaRecord
124
+ # Description of the relation to the source record.
125
+ # @return [String]
126
+ # @example "is part of"
127
+ # @see https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md#relatedresource-types
128
+ attr_reader :type
129
+
130
+ # Restructure the hash so that everything is under "description" key, since
131
+ # it's all descriptive metadata. This makes CocinaRecord methods work.
132
+ def initialize(cocina_doc)
133
+ @type = cocina_doc["type"]
134
+ @cocina_doc = {"description" => cocina_doc.except("type")}
120
135
  end
121
136
  end
122
137
  end
@@ -1,4 +1,4 @@
1
- require_relative "../contributor"
1
+ require_relative "../contributors/contributor"
2
2
 
3
3
  module CocinaDisplay
4
4
  module Concerns
@@ -62,8 +62,8 @@ module CocinaDisplay
62
62
  def contributor_names_by_role(with_date: false)
63
63
  contributors.each_with_object({}) do |contributor, hash|
64
64
  contributor.roles.each do |role|
65
- hash[role.display_str] ||= []
66
- hash[role.display_str] << contributor.display_name(with_date: with_date)
65
+ hash[role.to_s] ||= []
66
+ hash[role.to_s] << contributor.display_name(with_date: with_date)
67
67
  end
68
68
  end
69
69
  end
@@ -85,7 +85,7 @@ module CocinaDisplay
85
85
  @contributors ||= Enumerator::Chain.new(
86
86
  path("$.description.contributor.*"),
87
87
  path("$.description.event.*.contributor.*")
88
- ).map { |c| Contributor.new(c) }
88
+ ).map { |c| CocinaDisplay::Contributors::Contributor.new(c) }
89
89
  end
90
90
 
91
91
  # All contributors with a "publisher" role.
@@ -62,8 +62,8 @@ module CocinaDisplay
62
62
  # @param ignore_qualified [Boolean] Reject qualified dates (e.g. approximate)
63
63
  # @return [String, nil]
64
64
  # @example Year range
65
- # CocinaRecord.fetch('bb099mt5053').pub_year_display_str #=> "1932 - 2012"
66
- def pub_year_display_str(ignore_qualified: false)
65
+ # CocinaRecord.fetch('bb099mt5053').pub_year_str #=> "1932 - 2012"
66
+ def pub_year_str(ignore_qualified: false)
67
67
  date = pub_date(ignore_qualified: ignore_qualified)
68
68
  return unless date
69
69
 
@@ -72,18 +72,18 @@ module CocinaDisplay
72
72
 
73
73
  # String for displaying the imprint statement(s).
74
74
  # @return [String, nil]
75
- # @see CocinaDisplay::Imprint#display_str
75
+ # @see CocinaDisplay::Imprint#to_s
76
76
  # @example
77
- # CocinaRecord.fetch('bt553vr2845').imprint_display_str #=> "New York : Meridian Book, 1993, c1967"
78
- def imprint_display_str
79
- imprint_events.map(&:display_str).compact_blank.join("; ")
77
+ # CocinaRecord.fetch('bt553vr2845').imprint_str #=> "New York : Meridian Book, 1993, c1967"
78
+ def imprint_str
79
+ imprint_events.map(&:to_s).compact_blank.join("; ")
80
80
  end
81
81
 
82
82
  # List of places of publication as strings.
83
83
  # Considers locations for all publication, creation, and capture events.
84
84
  # @return [Array<String>]
85
85
  def publication_places
86
- publication_events.flat_map { |event| event.locations.map(&:display_str) }
86
+ publication_events.flat_map { |event| event.locations.map(&:to_s) }
87
87
  end
88
88
 
89
89
  # All events associated with the object.
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../subjects/subject_value"
4
+
5
+ module CocinaDisplay
6
+ module Concerns
7
+ # Methods for extracting geospatial metadata, such as coordinates.
8
+ module Geospatial
9
+ # All coordinate data as DMS format strings.
10
+ # @return [Array<String>]
11
+ # @example ["34°03′08″N 118°14′37″W"]
12
+ def coordinates
13
+ coordinate_subjects.map(&:to_s).compact.uniq
14
+ end
15
+
16
+ # All valid coordinate data formatted for indexing into a Solr RPT field.
17
+ # @note This type of field accommodates both points and bounding boxes.
18
+ # @see https://solr.apache.org/guide/solr/latest/query-guide/spatial-search.html#rpt
19
+ # @return [Array<String>]
20
+ # @example ["POINT(34.0522 -118.2437)", "POLYGON((-118.2437 34.0522, -118.2437 34.1996, -117.9522 34.1996, -117.9522 34.0522, -118.2437 34.0522))"]
21
+ def coordinates_as_wkt
22
+ coordinate_objects.map(&:as_wkt).uniq
23
+ end
24
+
25
+ # All valid coordinate data formatted for indexing into a Solr BBoxField.
26
+ # @note Points are not included since they can't be represented as a box.
27
+ # @see https://solr.apache.org/guide/solr/latest/query-guide/spatial-search.html#bboxfield
28
+ # @return [Array<String>]
29
+ # @example ["ENVELOPE(-118.2437, -117.9522, 34.1996, 34.0522)"]
30
+ def coordinates_as_envelope
31
+ coordinate_objects.map(&:as_envelope).compact.uniq
32
+ end
33
+
34
+ # All valid coordinate data formatted for indexing into a Solr LatLon field.
35
+ # @note Bounding boxes are automatically converted to their center point.
36
+ # @see https://solr.apache.org/guide/solr/latest/query-guide/spatial-search.html#indexing-points
37
+ # @return [Array<String>]
38
+ # @example ["34.0522,-118.2437"]
39
+ def coordinates_as_point
40
+ coordinate_objects.map(&:as_point).uniq
41
+ end
42
+
43
+ # Identifiers assigned by geonames.org for places related to the object.
44
+ # @return [Array<String>]
45
+ # @example ["6252001", "5368361"]
46
+ def geonames_ids
47
+ place_subject_values.map { |s| s.geonames_id }.compact.uniq
48
+ end
49
+
50
+ private
51
+
52
+ # Parsed coordinate values from the coordinate subject values.
53
+ # @return [Array<Geospatial::Coordinates>]
54
+ def coordinate_objects
55
+ coordinate_subjects.filter_map(&:coordinates)
56
+ end
57
+
58
+ # All subject values that could contain parsed coordinates.
59
+ # @return [Array<Subjects::CoordinatesSubjectValue>]
60
+ def coordinate_subjects
61
+ subject_values.filter { |s| s.is_a? CocinaDisplay::Subjects::CoordinatesSubjectValue }
62
+ end
63
+ end
64
+ end
65
+ end
@@ -13,7 +13,7 @@ module CocinaDisplay
13
13
  # Names of languages associated with the object, if recognized by Searchworks.
14
14
  # @return [Array<String>]
15
15
  def searchworks_language_names
16
- languages.filter_map { |lang| lang.display_str if lang.searchworks_language? }.compact_blank.uniq
16
+ languages.filter_map { |lang| lang.to_s if lang.searchworks_language? }.compact_blank.uniq
17
17
  end
18
18
  end
19
19
  end
@@ -0,0 +1,41 @@
1
+ require "active_support/number_helper/number_to_human_size_converter"
2
+
3
+ module CocinaDisplay
4
+ module Concerns
5
+ # Methods for inspecting structural metadata (e.g. file hierarchy)
6
+ module Structural
7
+ # Structured data for all individual files in the object.
8
+ # Traverses nested FileSet structure to return a flattened array.
9
+ # @return [Array<Hash>]
10
+ # @example
11
+ # record.files.each do |file|
12
+ # puts file["filename"] #=> "image1.jpg"
13
+ # puts file["size"] #=> 123456
14
+ # end
15
+ def files
16
+ @files ||= path("$.structural.contains.*.structural.contains.*").search
17
+ end
18
+
19
+ # All unique MIME types of files in this object.
20
+ # @return [Array<String>]
21
+ # @example ["image/jpeg", "application/pdf"]
22
+ def file_mime_types
23
+ files.pluck("hasMimeType").uniq
24
+ end
25
+
26
+ # Human-readable string representation of {total_file_size_int}.
27
+ # @return [String]
28
+ # @example "2.5 MB"
29
+ def total_file_size_str
30
+ ActiveSupport::NumberHelper.number_to_human_size(total_file_size_int)
31
+ end
32
+
33
+ # Summed size of all files in bytes.
34
+ # @return [Integer]
35
+ # @example 2621440
36
+ def total_file_size_int
37
+ files.pluck("size").sum
38
+ end
39
+ end
40
+ end
41
+ end
@@ -8,31 +8,37 @@ module CocinaDisplay
8
8
  # All unique subject values that are topics.
9
9
  # @return [Array<String>]
10
10
  def subject_topics
11
- subject_values.filter { |s| s.type == "topic" }.map(&:display_str).uniq
11
+ subject_values.filter { |s| s.type == "topic" }.map(&:to_s).uniq
12
12
  end
13
13
 
14
14
  # All unique subject values that are genres.
15
15
  # @return [Array<String>]
16
16
  def subject_genres
17
- subject_values.filter { |s| s.type == "genre" }.map(&:display_str).uniq
17
+ subject_values.filter { |s| s.type == "genre" }.map(&:to_s).uniq
18
18
  end
19
19
 
20
20
  # All unique subject values that are titles.
21
21
  # @return [Array<String>]
22
22
  def subject_titles
23
- subject_values.filter { |s| s.type == "title" }.map(&:display_str).uniq
23
+ subject_values.filter { |s| s.type == "title" }.map(&:to_s).uniq
24
24
  end
25
25
 
26
26
  # All unique subject values that are date/time info.
27
27
  # @return [Array<String>]
28
28
  def subject_temporal
29
- subject_values.filter { |s| s.type == "time" }.map(&:display_str).uniq
29
+ subject_values.filter { |s| s.type == "time" }.map(&:to_s).uniq
30
30
  end
31
31
 
32
32
  # All unique subject values that are occupations.
33
33
  # @return [Array<String>]
34
34
  def subject_occupations
35
- subject_values.filter { |s| s.type == "occupation" }.map(&:display_str).uniq
35
+ subject_values.filter { |s| s.type == "occupation" }.map(&:to_s).uniq
36
+ end
37
+
38
+ # All unique subject values that are named geographic places.
39
+ # @return [Array<String>]
40
+ def subject_places
41
+ place_subject_values.map(&:to_s).uniq
36
42
  end
37
43
 
38
44
  # All unique subject values that are names of entities.
@@ -40,15 +46,16 @@ module CocinaDisplay
40
46
  # @see CocinaDisplay::NameSubjectValue
41
47
  # @return [Array<String>]
42
48
  def subject_names
43
- subject_values.filter { |s| s.is_a? CocinaDisplay::Subjects::NameSubjectValue }.map(&:display_str).uniq
49
+ subject_values.filter { |s| s.is_a? CocinaDisplay::Subjects::NameSubjectValue }.map(&:to_s).uniq
44
50
  end
45
51
 
46
52
  # Combination of all subject values for searching.
47
53
  # @see #subject_topics_other
48
54
  # @see #subject_temporal_genre
55
+ # @see #subject_places
49
56
  # @return [Array<String>]
50
57
  def subject_all
51
- subject_topics_other + subject_temporal_genre
58
+ subject_topics_other + subject_temporal_genre + subject_places
52
59
  end
53
60
 
54
61
  # Combination of topic, occupation, name, and title subject values for searching.
@@ -77,10 +84,10 @@ module CocinaDisplay
77
84
  end
78
85
 
79
86
  # Combination of all subjects with nested values concatenated for display.
80
- # @see Subject#display_str
87
+ # @see Subject#to_s
81
88
  # @return [Array<String>]
82
89
  def subject_all_display
83
- subjects.map(&:display_str).uniq
90
+ subjects.map(&:to_s).uniq
84
91
  end
85
92
 
86
93
  private
@@ -100,6 +107,12 @@ module CocinaDisplay
100
107
  def subject_values
101
108
  @subject_values ||= subjects.flat_map(&:subject_values)
102
109
  end
110
+
111
+ # All subject values that are named places.
112
+ # @return [Array<PlaceSubjectValue>]
113
+ def place_subject_values
114
+ @place_subject_values ||= subject_values.filter { |s| s.is_a? CocinaDisplay::Subjects::PlaceSubjectValue }
115
+ end
103
116
  end
104
117
  end
105
118
  end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_support/core_ext/object/blank"
5
+ require "active_support/core_ext/array/conversions"
6
+
7
+ require_relative "../utils"
8
+ require_relative "name"
9
+ require_relative "role"
10
+
11
+ module CocinaDisplay
12
+ module Contributors
13
+ # A contributor to a work, such as an author or publisher.
14
+ class Contributor
15
+ attr_reader :cocina
16
+
17
+ # Initialize a Contributor object with Cocina structured data.
18
+ # @param cocina [Hash] The Cocina structured data for the contributor.
19
+ def initialize(cocina)
20
+ @cocina = cocina
21
+ end
22
+
23
+ # String representation of the contributor, including name and role.
24
+ # Used for debugging and logging.
25
+ # @return [String]
26
+ def to_s
27
+ Utils.compact_and_join([display_name, display_role], delimiter: ": ")
28
+ end
29
+
30
+ # Is this contributor a human?
31
+ # @return [Boolean]
32
+ def person?
33
+ cocina["type"] == "person"
34
+ end
35
+
36
+ # Is this contributor an organization?
37
+ # @return [Boolean]
38
+ def organization?
39
+ cocina["type"] == "organization"
40
+ end
41
+
42
+ # Is this contributor a conference?
43
+ # @return [Boolean]
44
+ def conference?
45
+ cocina["type"] == "conference"
46
+ end
47
+
48
+ # Is this contributor marked as primary?
49
+ # @return [Boolean]
50
+ def primary?
51
+ cocina["status"] == "primary"
52
+ end
53
+
54
+ # Does this contributor have a role that indicates they are an author?
55
+ # @return [Boolean]
56
+ def author?
57
+ roles.any?(&:author?)
58
+ end
59
+
60
+ # Does this contributor have a role that indicates they are a publisher?
61
+ # @return [Boolean]
62
+ def publisher?
63
+ roles.any?(&:publisher?)
64
+ end
65
+
66
+ # Does this contributor have a role that indicates they are a funder?
67
+ # @return [Boolean]
68
+ def funder?
69
+ roles.any?(&:funder?)
70
+ end
71
+
72
+ # Does this contributor have any roles defined?
73
+ # @return [Boolean]
74
+ def role?
75
+ roles.any?
76
+ end
77
+
78
+ # The display name for the contributor as a string.
79
+ # Uses the first name if multiple names are present.
80
+ # @param with_date [Boolean] Include life dates, if present
81
+ # @return [String]
82
+ def display_name(with_date: false)
83
+ names.map { |name| name.to_s(with_date: with_date) }.first
84
+ end
85
+
86
+ # The full forename for the contributor from the first available name.
87
+ # @see Contributor::Name::forename_str
88
+ # @return [String, nil]
89
+ def forename
90
+ names.map(&:forename_str).first.presence
91
+ end
92
+
93
+ # The full surname for the contributor from the first available name.
94
+ # @see Contributor::Name::surname_str
95
+ # @return [String, nil]
96
+ def surname
97
+ names.map(&:surname_str).first.presence
98
+ end
99
+
100
+ # A string representation of the contributor's roles, formatted for display.
101
+ # If there are multiple roles, they are joined with commas.
102
+ # @return [String]
103
+ def display_role
104
+ roles.map(&:to_s).to_sentence
105
+ end
106
+
107
+ # All names in the Cocina as Name objects.
108
+ # @return [Array<Name>]
109
+ def names
110
+ @names ||= Array(cocina["name"]).map { |name| Name.new(name) }
111
+ end
112
+
113
+ # All roles in the Cocina structured data.
114
+ # @return [Array<Hash>]
115
+ def roles
116
+ @roles ||= Array(cocina["role"]).map { |role| Role.new(role) }
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../utils"
4
+
5
+ module CocinaDisplay
6
+ module Contributors
7
+ # A name associated with a contributor.
8
+ class Name
9
+ attr_reader :cocina
10
+
11
+ # Initialize a Name object with Cocina structured data.
12
+ # @param cocina [Hash] The Cocina structured data for the name.
13
+ def initialize(cocina)
14
+ @cocina = cocina
15
+ end
16
+
17
+ # The display string for the name, optionally including life dates.
18
+ # Uses these values in order, if present:
19
+ # 1. Unstructured value
20
+ # 2. Any structured/parallel values marked as "display"
21
+ # 3. Joined structured values, optionally with life dates
22
+ # @param with_date [Boolean] Include life dates, if present
23
+ # @return [String]
24
+ # @example no dates
25
+ # name.to_s # => "King, Martin Luther, Jr."
26
+ # @example with dates
27
+ # name.to_s(with_date: true) # => "King, Martin Luther, Jr., 1929-1968"
28
+ def to_s(with_date: false)
29
+ if cocina["value"].present?
30
+ cocina["value"]
31
+ elsif display_name_str.present?
32
+ display_name_str
33
+ elsif dates_str.present? && with_date
34
+ Utils.compact_and_join([full_name_str, dates_str], delimiter: ", ")
35
+ else
36
+ full_name_str
37
+ end
38
+ end
39
+
40
+ # The full name as a string, combining all name components and terms of address.
41
+ # @return [String]
42
+ def full_name_str
43
+ Utils.compact_and_join(name_components.push(terms_of_address_str), delimiter: ", ")
44
+ end
45
+
46
+ # Flattened form of any names explicitly marked as "display name".
47
+ # @return [String]
48
+ def display_name_str
49
+ Utils.compact_and_join(Array(name_values["display"]), delimiter: ", ")
50
+ end
51
+
52
+ # List of all name components.
53
+ # If any of forename, surname, or term of address are present, those are used.
54
+ # Otherwise, fall back to any names explicitly marked as "name" or untyped.
55
+ # @return [Array<String>]
56
+ def name_components
57
+ [surname_str, forename_str].compact_blank.presence || Array(name_values["name"])
58
+ end
59
+
60
+ # Flatten all terms of address into a comma-delimited string.
61
+ # @return [String]
62
+ def terms_of_address_str
63
+ Utils.compact_and_join(Array(name_values["term of address"]), delimiter: ", ")
64
+ end
65
+
66
+ # Flatten all forename values and ordinals into a whitespace-delimited string.
67
+ # @return [String]
68
+ def forename_str
69
+ Utils.compact_and_join(Array(name_values["forename"]) + Array(name_values["ordinal"]), delimiter: " ")
70
+ end
71
+
72
+ # Flatten all surname values into a whitespace-delimited string.
73
+ # @return [String]
74
+ def surname_str
75
+ Utils.compact_and_join(Array(name_values["surname"]), delimiter: " ")
76
+ end
77
+
78
+ # Flatten all life and activity dates into a comma-delimited string.
79
+ # @return [String]
80
+ def dates_str
81
+ Utils.compact_and_join(Array(name_values["life dates"]) + Array(name_values["activity dates"]), delimiter: ", ")
82
+ end
83
+
84
+ private
85
+
86
+ # A hash mapping destructured name types to their values.
87
+ # Name values with no type are grouped under "name".
88
+ # @return [Hash<String, Array<String>>]
89
+ # @see https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md#contributor-name-part-types-for-structured-value
90
+ # @note Currently we do nothing with "alternative", "inverted full name", "pseudonym", and "transliteration" types.
91
+ def name_values
92
+ Utils.flatten_nested_values(cocina).each_with_object({}) do |node, hash|
93
+ type = node["type"] || "name"
94
+ hash[type] ||= []
95
+ hash[type] << node["value"]
96
+ end.compact_blank
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../vocabularies/marc_relator_codes"
4
+
5
+ module CocinaDisplay
6
+ module Contributors
7
+ # A role associated with a contributor.
8
+ class Role
9
+ attr_reader :cocina
10
+
11
+ # Initialize a Role object with Cocina structured data.
12
+ # @param cocina [Hash] The Cocina structured data for the role.
13
+ def initialize(cocina)
14
+ @cocina = cocina
15
+ end
16
+
17
+ # The name of the role.
18
+ # Translates the MARC relator code if no value was present.
19
+ # @return [String, nil]
20
+ def to_s
21
+ cocina["value"] || (Vocabularies::MARC_RELATOR[code] if marc_relator?)
22
+ end
23
+
24
+ # A code associated with the role, e.g. a MARC relator code.
25
+ # @return [String, nil]
26
+ def code
27
+ cocina["code"]
28
+ end
29
+
30
+ # Does this role indicate the contributor is an author?
31
+ # @return [Boolean]
32
+ def author?
33
+ to_s =~ /^(author|creator|primary investigator)/i
34
+ end
35
+
36
+ # Does this role indicate the contributor is a publisher?
37
+ # @return [Boolean]
38
+ def publisher?
39
+ to_s =~ /^publisher/i
40
+ end
41
+
42
+ # Does this role indicate the contributor is a funder?
43
+ # @return [Boolean]
44
+ def funder?
45
+ to_s =~ /^funder/i
46
+ end
47
+
48
+ private
49
+
50
+ # Does this role have a MARC relator code?
51
+ # @return [Boolean]
52
+ def marc_relator?
53
+ cocina.dig("source", "code") == "marcrelator"
54
+ end
55
+ end
56
+ end
57
+ end
@@ -1,6 +1,6 @@
1
1
  require_relative "location"
2
2
  require_relative "../dates/date"
3
- require_relative "../contributor"
3
+ require_relative "../contributors/contributor"
4
4
 
5
5
  module CocinaDisplay
6
6
  module Events
@@ -62,7 +62,7 @@ module CocinaDisplay
62
62
  # @return [Array<CocinaDisplay::Contributor>]
63
63
  def contributors
64
64
  @contributors ||= Array(cocina["contributor"]).map do |contributor|
65
- CocinaDisplay::Contributor.new(contributor)
65
+ CocinaDisplay::Contributors::Contributor.new(contributor)
66
66
  end
67
67
  end
68
68
 
@@ -16,7 +16,7 @@ module CocinaDisplay
16
16
  class Imprint < Event
17
17
  # The entire imprint statement formatted as a string for display.
18
18
  # @return [String]
19
- def display_str
19
+ def to_s
20
20
  place_pub = Utils.compact_and_join([place_str, publisher_str], delimiter: " : ")
21
21
  edition_place_pub = Utils.compact_and_join([edition_str, place_pub], delimiter: " - ")
22
22
  Utils.compact_and_join([edition_place_pub, date_str], delimiter: ", ")
@@ -93,7 +93,7 @@ module CocinaDisplay
93
93
  def locations_for_display
94
94
  unencoded_locs, encoded_locs = locations.partition { |loc| loc.unencoded_value? }
95
95
  locs_for_display = unencoded_locs.presence || encoded_locs
96
- locs_for_display.map(&:display_str).compact_blank.uniq
96
+ locs_for_display.map(&:to_s).compact_blank.uniq
97
97
  end
98
98
  end
99
99
  end
@@ -15,7 +15,7 @@ module CocinaDisplay
15
15
  # The name of the location.
16
16
  # Decodes a MARC country code if present and no value was present.
17
17
  # @return [String, nil]
18
- def display_str
18
+ def to_s
19
19
  cocina["value"] || decoded_country
20
20
  end
21
21