relaton-bib 1.0.1 → 1.1.1

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.
@@ -0,0 +1,26 @@
1
+ module RelatonBib
2
+ class ICS
3
+ # @return [String]
4
+ attr_reader :code, :text
5
+
6
+ # @param code [String]
7
+ # @param text [String]
8
+ def initialize(code:, text:)
9
+ @code = code
10
+ @text = text
11
+ end
12
+
13
+ # @param builder [Nokogiri::XML::Builder]
14
+ def to_xml(builder)
15
+ builder.ics do |b|
16
+ b.code code
17
+ b.text_ text
18
+ end
19
+ end
20
+
21
+ # @return [Hash]
22
+ def to_hash
23
+ { "code" => code, "text" => text }
24
+ end
25
+ end
26
+ end
@@ -11,21 +11,35 @@ module RelatonBib
11
11
  # @return [Array<String>] script Iso15924 code
12
12
  attr_reader :script
13
13
 
14
- # @return [String]
14
+ # @return [String, Array<RelatonBib::LocalizedString>]
15
15
  attr_accessor :content
16
16
 
17
- # @param content [String]
17
+ # @param content [String, Array<RelatonBib::LocalizedString>]
18
18
  # @param language [String] language code Iso639
19
19
  # @param script [String] script code Iso15924
20
20
  def initialize(content, language = nil, script = nil)
21
+ unless content.is_a?(String) || content.is_a?(Array) &&
22
+ (inv = content.reject { |c| c.is_a?(LocalizedString) || c.is_a?(Hash) }).
23
+ none? && content.any?
24
+ klass = content.is_a?(Array) ? inv.first.class : content.class
25
+ raise ArgumentError, "invalid LocalizedString content type: #{klass}"
26
+ end
21
27
  @language = language.is_a?(String) ? [language] : language
22
28
  @script = script.is_a?(String) ? [script] : script
23
- @content = content
29
+ @content = if content.is_a?(Array)
30
+ content.map do |c|
31
+ if c.is_a?(Hash)
32
+ LocalizedString.new c[:content], c[:language], c[:script]
33
+ else c
34
+ end
35
+ end
36
+ else content
37
+ end
24
38
  end
25
39
 
26
40
  # @return [String]
27
41
  def to_s
28
- content
42
+ content.is_a?(String) ? content : content.first.to_s
29
43
  end
30
44
 
31
45
  # @return [TrueClass, FalseClass]
@@ -37,19 +51,26 @@ module RelatonBib
37
51
  def to_xml(builder)
38
52
  return unless content
39
53
 
40
- builder.parent["language"] = language.join(",") if language&.any?
41
- builder.parent["script"] = script.join(",") if script&.any?
42
- builder.text content.encode(xml: :text)
54
+ if content.is_a?(Array)
55
+ content.each { |c| builder.variant { c.to_xml builder } }
56
+ else
57
+ builder.parent["language"] = language.join(",") if language&.any?
58
+ builder.parent["script"] = script.join(",") if script&.any?
59
+ builder.text content.encode(xml: :text)
60
+ end
43
61
  end
44
62
 
45
63
  # @return [Hash]
46
64
  def to_hash
47
- return content unless language || script
65
+ if content.is_a? String
66
+ return content unless language || script
48
67
 
49
- hash = { "content" => content }
50
- hash["language"] = single_element_array(language) if language&.any?
51
- hash["script"] = single_element_array(script) if script&.any?
52
- hash
68
+ hash = { "content" => content }
69
+ hash["language"] = single_element_array(language) if language&.any?
70
+ hash["script"] = single_element_array(script) if script&.any?
71
+ hash
72
+ else content.map &:to_hash
73
+ end
53
74
  end
54
75
  end
55
76
  end
@@ -0,0 +1,141 @@
1
+ module RelatonBib
2
+ class StructuredIdentifierCollection
3
+ include RelatonBib
4
+ extend Forwardable
5
+
6
+ def_delegators :@collection, :any?, :size, :[], :detect
7
+
8
+ # @param collection [Array<RelatonBib::StructuredIdentifier>]
9
+ def initialize(collection)
10
+ @collection = collection
11
+ end
12
+
13
+ # @param builder [Nokogiri::XML::Builder]
14
+ def to_xml(builder)
15
+ @collection.each { |si| si.to_xml builder }
16
+ end
17
+
18
+ # @return [Array<Hash>]
19
+ def to_hash
20
+ single_element_array @collection
21
+ end
22
+
23
+ # remoe year from docnumber
24
+ def remove_date
25
+ @collection.each &:remove_date
26
+ end
27
+
28
+ def remove_part
29
+ @collection.each &:remove_part
30
+ end
31
+
32
+ def all_parts
33
+ @collection.each &:all_parts
34
+ end
35
+
36
+ def presence?
37
+ any?
38
+ end
39
+
40
+ # @return [RelatonBib::StructuredIdentifierCollection]
41
+ # def map(&block)
42
+ # StructuredIdentifierCollection.new @collection.map &block
43
+ # end
44
+ end
45
+
46
+ class StructuredIdentifier
47
+ include RelatonBib
48
+
49
+ # @return [String]
50
+ attr_reader :docnumber
51
+
52
+ # @return [Array<String>]
53
+ attr_reader :agency
54
+
55
+ # @return [String, NilClass]
56
+ attr_reader :type, :klass, :partnumber, :edition, :version, :supplementtype,
57
+ :supplementnumber, :language, :year
58
+
59
+ # rubocop:disable Metrics/MethodLength
60
+
61
+ # @param type [String, NilClass]
62
+ # @param agency [Array<String>]
63
+ # @parma class [Stirng, NilClass]
64
+ # @parma docnumber [String]
65
+ # @param partnumber [String, NilClass]
66
+ # @param edition [String, NilClass]
67
+ # @param version [String, NilClass]
68
+ # @param supplementtype [String, NilClass]
69
+ # @param supplementnumber [String, NilClass]
70
+ # @param language [String, NilClass]
71
+ # @param year [String, NilClass]
72
+ def initialize(docnumber:, **args)
73
+ @type = args[:type]
74
+ @agency = args[:agency]
75
+ @klass = args[:class]
76
+ @docnumber = docnumber
77
+ @partnumber = args[:partnumber]
78
+ @edition = args[:edition]
79
+ @version = args[:version]
80
+ @supplementtype = args[:supplementtype]
81
+ @supplementnumber = args[:supplementnumber]
82
+ @language = args[:language]
83
+ @year = args[:year]
84
+ end
85
+
86
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
87
+
88
+ # @param builder [Nokogiri::XML::Builder]
89
+ def to_xml(builder)
90
+ xml = builder.structuredidentifier do |b|
91
+ agency&.each { |a| b.agency a }
92
+ b.class_ klass if klass
93
+ b.docnumber docnumber
94
+ b.partnumber partnumber if partnumber
95
+ b.edition edition if edition
96
+ b.version version if version
97
+ b.supplementtype supplementtype if supplementtype
98
+ b.supplementnumber supplementnumber if supplementnumber
99
+ b.language language if language
100
+ b.year year if year
101
+ end
102
+ xml[:type] = type if type
103
+ end
104
+
105
+ # @return [Hash]
106
+ def to_hash
107
+ hash = { "docnumber" => docnumber }
108
+ hash["type"] = type if type
109
+ hash["agency"] = single_element_array agency if agency&.any?
110
+ hash["class"] = klass if klass
111
+ hash["partnumber"] = partnumber if partnumber
112
+ hash["edition"] = edition if edition
113
+ hash["version"] = version if version
114
+ hash["supplementtype"] = supplementtype if supplementtype
115
+ hash["supplementnumber"] = supplementnumber if supplementnumber
116
+ hash["language"] = language if language
117
+ hash["year"] = year if year
118
+ hash
119
+ end
120
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
121
+
122
+ def remove_date
123
+ if @type == "Chinese Standard"
124
+ @docnumber.sub!(/-[12]\d\d\d/, "")
125
+ else
126
+ @docnumber.sub!(/:[12]\d\d\d/, "")
127
+ end
128
+ @year = nil
129
+ end
130
+
131
+ # in docid manipulations, assume ISO as the default: id-part:year
132
+ def remove_part
133
+ @partnumber = nil
134
+ @docnumber = @docnumber.sub(/-\d+/, "")
135
+ end
136
+
137
+ def all_parts
138
+ @docnumber = @docnumber + " (all parts)"
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,25 @@
1
+ require "relaton_bib/workgroup"
2
+
3
+ module RelatonBib
4
+ class TechnicalCommittee
5
+ # @return [RelatonBib::WorkGroup]
6
+ attr_reader :workgroup
7
+
8
+ # @param workgroup [RelatonBib::WorkGroup]
9
+ def initialize(workgroup)
10
+ @workgroup = workgroup
11
+ end
12
+
13
+ # @param builder [Nokogiri::XML::Builder]
14
+ def to_xml(builder)
15
+ builder.send "technical-committee" do |b|
16
+ workgroup.to_xml b
17
+ end
18
+ end
19
+
20
+ # @return [Hash]
21
+ def to_hash
22
+ workgroup.to_hash
23
+ end
24
+ end
25
+ end
@@ -1,14 +1,12 @@
1
1
  module RelatonBib
2
2
  class TypedTitleString
3
- # TITLE_TYPES = %w[alternative original unofficial subtitle main].freeze
4
-
5
3
  # @return [String]
6
4
  attr_reader :type
7
5
 
8
6
  # @return [RelatonBib::FormattedString]
9
7
  attr_reader :title
10
8
 
11
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
9
+ # rubocop:disable Metrics/MethodLength
12
10
 
13
11
  # @param type [String]
14
12
  # @param title [RelatonBib::FormattedString, Hash]
@@ -16,10 +14,6 @@ module RelatonBib
16
14
  # @param language [String]
17
15
  # @param script [String]
18
16
  def initialize(**args)
19
- # if args[:type] && !TITLE_TYPES.include?(args[:type])
20
- # warn %{[relaton-bib] title type "#{args[:type]}" is invalid.}
21
- # end
22
-
23
17
  unless args[:title] || args[:content]
24
18
  raise ArgumentError, %{Keyword "title" or "content" should be passed.}
25
19
  end
@@ -30,12 +24,48 @@ module RelatonBib
30
24
  @title = args[:title]
31
25
  else
32
26
  fsargs = args.select do |k, _v|
33
- %i[content language script format].include? k
27
+ %i[content language script format].include? k
34
28
  end
35
29
  @title = FormattedString.new(fsargs)
36
30
  end
37
31
  end
38
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
32
+ # rubocop:enable Metrics/MethodLength
33
+
34
+ # @param title [String]
35
+ # @return [Array<self>]
36
+ def self.from_string(title, lang = nil, script = nil)
37
+ types = %w[title-intro title-main title-part]
38
+ ttls = split_title(title)
39
+ tts = ttls.map.with_index do |p, i|
40
+ new type: types[i], content: p, language: lang, script: script if p
41
+ end.compact
42
+ tts << new(type: "main", content: ttls.compact.join(" - "),
43
+ language: lang, script: script)
44
+ end
45
+
46
+ # @param title [String]
47
+ # @return [Array<String, nil>]
48
+ def self.split_title(title)
49
+ ttls = title.sub(/\w\.Imp\s?\d+\u00A0:\u00A0/, "").split " - "
50
+ case ttls.size
51
+ when 0, 1 then [nil, ttls.first.to_s, nil]
52
+ else intro_or_part ttls
53
+ # when 2, 3 then intro_or_part ttls
54
+ # else [ttls[0], ttls[1], ttls[2..-1].join(" -- ")]
55
+ end
56
+ end
57
+
58
+ # @param ttls [Array<String>]
59
+ # @return [Array<Strin, nil>]
60
+ def self.intro_or_part(ttls)
61
+ if /^(Part|Partie) \d+:/ =~ ttls[1]
62
+ [nil, ttls[0], ttls[1..-1].join(" -- ")]
63
+ else
64
+ parts = ttls.slice(2..-1)
65
+ part = parts.join " -- " if parts.any?
66
+ [ttls[0], ttls[1], part]
67
+ end
68
+ end
39
69
 
40
70
  # @param builder [Nokogiri::XML::Builder]
41
71
  def to_xml(builder)
@@ -1,3 +1,3 @@
1
1
  module RelatonBib
2
- VERSION = "1.0.1".freeze
2
+ VERSION = "1.1.1".freeze
3
3
  end
@@ -0,0 +1,36 @@
1
+ module RelatonBib
2
+ class WorkGroup
3
+ # @return [String]
4
+ attr_reader :content
5
+
6
+ # @return [Integer, nil]
7
+ attr_reader :number
8
+
9
+ # @return [String, nil]
10
+ attr_reader :type
11
+
12
+ # @param content [String]
13
+ # @param number [Integer, nil]
14
+ # @param type [String, nil]
15
+ def initialize(content:, number: nil, type: nil)
16
+ @content = content
17
+ @number = number
18
+ @type = type
19
+ end
20
+
21
+ # @param builder [Nokogiri::XML::Builder]
22
+ def to_xml(builder)
23
+ builder.text content
24
+ builder.parent[:number] = number if number
25
+ builder.parent[:type] = type if type
26
+ end
27
+
28
+ # @return [Hash]
29
+ def to_hash
30
+ hash = { "content" => content }
31
+ hash["number"] = number if number
32
+ hash["type"] = type if type
33
+ hash
34
+ end
35
+ end
36
+ end
@@ -20,6 +20,7 @@ module RelatonBib
20
20
 
21
21
  # @return [Hash]
22
22
  def item_data(bibitem)
23
+ ext = bibitem.at "//ext"
23
24
  {
24
25
  id: bibitem[:id]&.empty? ? nil : bibitem[:id],
25
26
  type: bibitem[:type]&.empty? ? nil : bibitem[:type],
@@ -49,6 +50,10 @@ module RelatonBib
49
50
  keyword: bibitem.xpath("keyword").map(&:text),
50
51
  license: bibitem.xpath("license").map(&:text),
51
52
  validity: fetch_validity(bibitem),
53
+ doctype: ext&.at("doctype")&.text,
54
+ editorialgroup: fetch_editorialgroup(ext),
55
+ ics: fetch_ics(ext),
56
+ structuredidentifier: fetch_structuredidentifier(ext),
52
57
  }
53
58
  end
54
59
  # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
@@ -159,8 +164,12 @@ module RelatonBib
159
164
  def ttitle(title)
160
165
  return unless title
161
166
 
167
+ variants = title.xpath("variant").map do |v|
168
+ LocalizedString.new v.text, v[:language], v[:script]
169
+ end
170
+ content = variants.any? ? variants : title.text
162
171
  TypedTitleString.new(
163
- type: title[:type], content: title.text, language: title[:language],
172
+ type: title[:type], content: content, language: title[:language],
164
173
  script: title[:script], format: title[:format]
165
174
  )
166
175
  end
@@ -169,14 +178,22 @@ module RelatonBib
169
178
  status = item.at("./status")
170
179
  return unless status
171
180
 
172
- stage = status.at "stage"
181
+ stg = status.at "stage"
173
182
  DocumentStatus.new(
174
- stage: stage ? stage.text : status.text,
175
- substage: status.at("substage")&.text,
183
+ stage: stg ? stage(stg) : status.text,
184
+ substage: stage(status.at("substage")),
176
185
  iteration: status.at("iteration")&.text,
177
186
  )
178
187
  end
179
188
 
189
+ # @param node [Nokogiri::XML::Elemen]
190
+ # @return [RelatonBib::DocumentStatus::Stage]
191
+ def stage(elm)
192
+ return unless elm
193
+
194
+ DocumentStatus::Stage.new value: elm.text, abbreviation: elm[:abbreviation]
195
+ end
196
+
180
197
  def fetch_dates(item)
181
198
  item.xpath("./date").reduce([]) do |a, d|
182
199
  type = d[:type].to_s.empty? ? "published" : d[:type]
@@ -232,14 +249,9 @@ module RelatonBib
232
249
  end
233
250
 
234
251
  cn = person.at "./name/completename"
235
- cname = if cn
236
- LocalizedString.new(cn.text, cn[:language], cn[:script])
237
- else
238
- nil
239
- end
240
-
252
+ cname = cn && LocalizedString.new(cn.text, cn[:language], cn[:script])
241
253
  sn = person.at "./name/surname"
242
- sname = sn ? LocalizedString.new(sn.text, sn[:language], sn[:script]) : nil
254
+ sname = sn && LocalizedString.new(sn.text, sn[:language], sn[:script])
243
255
 
244
256
  name = FullName.new(
245
257
  completename: cname, surname: sname,
@@ -279,25 +291,24 @@ module RelatonBib
279
291
  # @return [Array<RelatonBib::FormattedString>]
280
292
  def fetch_abstract(item)
281
293
  item.xpath("./abstract").map do |a|
282
- FormattedString.new(
283
- content: a.text, language: a[:language], script: a[:script], format: a[:format]
284
- )
294
+ FormattedString.new(content: a.text, language: a[:language],
295
+ script: a[:script], format: a[:format])
285
296
  end
286
297
  end
287
298
 
288
299
  # @param item [Nokogiri::XML::Element]
289
- # @return [RelatonBib::CopyrightAssociation]
300
+ # @return [Array<RelatonBib::CopyrightAssociation>]
290
301
  def fetch_copyright(item)
291
- cp = item.at("./copyright") || return
292
- org = cp&.at("owner/organization")
293
- name = org&.at("name")&.text
294
- abbr = org&.at("abbreviation")&.text
295
- url = org&.at("uri")&.text
296
- entity = Organization.new(name: name, abbreviation: abbr, url: url)
297
- from = cp.at("from")&.text
298
- to = cp.at("to")&.text
299
- owner = ContributionInfo.new entity: entity
300
- CopyrightAssociation.new(owner: owner, from: from, to: to)
302
+ item.xpath("./copyright").map do |cp|
303
+ owner = cp.xpath("owner").map do |o|
304
+ ContributionInfo.new entity: get_org(o.at("organization"))
305
+ end
306
+ from = cp.at("from")&.text
307
+ to = cp.at("to")&.text
308
+ scope = cp.at("scope")&.text
309
+ CopyrightAssociation.new(owner: owner, from: from, to: to,
310
+ scope: scope)
311
+ end
301
312
  end
302
313
 
303
314
  # @param item [Nokogiri::XML::Element]
@@ -345,7 +356,7 @@ module RelatonBib
345
356
  if lc[:type]
346
357
  LocalityStack.new [locality(lc)]
347
358
  else
348
- LocalityStack.new lc.xpath("./locality").map { |l| locality l }
359
+ LocalityStack.new(lc.xpath("./locality").map { |l| locality l })
349
360
  end
350
361
  end
351
362
  end
@@ -353,7 +364,7 @@ module RelatonBib
353
364
  # @param loc [Nokogiri::XML::Element]
354
365
  # @return [RelatonBib::Locality]
355
366
  def locality(loc, klass = Locality)
356
- ref_to = (rt = loc.at("./referenceTo")) ? LocalizedString.new(rt.text) : nil
367
+ ref_to = (rt = loc.at("./referenceTo")) && LocalizedString.new(rt.text)
357
368
  klass.new(
358
369
  loc[:type],
359
370
  LocalizedString.new(loc.at("./referenceFrom").text),
@@ -385,6 +396,53 @@ module RelatonBib
385
396
  language: ident[:language], script: ident[:script]
386
397
  )
387
398
  end
399
+
400
+ # @param ext [Nokogiri::XML::Element]
401
+ # @return [RelatonBib::EditorialGroup, nil]
402
+ def fetch_editorialgroup(ext)
403
+ return unless ext && (eg = ext.at "editorialgroup")
404
+
405
+ eg = eg.xpath("technical-committee").map do |tc|
406
+ wg = WorkGroup.new(content: tc.text, number: tc[:number]&.to_i,
407
+ type: tc[:type])
408
+ TechnicalCommittee.new wg
409
+ end
410
+ EditorialGroup.new eg if eg.any?
411
+ end
412
+
413
+ def fetch_ics(ext)
414
+ return [] unless ext
415
+
416
+ ext.xpath("ics").map do |ics|
417
+ ICS.new code: ics.at("code").text, text: ics.at("text").text
418
+ end
419
+ end
420
+
421
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
422
+
423
+ # @param ext [Nokogiri::XML::Element]
424
+ # @return [RelatonBib::StructuredIdentifierCollection]
425
+ def fetch_structuredidentifier(ext)
426
+ return unless ext
427
+
428
+ sids = ext.xpath("structuredidentifier").map do |si|
429
+ StructuredIdentifier.new(
430
+ type: si[:type],
431
+ agency: si.xpath("agency").map(&:text),
432
+ class: si.at("class")&.text,
433
+ docnumber: si.at("docnumber")&.text,
434
+ partnumber: si.at("partnumber")&.text,
435
+ edition: si.at("edition")&.text,
436
+ version: si.at("version")&.text,
437
+ supplementtype: si.at("supplementtype")&.text,
438
+ supplementnumber: si.at("supplementnumber")&.text,
439
+ language: si.at("language")&.text,
440
+ year: si.at("year")&.text,
441
+ )
442
+ end
443
+ StructuredIdentifierCollection.new sids
444
+ end
445
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
388
446
  end
389
447
  end
390
448
  end