theoj 1.11.0 → 1.12.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: 79fae8106ae731512a668acc07f1b4e1c91a2669d0115d01d0ce3fb9ac352fe4
4
- data.tar.gz: d9fa5696c4057c07d25af72a140c426bd204b8e0ae1bcf2b9c3ef1411616b00a
3
+ metadata.gz: 6862eb2fc9d140c509d46202caf2bbd5edd8aa8273ab5a97ed9cfabaa1bdef04
4
+ data.tar.gz: 674e699e2e66dbb855e2423a1b04f0ccb7efd10e8455c95caa01504186c1f324
5
5
  SHA512:
6
- metadata.gz: 264f8d8407ea599bc876112b8126e3715981b199c6ea1d85ac23e4c8bf0962e5fef91645ebd5a9c602bf6ebbb4f30907a66f2fa6617b8e1c5d3dab20d78beb2e
7
- data.tar.gz: 1d54e621585399d69363d5986e297f816b7fb5a878b32a88551fe49d0c6c6c5a5b1c79342508a9a74e65af085be05cf3c190a8fbd2cff0118ba5a945d15554c0
6
+ metadata.gz: 9ae854285d72cb34bedc7bacb4bf6f04c966d13c1cd3d133c73ffaa786fef0c7fdfe9c22924d988fe625f79da653748a53ad7026804ffff5542550cdc17edbc3
7
+ data.tar.gz: cf43d49f026b8e9498c26287c8a8afc1bfd8ae29dabfe58d5652b5783e0c9a4e6b12f8389e2c327ba09d92a782e521f7178441185842430ab93c1ba5d6d70adf
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.12.0 (2026-01-20)
4
+
5
+ - Update name parsing to match JOSS' Pandoc pipeline
6
+
7
+ ## 1.11.1 (2025-07-16)
8
+
9
+ - Remove code tags & html comments from title in submission metadata
10
+
3
11
  ## 1.11.0 (2025-06-21)
4
12
 
5
13
  - Remove support for Ruby 3.1
data/lib/theoj/author.rb CHANGED
@@ -17,19 +17,51 @@ module Theoj
17
17
  end
18
18
 
19
19
  def given_name
20
- @parsed_name.first
20
+ return nil if @given_names.to_s.strip.empty?
21
+
22
+ @given_names.to_s.split(/\s+/).first
21
23
  end
22
24
 
23
25
  def middle_name
24
- @parsed_name.middle
26
+ return nil if @middle_names.to_s.strip.empty?
27
+
28
+ @middle_names
25
29
  end
26
30
 
27
31
  def last_name
28
- @parsed_name.last
32
+ particle = @non_dropping_particle.to_s.strip.empty? ? @dropping_particle : @non_dropping_particle
33
+ name_parts = [particle, @surname, @suffix].map do |part|
34
+ part.to_s.strip
35
+ end.reject(&:empty?)
36
+
37
+ fallback = @surname || @literal_name
38
+
39
+ name_parts.empty? ? fallback : name_parts.join(" ")
40
+ end
41
+
42
+ def citation_last_name
43
+ return @literal_name unless @literal_name.to_s.strip.empty?
44
+
45
+ name_parts = [@non_dropping_particle, @surname, @suffix].map do |part|
46
+ part.to_s.strip
47
+ end.reject(&:empty?)
48
+
49
+ return @surname if name_parts.empty?
50
+
51
+ name_parts.join(" ")
29
52
  end
30
53
 
31
54
  def initials
32
- [@parsed_name.first, @parsed_name.middle].compact.map {|v| v[0] + "."} * ' '
55
+ initials_parts = []
56
+ first_name = given_name.to_s
57
+ initials_parts << "#{first_name[0]}." unless first_name.empty?
58
+
59
+ middle_for_initials = @middle_names.to_s.strip.empty? ? @dropping_particle : @middle_names
60
+ unless middle_for_initials.to_s.strip.empty?
61
+ initials_parts.concat(middle_for_initials.split(/\s+/).map { |v| "#{v[0]}." })
62
+ end
63
+
64
+ initials_parts.compact.join(" ")
33
65
  end
34
66
 
35
67
  def to_h
@@ -46,24 +78,71 @@ module Theoj
46
78
 
47
79
  def parse_name(author_name)
48
80
  if author_name.is_a? Hash
49
- g = author_name["given-names"] || author_name["given"] || author_name["first"] || author_name["firstname"]
50
- m = author_name["dropping-particle"]
51
- s = author_name["surname"] || author_name["family"]
52
-
53
- g = strip_footnotes(g) unless g.nil?
54
- s = strip_footnotes(s) unless s.nil?
55
-
56
- @parsed_name = m.nil? ? Nameable::Latin.new(g, s) : Nameable::Latin.new(g, m, s)
81
+ name_parts = normalize_name_parts(author_name)
82
+ given_names = strip_footnotes(name_parts[:given_names])
83
+ dropping_particle = strip_footnotes(name_parts[:dropping_particle])
84
+ @dropping_particle = dropping_particle
85
+ @non_dropping_particle = strip_footnotes(name_parts[:non_dropping_particle])
86
+ display_particle = @non_dropping_particle || dropping_particle
87
+ @surname = strip_footnotes(name_parts[:surname])
88
+ @suffix = strip_footnotes(name_parts[:suffix])
89
+ literal_name = strip_footnotes(name_parts[:literal])
90
+ @literal_name = literal_name unless literal_name.to_s.strip.empty?
91
+ @middle_names = nil
92
+ @given_names = given_names
93
+
94
+ surname_with_particle = [@non_dropping_particle || @dropping_particle, @surname].compact.reject(&:empty?).join(" ")
95
+ name_hash = {
96
+ first: given_names,
97
+ middle: dropping_particle,
98
+ last: surname_with_particle,
99
+ suffix: @suffix
100
+ }
101
+
102
+ @parsed_name = Nameable::Latin.new(name_hash)
103
+ @name = @literal_name || build_display_name(given_names, display_particle, nil, @surname, @suffix)
57
104
  else
58
- @parsed_name = Nameable::Latin.new.parse(strip_footnotes(author_name))
105
+ parsed_name = Nameable::Latin.new.parse(strip_footnotes(author_name))
106
+ @parsed_name = parsed_name
107
+ @given_names = parsed_name.first
108
+ @middle_names = parsed_name.middle
109
+ @surname = parsed_name.last
110
+ @suffix = parsed_name.suffix
111
+ @dropping_particle = nil
112
+ @non_dropping_particle = nil
113
+ @literal_name = nil
114
+ @name = build_display_name(@given_names, nil, nil, @surname, @suffix)
59
115
  end
116
+ end
60
117
 
61
- @name = @parsed_name.to_nameable
118
+ def normalize_name_parts(author_hash)
119
+ {
120
+ literal: fetch_name_field(author_hash, %w[literal]),
121
+ given_names: fetch_name_field(author_hash, ["given-names", "given", "first", "firstname"]),
122
+ dropping_particle: fetch_name_field(author_hash, ["dropping-particle"]),
123
+ non_dropping_particle: fetch_name_field(author_hash, ["non-dropping-particle"]),
124
+ surname: fetch_name_field(author_hash, ["surname", "family"]),
125
+ suffix: fetch_name_field(author_hash, ["suffix"])
126
+ }
127
+ end
128
+
129
+ def fetch_name_field(name_hash, keys)
130
+ keys.map do |key|
131
+ name_hash[key] || name_hash[key.to_sym]
132
+ end.compact.first
133
+ end
134
+
135
+ def build_display_name(given_names, dropping_particle, non_dropping_particle, surname, suffix)
136
+ [given_names, dropping_particle, non_dropping_particle, surname, suffix].map do |part|
137
+ part.to_s.strip
138
+ end.reject(&:empty?).join(" ")
62
139
  end
63
140
 
64
141
  # Input: Arfon Smith^[Corresponding author: arfon@example.com]
65
142
  # Output: Arfon Smith
66
143
  def strip_footnotes(author_name)
144
+ return nil if author_name.nil?
145
+
67
146
  author_name.to_s[AUTHOR_FOOTNOTE_REGEX]
68
147
  end
69
148
 
data/lib/theoj/paper.rb CHANGED
@@ -26,14 +26,14 @@ module Theoj
26
26
  end
27
27
 
28
28
  def citation_author
29
- surname = authors.first.last_name
30
- initials = authors.first.initials
29
+ first_author = authors.first
30
+ surname = first_author.citation_last_name.to_s
31
+ initials = first_author.initials
31
32
 
32
- if authors.size > 1
33
- return "#{surname} et al."
34
- else
35
- return "#{surname}, #{initials}"
36
- end
33
+ return "#{surname} et al." if authors.size > 1
34
+ return surname if initials.to_s.strip.empty?
35
+
36
+ "#{surname}, #{initials}"
37
37
  end
38
38
 
39
39
  def title
@@ -187,7 +187,7 @@ module Theoj
187
187
 
188
188
  def plaintext(t)
189
189
  parsed_title = Commonmarker.to_html(t.strip, options:{ render: { hardbreaks: false } })
190
- parsed_title.gsub("\n", "").gsub(" ", " ").gsub(/\A<p>/, "").gsub(/<\/p>\z/, "").gsub("<span data-escaped-char>", "").gsub("<span>", "").gsub(/<\/span>/, "")
190
+ parsed_title.gsub("\n", "").gsub(" ", " ").gsub(/\A<p>/, "").gsub(/<\/p>\z/, "").gsub("<span data-escaped-char>", "").gsub("<span>", "").gsub(/<\/span>/, "").gsub("<code>", "").gsub(/<\/code>/, "").gsub("<!-- raw HTML omitted -->","")
191
191
  end
192
192
 
193
193
  def format_date(date_string)
data/lib/theoj/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Theoj
2
- VERSION = "1.11.0"
2
+ VERSION = "1.12.0"
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: theoj
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.11.0
4
+ version: 1.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Juanjo Bazán
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-06-21 00:00:00.000000000 Z
10
+ date: 2026-01-20 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: octokit
@@ -121,6 +121,20 @@ dependencies:
121
121
  - - ">="
122
122
  - !ruby/object:Gem::Version
123
123
  version: '0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: ostruct
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ type: :runtime
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
124
138
  - !ruby/object:Gem::Dependency
125
139
  name: rake
126
140
  requirement: !ruby/object:Gem::Requirement