cff 0.9.0 → 1.0.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 +4 -4
- data/CHANGES.md +73 -0
- data/CITATION.cff +3 -3
- data/CONTRIBUTING.md +71 -0
- data/LICENCE +1 -1
- data/README.md +27 -21
- data/Rakefile +9 -8
- data/cff.gemspec +12 -11
- data/lib/cff/citable.rb +72 -0
- data/lib/cff/entity.rb +11 -31
- data/lib/cff/errors.rb +2 -5
- data/lib/cff/file.rb +23 -18
- data/lib/cff/formatters/all.rb +26 -0
- data/lib/cff/formatters/apalike.rb +145 -0
- data/lib/cff/formatters/bibtex.rb +205 -0
- data/lib/cff/formatters/formatter.rb +98 -0
- data/lib/cff/formatters.rb +61 -0
- data/lib/cff/identifier.rb +13 -8
- data/lib/cff/{model.rb → index.rb} +65 -81
- data/lib/cff/licensable.rb +3 -3
- data/lib/cff/model_part.rb +46 -10
- data/lib/cff/person.rb +8 -10
- data/lib/cff/reference.rb +55 -114
- data/lib/cff/schema.rb +23 -0
- data/lib/{schema → cff/schemas}/1.2.0.json +0 -0
- data/lib/cff/util.rb +63 -6
- data/lib/cff/validatable.rb +12 -11
- data/lib/cff/version.rb +2 -2
- data/lib/cff.rb +4 -27
- metadata +37 -31
- data/lib/cff/formatter/apa_formatter.rb +0 -77
- data/lib/cff/formatter/bibtex_formatter.rb +0 -122
- data/lib/cff/formatter/formatter.rb +0 -63
data/lib/cff/identifier.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright (c) 2018-
|
3
|
+
# Copyright (c) 2018-2022 The Ruby Citation File Format Developers.
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
6
|
# you may not use this file except in compliance with the License.
|
@@ -14,9 +14,11 @@
|
|
14
14
|
# See the License for the specific language governing permissions and
|
15
15
|
# limitations under the License.
|
16
16
|
|
17
|
+
require_relative 'model_part'
|
18
|
+
require_relative 'schema'
|
19
|
+
|
17
20
|
##
|
18
21
|
module CFF
|
19
|
-
|
20
22
|
# An Identifier represents an identifier in a CITATION.cff file.
|
21
23
|
#
|
22
24
|
# Identifier implements all of the fields listed in the
|
@@ -29,11 +31,13 @@ module CFF
|
|
29
31
|
# * `type`
|
30
32
|
# * `value`
|
31
33
|
class Identifier < ModelPart
|
32
|
-
|
33
|
-
|
34
|
+
ALLOWED_FIELDS = # :nodoc:
|
35
|
+
SCHEMA_FILE['definitions']['identifier']['anyOf'].first['properties'].keys.dup.freeze
|
34
36
|
|
35
37
|
# The [defined set of identifier types](https://github.com/citation-file-format/citation-file-format/blob/main/README.md#identifier-type-strings).
|
36
|
-
IDENTIFIER_TYPES = ['
|
38
|
+
IDENTIFIER_TYPES = SCHEMA_FILE['definitions']['identifier']['anyOf'].map do |id|
|
39
|
+
id['properties']['type']['enum'].first
|
40
|
+
end.freeze
|
37
41
|
|
38
42
|
# :call-seq:
|
39
43
|
# new -> Identifier
|
@@ -44,15 +48,16 @@ module CFF
|
|
44
48
|
# Create a new Identifier with the optionally supplied type and value.
|
45
49
|
# If the supplied type is invalid, then neither the type or value are set.
|
46
50
|
def initialize(param = nil, *more)
|
51
|
+
super()
|
52
|
+
|
47
53
|
if param.is_a?(Hash)
|
48
54
|
@fields = param
|
49
|
-
@fields.default = ''
|
50
55
|
else
|
51
|
-
@fields =
|
56
|
+
@fields = {}
|
52
57
|
|
53
58
|
unless param.nil?
|
54
59
|
self.type = param
|
55
|
-
@fields['value'] = more[0] unless @fields['type'].
|
60
|
+
@fields['value'] = more[0] unless @fields['type'].nil?
|
56
61
|
end
|
57
62
|
end
|
58
63
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright (c) 2018-
|
3
|
+
# Copyright (c) 2018-2022 The Ruby Citation File Format Developers.
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
6
|
# you may not use this file except in compliance with the License.
|
@@ -14,19 +14,31 @@
|
|
14
14
|
# See the License for the specific language governing permissions and
|
15
15
|
# limitations under the License.
|
16
16
|
|
17
|
+
require_relative 'util'
|
18
|
+
require_relative 'model_part'
|
19
|
+
require_relative 'entity'
|
20
|
+
require_relative 'identifier'
|
21
|
+
require_relative 'licensable'
|
22
|
+
require_relative 'person'
|
23
|
+
require_relative 'reference'
|
24
|
+
require_relative 'schema'
|
25
|
+
require_relative 'validatable'
|
26
|
+
require_relative 'citable'
|
27
|
+
|
28
|
+
require 'yaml'
|
29
|
+
|
17
30
|
##
|
18
31
|
module CFF
|
19
|
-
|
20
|
-
# Model is the core data structure for a CITATION.cff file. It can be
|
32
|
+
# Index is the core data structure for a CITATION.cff file. It can be
|
21
33
|
# accessed direcly, or via File.
|
22
34
|
#
|
23
|
-
#
|
35
|
+
# Index implements all of the fields listed in the
|
24
36
|
# [CFF standard](https://citation-file-format.github.io/). Complex
|
25
37
|
# fields - `authors`, `contact`, `identifiers`, `keywords`,
|
26
|
-
# `preferred-citation` and `
|
27
|
-
# fields are simple strings and can be set as such. A field which has
|
28
|
-
# been set will return the empty string. The simple fields are (with
|
29
|
-
# in parentheses):
|
38
|
+
# `preferred-citation`, `references` and `type` - are documented below. All
|
39
|
+
# other fields are simple strings and can be set as such. A field which has
|
40
|
+
# not been set will return the empty string. The simple fields are (with
|
41
|
+
# defaults in parentheses):
|
30
42
|
#
|
31
43
|
# * `abstract`
|
32
44
|
# * `cff_version`
|
@@ -43,63 +55,63 @@ module CFF
|
|
43
55
|
# * `title`
|
44
56
|
# * `url`
|
45
57
|
# * `version`
|
46
|
-
class
|
47
|
-
|
58
|
+
class Index < ModelPart
|
59
|
+
include Citable
|
48
60
|
include Licensable
|
49
61
|
include Validatable
|
50
62
|
|
51
|
-
ALLOWED_FIELDS = [
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
'repository', 'repository-artifact', 'repository-code', 'title',
|
56
|
-
'url', 'version'
|
57
|
-
].freeze # :nodoc:
|
63
|
+
ALLOWED_FIELDS = SCHEMA_FILE['properties'].keys.freeze # :nodoc:
|
64
|
+
|
65
|
+
# The allowed CFF [types](https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#type).
|
66
|
+
MODEL_TYPES = SCHEMA_FILE['properties']['type']['enum'].dup.freeze
|
58
67
|
|
59
68
|
# The default message to use if none is explicitly set.
|
60
69
|
DEFAULT_MESSAGE = 'If you use this software in your work, please cite ' \
|
61
70
|
'it using the following metadata'
|
62
71
|
|
72
|
+
attr_date :date_released
|
73
|
+
|
63
74
|
# :call-seq:
|
64
|
-
# new(title) ->
|
65
|
-
# new(title) { |
|
75
|
+
# new(title) -> Index
|
76
|
+
# new(title) { |index| block } -> Index
|
66
77
|
#
|
67
|
-
# Initialize a new
|
78
|
+
# Initialize a new Index with the supplied title.
|
68
79
|
def initialize(param)
|
80
|
+
super()
|
81
|
+
|
69
82
|
if param.is_a?(Hash)
|
70
|
-
@fields =
|
71
|
-
@fields.default = ''
|
83
|
+
@fields = build_index(param)
|
72
84
|
else
|
73
|
-
@fields =
|
85
|
+
@fields = {}
|
74
86
|
@fields['cff-version'] = DEFAULT_SPEC_VERSION
|
75
87
|
@fields['message'] = DEFAULT_MESSAGE
|
76
88
|
@fields['title'] = param
|
77
89
|
end
|
78
90
|
|
79
91
|
%w[authors contact identifiers keywords references].each do |field|
|
80
|
-
@fields[field] = [] if @fields[field].empty?
|
92
|
+
@fields[field] = [] if @fields[field].nil? || @fields[field].empty?
|
81
93
|
end
|
82
94
|
|
83
95
|
yield self if block_given?
|
84
96
|
end
|
85
97
|
|
86
98
|
# :call-seq:
|
87
|
-
# read(String) ->
|
99
|
+
# read(String) -> Index
|
88
100
|
#
|
89
|
-
# Read a CFF
|
90
|
-
def self.read(
|
91
|
-
new(YAML.safe_load(
|
101
|
+
# Read a CFF Index from a String and parse it for subsequent manipulation.
|
102
|
+
def self.read(index)
|
103
|
+
new(YAML.safe_load(index, permitted_classes: [Date, Time]))
|
92
104
|
end
|
93
105
|
|
94
106
|
# :call-seq:
|
95
|
-
# open(String) ->
|
96
|
-
# open(String) { |cff| block } ->
|
107
|
+
# open(String) -> Index
|
108
|
+
# open(String) { |cff| block } -> Index
|
97
109
|
#
|
98
|
-
# With no associated block,
|
99
|
-
# optional code block is given, it will be passed the parsed
|
100
|
-
# argument and the
|
101
|
-
def self.open(
|
102
|
-
cff =
|
110
|
+
# With no associated block, Index.open is a synonym for ::read. If the
|
111
|
+
# optional code block is given, it will be passed the parsed index as an
|
112
|
+
# argument and the Index will be returned when the block terminates.
|
113
|
+
def self.open(index)
|
114
|
+
cff = Index.read(index)
|
103
115
|
|
104
116
|
yield cff if block_given?
|
105
117
|
|
@@ -107,61 +119,33 @@ module CFF
|
|
107
119
|
end
|
108
120
|
|
109
121
|
# :call-seq:
|
110
|
-
#
|
122
|
+
# type = type
|
111
123
|
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
@fields['
|
124
|
+
# Sets the type of this CFF Index. The type is currently restricted to one
|
125
|
+
# of `software` or `dataset`. If this field is not set then you should
|
126
|
+
# assume that the type is `software`.
|
127
|
+
def type=(type)
|
128
|
+
type = type.downcase
|
129
|
+
@fields['type'] = type if MODEL_TYPES.include?(type)
|
118
130
|
end
|
119
131
|
|
120
132
|
def to_yaml # :nodoc:
|
121
133
|
YAML.dump fields, line_width: -1, indentation: 2
|
122
134
|
end
|
123
135
|
|
124
|
-
# :call-seq:
|
125
|
-
# to_apalike(preferred_citation: true) -> String
|
126
|
-
#
|
127
|
-
# Output this Model in an APA-like format. Setting
|
128
|
-
# `preferred_citation: true` will honour the `preferred_citation` field in
|
129
|
-
# the model if one is present (default).
|
130
|
-
#
|
131
|
-
# *Note:* This method assumes that this Model is valid when called.
|
132
|
-
def to_apalike(preferred_citation: true)
|
133
|
-
CFF::ApaFormatter.format(
|
134
|
-
model: self, preferred_citation: preferred_citation
|
135
|
-
)
|
136
|
-
end
|
137
|
-
|
138
|
-
# :call-seq:
|
139
|
-
# to_bibtex(preferred_citation: true) -> String
|
140
|
-
#
|
141
|
-
# Output this Model in BibTeX format. Setting
|
142
|
-
# `preferred_citation: true` will honour the `preferred_citation` field in
|
143
|
-
# the model if one is present (default).
|
144
|
-
#
|
145
|
-
# *Note:* This method assumes that this Model is valid when called.
|
146
|
-
def to_bibtex(preferred_citation: true)
|
147
|
-
CFF::BibtexFormatter.format(
|
148
|
-
model: self, preferred_citation: preferred_citation
|
149
|
-
)
|
150
|
-
end
|
151
|
-
|
152
136
|
private
|
153
137
|
|
154
138
|
def fields
|
155
139
|
%w[authors contact identifiers references].each do |field|
|
156
|
-
normalize_modelpart_array!(@fields[field])
|
140
|
+
Util.normalize_modelpart_array!(@fields[field])
|
157
141
|
end
|
158
142
|
|
159
|
-
fields_to_hash(@fields)
|
143
|
+
Util.fields_to_hash(@fields)
|
160
144
|
end
|
161
145
|
|
162
|
-
def
|
163
|
-
build_actor_collection!(fields['authors'] || [])
|
164
|
-
build_actor_collection!(fields['contact'] || [])
|
146
|
+
def build_index(fields) # rubocop:disable Metrics
|
147
|
+
Util.build_actor_collection!(fields['authors'] || [])
|
148
|
+
Util.build_actor_collection!(fields['contact'] || [])
|
165
149
|
(fields['identifiers'] || []).map! do |i|
|
166
150
|
Identifier.new(i)
|
167
151
|
end
|
@@ -172,7 +156,7 @@ module CFF
|
|
172
156
|
Reference.new(fields['preferred-citation'])
|
173
157
|
|
174
158
|
# Only attempt an update of the `cff-version` field if it is present.
|
175
|
-
fields['cff-version'] &&= update_cff_version(fields['cff-version'])
|
159
|
+
fields['cff-version'] &&= Util.update_cff_version(fields['cff-version'])
|
176
160
|
|
177
161
|
fields
|
178
162
|
end
|
@@ -191,7 +175,7 @@ module CFF
|
|
191
175
|
# list, use:
|
192
176
|
#
|
193
177
|
# ```
|
194
|
-
#
|
178
|
+
# index.authors << author
|
195
179
|
# ```
|
196
180
|
#
|
197
181
|
# Authors can be a Person or Entity.
|
@@ -214,7 +198,7 @@ module CFF
|
|
214
198
|
# list, use:
|
215
199
|
#
|
216
200
|
# ```
|
217
|
-
#
|
201
|
+
# index.contact << contact
|
218
202
|
# ```
|
219
203
|
#
|
220
204
|
# Contacts can be a Person or Entity.
|
@@ -237,7 +221,7 @@ module CFF
|
|
237
221
|
# the list, use:
|
238
222
|
#
|
239
223
|
# ```
|
240
|
-
#
|
224
|
+
# index.identifiers << identifier
|
241
225
|
# ```
|
242
226
|
|
243
227
|
##
|
@@ -256,7 +240,7 @@ module CFF
|
|
256
240
|
# list, use:
|
257
241
|
#
|
258
242
|
# ```
|
259
|
-
#
|
243
|
+
# index.keywords << keyword
|
260
244
|
# ```
|
261
245
|
#
|
262
246
|
# Keywords will be converted to Strings on output.
|
@@ -293,7 +277,7 @@ module CFF
|
|
293
277
|
# list, use:
|
294
278
|
#
|
295
279
|
# ```
|
296
|
-
#
|
280
|
+
# index.references << reference
|
297
281
|
# ```
|
298
282
|
|
299
283
|
##
|
data/lib/cff/licensable.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright (c) 2018-
|
3
|
+
# Copyright (c) 2018-2022 The Ruby Citation File Format Developers.
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
6
|
# you may not use this file except in compliance with the License.
|
@@ -14,12 +14,12 @@
|
|
14
14
|
# See the License for the specific language governing permissions and
|
15
15
|
# limitations under the License.
|
16
16
|
|
17
|
+
require_relative 'schema'
|
18
|
+
|
17
19
|
##
|
18
20
|
module CFF
|
19
|
-
|
20
21
|
# Functionality to add licence(s) to parts of the CFF model.
|
21
22
|
module Licensable
|
22
|
-
|
23
23
|
LICENSES = SCHEMA_FILE['definitions']['license-enum']['enum'].dup.freeze # :nodoc:
|
24
24
|
|
25
25
|
# :call-seq:
|
data/lib/cff/model_part.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright (c) 2018-
|
3
|
+
# Copyright (c) 2018-2022 The Ruby Citation File Format Developers.
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
6
|
# you may not use this file except in compliance with the License.
|
@@ -14,18 +14,16 @@
|
|
14
14
|
# See the License for the specific language governing permissions and
|
15
15
|
# limitations under the License.
|
16
16
|
|
17
|
+
require 'date'
|
18
|
+
|
17
19
|
##
|
18
20
|
module CFF
|
19
|
-
|
20
|
-
#
|
21
|
-
# This includes Model, Person, Entity and Reference.
|
21
|
+
# ModelPart is the superclass of anything that makes up part of the CFF Index.
|
22
|
+
# This includes Index, Person, Entity and Reference.
|
22
23
|
#
|
23
|
-
# ModelPart
|
24
|
+
# ModelPart provides only one method for the public API: `empty?`.
|
24
25
|
class ModelPart
|
25
|
-
|
26
26
|
# :stopdoc:
|
27
|
-
include Util
|
28
|
-
|
29
27
|
attr_reader :fields
|
30
28
|
|
31
29
|
def method_missing(name, *args)
|
@@ -35,7 +33,7 @@ module CFF
|
|
35
33
|
if n.end_with?('=')
|
36
34
|
@fields[n.chomp('=')] = args[0] || ''
|
37
35
|
else
|
38
|
-
@fields[n]
|
36
|
+
@fields[n].nil? ? '' : @fields[n]
|
39
37
|
end
|
40
38
|
end
|
41
39
|
|
@@ -43,7 +41,45 @@ module CFF
|
|
43
41
|
n = method_to_field(name.id2name)
|
44
42
|
self.class::ALLOWED_FIELDS.include?(n.chomp('=')) || super
|
45
43
|
end
|
46
|
-
|
47
44
|
# :startdoc:
|
45
|
+
|
46
|
+
# :call-seq:
|
47
|
+
# empty? -> false
|
48
|
+
#
|
49
|
+
# Define `empty?` for CFF classes so that they can be tested in the
|
50
|
+
# same way as strings and arrays.
|
51
|
+
#
|
52
|
+
# This always returns `false` because CFF classes always return something
|
53
|
+
# from all of their methods.
|
54
|
+
def empty?
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.attr_date(*symbols) # :nodoc:
|
59
|
+
symbols.each do |symbol|
|
60
|
+
field = symbol.to_s.tr('_', '-')
|
61
|
+
|
62
|
+
class_eval(
|
63
|
+
# def date_end=(date)
|
64
|
+
# date = (date.is_a?(Date) ? date.dup : Date.parse(date))
|
65
|
+
#
|
66
|
+
# @fields['date-end'] = date
|
67
|
+
# end
|
68
|
+
<<-END_SETTER, __FILE__, __LINE__ + 1
|
69
|
+
def #{symbol}=(date)
|
70
|
+
date = (date.is_a?(Date) ? date.dup : Date.parse(date))
|
71
|
+
|
72
|
+
@fields['#{field}'] = date
|
73
|
+
end
|
74
|
+
END_SETTER
|
75
|
+
)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def method_to_field(name)
|
82
|
+
name.tr('_', '-')
|
83
|
+
end
|
48
84
|
end
|
49
85
|
end
|
data/lib/cff/person.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright (c) 2018-
|
3
|
+
# Copyright (c) 2018-2022 The Ruby Citation File Format Developers.
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
6
|
# you may not use this file except in compliance with the License.
|
@@ -14,9 +14,11 @@
|
|
14
14
|
# See the License for the specific language governing permissions and
|
15
15
|
# limitations under the License.
|
16
16
|
|
17
|
+
require_relative 'model_part'
|
18
|
+
require_relative 'schema'
|
19
|
+
|
17
20
|
##
|
18
21
|
module CFF
|
19
|
-
|
20
22
|
# A Person represents a person in a CITATION.cff file. A Person might have a
|
21
23
|
# number of roles, such as author, contact, editor, etc.
|
22
24
|
#
|
@@ -43,12 +45,7 @@ module CFF
|
|
43
45
|
# * `tel`
|
44
46
|
# * `website`
|
45
47
|
class Person < ModelPart
|
46
|
-
|
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:
|
48
|
+
ALLOWED_FIELDS = SCHEMA_FILE['definitions']['person']['properties'].keys.freeze # :nodoc:
|
52
49
|
|
53
50
|
# :call-seq:
|
54
51
|
# new -> Person
|
@@ -58,11 +55,12 @@ module CFF
|
|
58
55
|
#
|
59
56
|
# Create a new Person with the optionally supplied given and family names.
|
60
57
|
def initialize(param = nil, *more)
|
58
|
+
super()
|
59
|
+
|
61
60
|
if param.is_a?(Hash)
|
62
61
|
@fields = param
|
63
|
-
@fields.default = ''
|
64
62
|
else
|
65
|
-
@fields =
|
63
|
+
@fields = {}
|
66
64
|
|
67
65
|
unless param.nil?
|
68
66
|
@fields['family-names'] = more[0]
|