relaton-bib 1.0.1 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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