cff 0.1.0 → 0.8.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.
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018-2021 The Ruby Citation File Format Developers.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ ##
18
+ module CFF
19
+
20
+ # ModelPart is the superclass of anything that makes up part of the CFF Model.
21
+ # This includes Model, Person, Entity and Reference.
22
+ #
23
+ # ModelPart does not provide any methods or fields for the public API.
24
+ class ModelPart
25
+
26
+ # :stopdoc:
27
+ include Util
28
+
29
+ attr_reader :fields
30
+
31
+ def method_missing(name, *args)
32
+ n = method_to_field(name.id2name)
33
+ super unless self.class::ALLOWED_FIELDS.include?(n.chomp('='))
34
+
35
+ if n.end_with?('=')
36
+ @fields[n.chomp('=')] = args[0] || ''
37
+ else
38
+ @fields[n]
39
+ end
40
+ end
41
+
42
+ def respond_to_missing?(name, *)
43
+ n = method_to_field(name.id2name)
44
+ self.class::ALLOWED_FIELDS.include?(n.chomp('=')) || super
45
+ end
46
+
47
+ # :startdoc:
48
+ end
49
+ end
data/lib/cff/person.rb CHANGED
@@ -1,4 +1,6 @@
1
- # Copyright (c) 2018 Robert Haines.
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018-2021 The Ruby Citation File Format Developers.
2
4
  #
3
5
  # Licensed under the Apache License, Version 2.0 (the "License");
4
6
  # you may not use this file except in compliance with the License.
@@ -12,24 +14,63 @@
12
14
  # See the License for the specific language governing permissions and
13
15
  # limitations under the License.
14
16
 
15
- #
17
+ ##
16
18
  module CFF
17
19
 
18
20
  # A Person represents a person in a CITATION.cff file. A Person might have a
19
21
  # number of roles, such as author, contact, editor, etc.
20
- class Person
22
+ #
23
+ # Person implements all of the fields listed in the
24
+ # [CFF standard](https://citation-file-format.github.io/). All fields
25
+ # are simple strings and can be set as such. A field which has not been set
26
+ # will return the empty string. The simple fields are (with defaults in
27
+ # parentheses):
28
+ #
29
+ # * `address`
30
+ # * `affiliation`
31
+ # * `alias`
32
+ # * `city`
33
+ # * `country`
34
+ # * `email`
35
+ # * `family_names`
36
+ # * `fax`
37
+ # * `given_names`
38
+ # * `name_particle`
39
+ # * `name_suffix`
40
+ # * `orcid`
41
+ # * `post_code`
42
+ # * `region`
43
+ # * `tel`
44
+ # * `website`
45
+ class Person < ModelPart
21
46
 
22
- attr_reader :fields # :nodoc:
47
+ ALLOWED_FIELDS = [
48
+ 'address', 'affiliation', 'alias', 'city', 'country', 'email',
49
+ 'family-names', 'fax', 'given-names', 'name-particle', 'name-suffix',
50
+ 'orcid', 'post-code', 'region', 'tel', 'website'
51
+ ].freeze # :nodoc:
23
52
 
24
53
  # :call-seq:
54
+ # new -> Person
55
+ # new { |person| block } -> Person
25
56
  # new(given_name, family_name) -> Person
57
+ # new(given_name, family_name) { |person| block } -> Person
26
58
  #
27
- # Create a new Person with the supplied given and family names.
28
- def initialize(given, family)
29
- @fields = Hash.new('')
30
- @fields['family-names'] = family
31
- @fields['given-names'] = given
32
- end
59
+ # Create a new Person with the optionally supplied given and family names.
60
+ def initialize(param = nil, *more)
61
+ if param.is_a?(Hash)
62
+ @fields = param
63
+ @fields.default = ''
64
+ else
65
+ @fields = Hash.new('')
33
66
 
67
+ unless param.nil?
68
+ @fields['family-names'] = more[0]
69
+ @fields['given-names'] = param
70
+ end
71
+ end
72
+
73
+ yield self if block_given?
74
+ end
34
75
  end
35
76
  end
@@ -0,0 +1,541 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018-2021 The Ruby Citation File Format Developers.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ ##
18
+ module CFF
19
+
20
+ # Reference provides a reference pertaining to the software version or the
21
+ # software itself, e.g., a software paper describing the abstract concepts of
22
+ # the software, a paper describing an algorithm that has been implemented in
23
+ # the software version, etc.
24
+ #
25
+ # Reference implements all of the fields listed in the
26
+ # [CFF standard](https://citation-file-format.github.io/). Complex
27
+ # fields - `authors`, `contact`, `editors`, `editors_series`, `identifiers`,
28
+ # `keywords`, `languages`, `patent_states`, `recipients`, `senders` and
29
+ # `translators` - are documented below. All other fields are simple strings
30
+ # and can be set as such. A field which has not been set will return the
31
+ # empty string. The simple fields are (with defaults in parentheses):
32
+ #
33
+ # * `abbreviation`
34
+ # * `abstract`
35
+ # * `collection_doi`
36
+ # * `collection_title`
37
+ # * `collection_type`
38
+ # * `commit`
39
+ # * `conference`
40
+ # * `copyright`
41
+ # * `data-type`
42
+ # * `database`
43
+ # * `database_provider`
44
+ # * `date_accessed` - *Note:* returns a `Date` object
45
+ # * `date_downloaded` - *Note:* returns a `Date` object
46
+ # * `date_published` - *Note:* returns a `Date` object
47
+ # * `date_released` - *Note:* returns a `Date` object
48
+ # * `department`
49
+ # * `doi`
50
+ # * `edition`
51
+ # * `end`
52
+ # * `entry`
53
+ # * `filename`
54
+ # * `format`
55
+ # * `institution`
56
+ # * `isbn`
57
+ # * `issn`
58
+ # * `issue`
59
+ # * `issue_date` - *Note:* returns a `Date` object
60
+ # * `issue_title`
61
+ # * `journal`
62
+ # * `license` - *Note:* see documentation for `license =` below
63
+ # * `license_url`
64
+ # * `loc_end`
65
+ # * `loc_start`
66
+ # * `location`
67
+ # * `medium`
68
+ # * `month`
69
+ # * `nihmsid`
70
+ # * `notes`
71
+ # * `number`
72
+ # * `number_volumes`
73
+ # * `pages`
74
+ # * `pmcid`
75
+ # * `publisher`
76
+ # * `repository`
77
+ # * `repository_code`
78
+ # * `repository_artifact`
79
+ # * `scope`
80
+ # * `section`
81
+ # * `start`
82
+ # * `status` - *Note:* see documentation for `status =` below
83
+ # * `thesis_type`
84
+ # * `title`
85
+ # * `type` - *Note:* see documentation for `type =` below
86
+ # * `url`
87
+ # * `version`
88
+ # * `volume`
89
+ # * `volume_title`
90
+ # * `year`
91
+ # * `year_original`
92
+ class Reference < ModelPart
93
+
94
+ include Licensable
95
+
96
+ ALLOWED_FIELDS = [
97
+ 'abbreviation', 'abstract', 'authors', 'collection-doi',
98
+ 'collection-title', 'collection-type', 'commit', 'conference', 'contact',
99
+ 'copyright', 'data-type', 'database', 'database-provider',
100
+ 'date-accessed', 'date-downloaded', 'date-published', 'date-released',
101
+ 'department', 'doi', 'edition', 'editors', 'editors-series', 'end',
102
+ 'entry', 'filename', 'identifiers', 'institution', 'isbn', 'issn',
103
+ 'issue', 'issue-date', 'issue-title', 'journal', 'keywords', 'license',
104
+ 'license-url', 'loc-end', 'loc-start', 'location', 'medium', 'month',
105
+ 'nihmsid', 'notes', 'number', 'number-volumes', 'pages', 'patent-states',
106
+ 'pmcid', 'publisher', 'recipients', 'repository', 'repository-code',
107
+ 'repository-artifact', 'scope', 'section', 'senders', 'start', 'status',
108
+ 'thesis-type', 'title', 'translators', 'type', 'url', 'version',
109
+ 'volume', 'volume-title', 'year', 'year-original'
110
+ ].freeze # :nodoc:
111
+
112
+ # The [defined set of reference types](https://github.com/citation-file-format/citation-file-format#reference-types).
113
+ REFERENCE_TYPES = [
114
+ 'art', 'article', 'audiovisual', 'bill', 'blog', 'book', 'catalogue',
115
+ 'conference', 'conference-paper', 'data', 'database', 'dictionary',
116
+ 'edited-work', 'encyclopedia', 'film-broadcast', 'generic',
117
+ 'government-document', 'grant', 'hearing', 'historical-work',
118
+ 'legal-case', 'legal-rule', 'magazine-article', 'manual', 'map',
119
+ 'multimedia', 'music', 'newspaper-article', 'pamphlet', 'patent',
120
+ 'personal-communication', 'proceedings', 'report', 'serial', 'slides',
121
+ 'software', 'software-code', 'software-container', 'software-executable',
122
+ 'software-virtual-machine', 'sound-recording', 'standard', 'statute',
123
+ 'thesis', 'unpublished', 'video', 'website'
124
+ ].freeze
125
+
126
+ # The [defined set of reference status types](https://github.com/citation-file-format/citation-file-format#status-strings).
127
+ REFERENCE_STATUS_TYPES = [
128
+ 'abstract', 'advance-online', 'in-preparation', 'in-press',
129
+ 'pre-print', 'submitted'
130
+ ].freeze
131
+
132
+ # :call-seq:
133
+ # new(title) -> Reference
134
+ # new(title) { |ref| block } -> Reference
135
+ # new(title, type) -> Reference
136
+ # new(title, type) { |ref| block } -> Reference
137
+ #
138
+ # Create a new Reference with the supplied title and, optionally, type.
139
+ # If type is not given, or is not one of the
140
+ # [defined set of reference types](https://github.com/citation-file-format/citation-file-format#reference-types),
141
+ # 'generic' will be used by default.
142
+ def initialize(param, *more) # rubocop:disable Metrics/AbcSize
143
+ if param.is_a?(Hash)
144
+ @fields = build_model(param)
145
+ @fields.default = ''
146
+ else
147
+ @fields = Hash.new('')
148
+ type = more[0] &&= more[0].downcase
149
+ @fields['type'] = REFERENCE_TYPES.include?(type) ? type : 'generic'
150
+ @fields['title'] = param
151
+ end
152
+
153
+ [
154
+ 'authors', 'contact', 'editors', 'editors-series', 'identifiers',
155
+ 'keywords', 'patent-states', 'recipients', 'senders', 'translators'
156
+ ].each do |field|
157
+ @fields[field] = [] if @fields[field].empty?
158
+ end
159
+
160
+ yield self if block_given?
161
+ end
162
+
163
+ # :call-seq:
164
+ # add_language language
165
+ #
166
+ # Add a language to this Reference. Input is converted to the ISO 639-3
167
+ # three letter language code, so `GER` becomes `deu`, `french` becomes
168
+ # `fra` and `en` becomes `eng`.
169
+ def add_language(lang)
170
+ @fields['languages'] = [] if @fields['languages'].empty?
171
+ lang = LanguageList::LanguageInfo.find(lang)
172
+ return if lang.nil?
173
+
174
+ lang = lang.iso_639_3
175
+ @fields['languages'] << lang unless @fields['languages'].include? lang
176
+ end
177
+
178
+ # :call-seq:
179
+ # reset_languages
180
+ #
181
+ # Reset the list of languages for this Reference to be empty.
182
+ def reset_languages
183
+ @fields.delete('languages')
184
+ end
185
+
186
+ # :call-seq:
187
+ # languages -> Array
188
+ #
189
+ # Return the list of languages associated with this Reference.
190
+ def languages
191
+ @fields['languages'].empty? ? [] : @fields['languages'].dup
192
+ end
193
+
194
+ # :call-seq:
195
+ # date_accessed = date
196
+ #
197
+ # Set the `date-accessed` field. If a non-Date object is passed in it will
198
+ # be parsed into a Date.
199
+ def date_accessed=(date)
200
+ date = Date.parse(date) unless date.is_a?(Date)
201
+
202
+ @fields['date-accessed'] = date
203
+ end
204
+
205
+ # :call-seq:
206
+ # date_downloaded = date
207
+ #
208
+ # Set the `date-downloaded` field. If a non-Date object is passed in it will
209
+ # be parsed into a Date.
210
+ def date_downloaded=(date)
211
+ date = Date.parse(date) unless date.is_a?(Date)
212
+
213
+ @fields['date-downloaded'] = date
214
+ end
215
+
216
+ # :call-seq:
217
+ # date_published = date
218
+ #
219
+ # Set the `date-published` field. If a non-Date object is passed in it will
220
+ # be parsed into a Date.
221
+ def date_published=(date)
222
+ date = Date.parse(date) unless date.is_a?(Date)
223
+
224
+ @fields['date-published'] = date
225
+ end
226
+
227
+ # :call-seq:
228
+ # date_released = date
229
+ #
230
+ # Set the `date-released` field. If a non-Date object is passed in it will
231
+ # be parsed into a Date.
232
+ def date_released=(date)
233
+ date = Date.parse(date) unless date.is_a?(Date)
234
+
235
+ @fields['date-released'] = date
236
+ end
237
+
238
+ # Returns the format of this Reference.
239
+ #
240
+ # This method is explicitly defined to override the private format method
241
+ # that all objects seem to have.
242
+ def format # :nodoc:
243
+ @fields['format']
244
+ end
245
+
246
+ # Sets the format of this Reference.
247
+ #
248
+ # This method is explicitly defined to override the private format method
249
+ # that all objects seem to have.
250
+ def format=(fmt) # :nodoc:
251
+ @fields['format'] = fmt
252
+ end
253
+
254
+ # :call-seq:
255
+ # status = status
256
+ #
257
+ # Sets the status of this Reference. The status is restricted to a
258
+ # [defined set of status types](https://github.com/citation-file-format/citation-file-format#status-strings).
259
+ def status=(status)
260
+ status = status.downcase
261
+ @fields['status'] = status if REFERENCE_STATUS_TYPES.include?(status)
262
+ end
263
+
264
+ # :call-seq:
265
+ # type = type
266
+ #
267
+ # Sets the type of this Reference. The type is restricted to a
268
+ # [defined set of reference types](https://github.com/citation-file-format/citation-file-format#reference-types).
269
+ def type=(type)
270
+ type = type.downcase
271
+ @fields['type'] = type if REFERENCE_TYPES.include?(type)
272
+ end
273
+
274
+ # Override superclass #fields as References contain model parts too.
275
+ def fields # :nodoc:
276
+ [
277
+ 'authors', 'contact', 'editors', 'editors-series', 'identifiers',
278
+ 'recipients', 'senders', 'translators'
279
+ ].each do |field|
280
+ normalize_modelpart_array!(@fields[field])
281
+ end
282
+
283
+ fields_to_hash(@fields)
284
+ end
285
+
286
+ private
287
+
288
+ def build_model(fields) # :nodoc:
289
+ [
290
+ 'authors', 'contact', 'editors', 'editors-series', 'recipients',
291
+ 'senders', 'translators'
292
+ ].each do |field|
293
+ build_actor_collection!(fields[field]) if fields.include?(field)
294
+ end
295
+
296
+ [
297
+ 'conference', 'database-provider', 'institution', 'location',
298
+ 'publisher'
299
+ ].each do |field|
300
+ fields[field] &&= Entity.new(fields[field])
301
+ end
302
+
303
+ (fields['identifiers'] || []).map! do |i|
304
+ Identifier.new(i)
305
+ end
306
+
307
+ fields
308
+ end
309
+
310
+ public
311
+
312
+ # Some documentation of "hidden" methods is provided here, out of the
313
+ # way of the main class code.
314
+
315
+ ##
316
+ # :method: authors
317
+ # :call-seq:
318
+ # authors -> Array
319
+ #
320
+ # Return the list of authors for this Reference. To add an author to the
321
+ # list, use:
322
+ #
323
+ # ```
324
+ # reference.authors << author
325
+ # ```
326
+ #
327
+ # Authors can be a Person or Entity.
328
+
329
+ ##
330
+ # :method: authors=
331
+ # :call-seq:
332
+ # authors = array_of_authors -> Array
333
+ #
334
+ # Replace the list of authors for this reference.
335
+ #
336
+ # Authors can be a Person or Entity.
337
+
338
+ ##
339
+ # :method: contact
340
+ # :call-seq:
341
+ # contact -> Array
342
+ #
343
+ # Return the list of contacts for this Reference. To add a contact to the
344
+ # list, use:
345
+ #
346
+ # ```
347
+ # reference.contact << contact
348
+ # ```
349
+ #
350
+ # Contacts can be a Person or Entity.
351
+
352
+ ##
353
+ # :method: contact=
354
+ # :call-seq:
355
+ # contact = array_of_contacts -> Array
356
+ #
357
+ # Replace the list of contacts for this reference.
358
+ #
359
+ # Contacts can be a Person or Entity.
360
+
361
+ ##
362
+ # :method: editors
363
+ # :call-seq:
364
+ # editors -> Array
365
+ #
366
+ # Return the list of editors for this Reference. To add an editor to the
367
+ # list, use:
368
+ #
369
+ # ```
370
+ # reference.editors << editor
371
+ # ```
372
+ #
373
+ # An editor can be a Person or Entity.
374
+
375
+ ##
376
+ # :method: editors=
377
+ # :call-seq:
378
+ # editors = array_of_editors -> Array
379
+ #
380
+ # Replace the list of editors for this reference.
381
+ #
382
+ # Editors can be a Person or Entity.
383
+
384
+ ##
385
+ # :method: editors_series
386
+ # :call-seq:
387
+ # editors_series -> Array
388
+ #
389
+ # Return the list of series editors for this Reference. To add a series
390
+ # editor to the list, use:
391
+ #
392
+ # ```
393
+ # reference.editors_series << editor
394
+ # ```
395
+ #
396
+ # An editor can be a Person or Entity.
397
+
398
+ ##
399
+ # :method: editors_series=
400
+ # :call-seq:
401
+ # editors_series = array_of_series_editors -> Array
402
+ #
403
+ # Replace the list of series editors for this reference.
404
+ #
405
+ # Series editors can be a Person or Entity.
406
+
407
+ ##
408
+ # :method: identifiers
409
+ # :call-seq:
410
+ # identifiers -> Array
411
+ #
412
+ # Return the list of identifiers for this citation. To add a identifier to
413
+ # the list, use:
414
+ #
415
+ # ```
416
+ # model.identifiers << identifier
417
+ # ```
418
+
419
+ ##
420
+ # :method: identifiers=
421
+ # :call-seq:
422
+ # identifiers = array_of_identifiers -> Array
423
+ #
424
+ # Replace the list of identifiers for this citation.
425
+
426
+ ##
427
+ # :method: keywords
428
+ # :call-seq:
429
+ # keywords -> Array
430
+ #
431
+ # Return the list of keywords for this reference. To add a keyword to the
432
+ # list, use:
433
+ #
434
+ # ```
435
+ # model.keywords << keyword
436
+ # ```
437
+ #
438
+ # Keywords will be converted to Strings on output.
439
+
440
+ ##
441
+ # :method: keywords=
442
+ # :call-seq:
443
+ # keywords = array_of_keywords -> Array
444
+ #
445
+ # Replace the list of keywords for this reference.
446
+ #
447
+ # Keywords will be converted to Strings on output.
448
+
449
+ ##
450
+ # :method: patent_states
451
+ # :call-seq:
452
+ # patent_states -> Array
453
+ #
454
+ # Return the list of patent states for this reference. To add a patent
455
+ # state to the list, use:
456
+ #
457
+ # ```
458
+ # model.patent_states << patent_state
459
+ # ```
460
+ #
461
+ # Patent states will be converted to Strings on output.
462
+
463
+ ##
464
+ # :method: patent_states=
465
+ # :call-seq:
466
+ # patent_states = array_of_states -> Array
467
+ #
468
+ # Replace the list of patent states for this reference.
469
+ #
470
+ # Patent states will be converted to Strings on output.
471
+
472
+ ##
473
+ # :method: recipients
474
+ # :call-seq:
475
+ # recipients -> Array
476
+ #
477
+ # Return the list of recipients for this Reference. To add a recipient
478
+ # to the list, use:
479
+ #
480
+ # ```
481
+ # reference.recipients << recipient
482
+ # ```
483
+ #
484
+ # Recipients can be a Person or Entity.
485
+
486
+ ##
487
+ # :method: recipients=
488
+ # :call-seq:
489
+ # recipients = array_of_recipients -> Array
490
+ #
491
+ # Replace the list of recipients for this reference.
492
+ #
493
+ # Recipients can be a Person or Entity.
494
+
495
+ ##
496
+ # :method: senders
497
+ # :call-seq:
498
+ # senders -> Array
499
+ #
500
+ # Return the list of senders for this Reference. To add a sender to the
501
+ # list, use:
502
+ #
503
+ # ```
504
+ # reference.senders << sender
505
+ # ```
506
+ #
507
+ # Senders can be a Person or Entity.
508
+
509
+ ##
510
+ # :method: senders=
511
+ # :call-seq:
512
+ # senders = array_of_senders -> Array
513
+ #
514
+ # Replace the list of senders for this reference.
515
+ #
516
+ # Senders can be a Person or Entity.
517
+
518
+ ##
519
+ # :method: translators
520
+ # :call-seq:
521
+ # translators -> Array
522
+ #
523
+ # Return the list of translators for this Reference. To add a translator
524
+ # to the list, use:
525
+ #
526
+ # ```
527
+ # reference.translators << translator
528
+ # ```
529
+ #
530
+ # Translators can be a Person or Entity.
531
+
532
+ ##
533
+ # :method: translators=
534
+ # :call-seq:
535
+ # translators = array_of_translators -> Array
536
+ #
537
+ # Replace the list of translators for this reference.
538
+ #
539
+ # Translators can be a Person or Entity.
540
+ end
541
+ end