cff 0.4.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,63 @@
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
+ # Formatter base class
20
+ class Formatter # :nodoc:
21
+
22
+ def self.select_and_check_model(model, preferred_citation)
23
+ if preferred_citation && model.preferred_citation.is_a?(Reference)
24
+ model = model.preferred_citation
25
+ end
26
+
27
+ # Safe to assume valid `Model`s and `Reference`s will have these fields.
28
+ model.authors.empty? || model.title.empty? ? nil : model
29
+ end
30
+
31
+ def self.initials(name)
32
+ name.split.map { |part| part[0].capitalize }.join('. ')
33
+ end
34
+
35
+ # Prefer `repository_code` over `url`
36
+ def self.url(model)
37
+ model.repository_code.empty? ? model.url : model.repository_code
38
+ end
39
+
40
+ def self.month_and_year_from_model(model)
41
+ if model.respond_to?(:year)
42
+ result = [model.month, model.year].map(&:to_s)
43
+
44
+ return result unless result.any?(&:empty?)
45
+ end
46
+
47
+ month_and_year_from_date(model.date_released)
48
+ end
49
+
50
+ def self.month_and_year_from_date(value)
51
+ if value.is_a?(Date)
52
+ [value.month, value.year].map(&:to_s)
53
+ else
54
+ begin
55
+ date = Date.parse(value.to_s)
56
+ [date.month, date.year].map(&:to_s)
57
+ rescue ArgumentError
58
+ ['', '']
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,71 @@
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
+ # An Identifier represents an identifier in a CITATION.cff file.
21
+ #
22
+ # Identifier implements all of the fields listed in the
23
+ # [CFF standard](https://citation-file-format.github.io/). All fields
24
+ # are simple strings and can be set as such. A field which has not been set
25
+ # will return the empty string. The simple fields are (with defaults in
26
+ # parentheses):
27
+ #
28
+ # * `type`
29
+ # * `value`
30
+ class Identifier < ModelPart
31
+
32
+ ALLOWED_FIELDS = ['type', 'value'].freeze # :nodoc:
33
+
34
+ # The [defined set of identifier types](https://github.com/citation-file-format/citation-file-format/blob/main/README.md#identifier-type-strings).
35
+ IDENTIFIER_TYPES = ['doi', 'url', 'swh', 'other'].freeze
36
+
37
+ # :call-seq:
38
+ # new -> Identifier
39
+ # new { |id| block } -> Identifier
40
+ # new(type, value) -> Identifier
41
+ # new(type, value) { |id| block } -> Identifier
42
+ #
43
+ # Create a new Identifier with the optionally supplied type and value.
44
+ # If the supplied type is invalid, then neither the type or value are set.
45
+ def initialize(param = nil, *more)
46
+ if param.is_a?(Hash)
47
+ @fields = param
48
+ @fields.default = ''
49
+ else
50
+ @fields = Hash.new('')
51
+
52
+ unless param.nil?
53
+ self.type = param
54
+ @fields['value'] = more[0] unless @fields['type'].empty?
55
+ end
56
+ end
57
+
58
+ yield self if block_given?
59
+ end
60
+
61
+ # :call-seq:
62
+ # type = type
63
+ #
64
+ # Sets the type of this Identifier. The type is restricted to a
65
+ # [defined set of identifier types](https://github.com/citation-file-format/citation-file-format/blob/main/README.md#identifier-type-strings).
66
+ def type=(type)
67
+ type = type.downcase
68
+ @fields['type'] = type if IDENTIFIER_TYPES.include?(type)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,46 @@
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
+ # Functionality to add licence(s) to parts of the CFF model.
21
+ module Licensable
22
+
23
+ # :nodoc:
24
+ LICENSES = SCHEMA_FILE['definitions']['license-enum']['enum'].dup.freeze
25
+
26
+ # :call-seq:
27
+ # license = license
28
+ # license = Array
29
+ #
30
+ # Set the license, or licenses, of this work. Only licenses that conform
31
+ # to the [SPDX License List](https://spdx.org/licenses/) will be accepted.
32
+ # If you need specify a different license you should set `license-url`
33
+ # with a link to the license instead.
34
+ def license=(lic)
35
+ list = [*lic].select { |l| LICENSES.include?(l) }
36
+ @fields['license'] = case list.length
37
+ when 0
38
+ @fields['license']
39
+ when 1
40
+ list[0]
41
+ else
42
+ list
43
+ end
44
+ end
45
+ end
46
+ end
data/lib/cff/model.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.
@@ -17,21 +19,50 @@ module CFF
17
19
 
18
20
  # Model is the core data structure for a CITATION.cff file. It can be
19
21
  # accessed direcly, or via File.
22
+ #
23
+ # Model implements all of the fields listed in the
24
+ # [CFF standard](https://citation-file-format.github.io/). Complex
25
+ # fields - `authors`, `contact`, `identifiers`, `keywords`,
26
+ # `preferred-citation` and `references` - are documented below. All other
27
+ # fields are simple strings and can be set as such. A field which has not
28
+ # been set will return the empty string. The simple fields are (with defaults
29
+ # in parentheses):
30
+ #
31
+ # * `abstract`
32
+ # * `cff_version`
33
+ # * `commit`
34
+ # * `date_released` - *Note:* returns a `Date` object
35
+ # * `doi`
36
+ # * `license`
37
+ # * `license_url`
38
+ # * `message` (If you use this software in your work, please cite it using
39
+ # the following metadata)
40
+ # * `repository`
41
+ # * `repository_artifact`
42
+ # * `repository_code`
43
+ # * `title`
44
+ # * `url`
45
+ # * `version`
20
46
  class Model < ModelPart
21
47
 
48
+ include Licensable
49
+ include Validatable
50
+
22
51
  ALLOWED_FIELDS = [
23
52
  'abstract', 'authors', 'cff-version', 'contact', 'commit',
24
- 'date-released', 'doi', 'keywords', 'license', 'license-url', 'message',
25
- 'references', 'repository', 'repository-artifact', 'repository-code',
26
- 'title', 'url', 'version'
53
+ 'date-released', 'doi', 'identifiers', 'keywords', 'license',
54
+ 'license-url', 'message', 'preferred-citation', 'references',
55
+ 'repository', 'repository-artifact', 'repository-code', 'title',
56
+ 'url', 'version'
27
57
  ].freeze # :nodoc:
28
58
 
29
59
  # The default message to use if none is explicitly set.
30
60
  DEFAULT_MESSAGE = 'If you use this software in your work, please cite ' \
31
- 'it using the following metadata'.freeze
61
+ 'it using the following metadata'
32
62
 
33
63
  # :call-seq:
34
64
  # new(title) -> Model
65
+ # new(title) { |model| block } -> Model
35
66
  #
36
67
  # Initialize a new Model with the supplied title.
37
68
  def initialize(param)
@@ -45,9 +76,11 @@ module CFF
45
76
  @fields['title'] = param
46
77
  end
47
78
 
48
- %w[authors contact keywords references].each do |field|
79
+ %w[authors contact identifiers keywords references].each do |field|
49
80
  @fields[field] = [] if @fields[field].empty?
50
81
  end
82
+
83
+ yield self if block_given?
51
84
  end
52
85
 
53
86
  # :call-seq:
@@ -61,34 +94,62 @@ module CFF
61
94
  @fields['date-released'] = date
62
95
  end
63
96
 
97
+ def to_yaml # :nodoc:
98
+ YAML.dump fields, line_width: -1, indentation: 2
99
+ end
100
+
64
101
  # :call-seq:
65
- # version = version
102
+ # to_apalike(preferred_citation: true) -> String
66
103
  #
67
- # Set the `version` field.
68
- def version=(version)
69
- @fields['version'] = version.to_s
104
+ # Output this Model in an APA-like format. Setting
105
+ # `preferred_citation: true` will honour the `preferred_citation` field in
106
+ # the model if one is present (default).
107
+ #
108
+ # *Note:* This method assumes that this Model is valid when called.
109
+ def to_apalike(preferred_citation: true)
110
+ CFF::ApaFormatter.format(
111
+ model: self, preferred_citation: preferred_citation
112
+ )
70
113
  end
71
114
 
72
- def to_yaml # :nodoc:
73
- YAML.dump fields, line_width: -1, indentation: 2
115
+ # :call-seq:
116
+ # to_bibtex(preferred_citation: true) -> String
117
+ #
118
+ # Output this Model in BibTeX format. Setting
119
+ # `preferred_citation: true` will honour the `preferred_citation` field in
120
+ # the model if one is present (default).
121
+ #
122
+ # *Note:* This method assumes that this Model is valid when called.
123
+ def to_bibtex(preferred_citation: true)
124
+ CFF::BibtexFormatter.format(
125
+ model: self, preferred_citation: preferred_citation
126
+ )
74
127
  end
75
128
 
76
129
  private
77
130
 
78
131
  def fields
79
- %w[authors contact references].each do |field|
132
+ %w[authors contact identifiers references].each do |field|
80
133
  normalize_modelpart_array!(@fields[field])
81
134
  end
82
135
 
83
136
  fields_to_hash(@fields)
84
137
  end
85
138
 
86
- def build_model(fields)
139
+ def build_model(fields) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
87
140
  build_actor_collection!(fields['authors'] || [])
88
141
  build_actor_collection!(fields['contact'] || [])
142
+ (fields['identifiers'] || []).map! do |i|
143
+ Identifier.new(i)
144
+ end
89
145
  (fields['references'] || []).map! do |r|
90
146
  Reference.new(r)
91
147
  end
148
+ fields['preferred-citation'] &&=
149
+ Reference.new(fields['preferred-citation'])
150
+
151
+ # Only attempt an update of the `cff-version` field if it is present.
152
+ fields['cff-version'] &&= update_cff_version(fields['cff-version'])
92
153
 
93
154
  fields
94
155
  end
@@ -144,6 +205,25 @@ module CFF
144
205
  #
145
206
  # Contacts can be a Person or Entity.
146
207
 
208
+ ##
209
+ # :method: identifiers
210
+ # :call-seq:
211
+ # identifiers -> Array
212
+ #
213
+ # Return the list of identifiers for this citation. To add a identifier to
214
+ # the list, use:
215
+ #
216
+ # ```
217
+ # model.identifiers << identifier
218
+ # ```
219
+
220
+ ##
221
+ # :method: identifiers=
222
+ # :call-seq:
223
+ # identifiers = array_of_identifiers -> Array
224
+ #
225
+ # Replace the list of identifiers for this citation.
226
+
147
227
  ##
148
228
  # :method: keywords
149
229
  # :call-seq:
@@ -167,6 +247,20 @@ module CFF
167
247
  #
168
248
  # Keywords will be converted to Strings on output.
169
249
 
250
+ ##
251
+ # :method: preferred_citation
252
+ # :call-seq:
253
+ # preferred_citation -> Reference
254
+ #
255
+ # Return the preferred citation for this citation.
256
+
257
+ ##
258
+ # :method: preferred_citation=
259
+ # :call-seq:
260
+ # preferred_citation = Reference
261
+ #
262
+ # Replace the preferred citation for this citation.
263
+
170
264
  ##
171
265
  # :method: references
172
266
  # :call-seq:
@@ -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.
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.
@@ -17,26 +19,58 @@ 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.
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`
20
45
  class Person < ModelPart
21
46
 
22
47
  ALLOWED_FIELDS = [
23
- 'address', 'affiliation', 'city', 'country', 'email', 'family-names',
24
- 'fax', 'given-names', 'name-particle', 'name-suffix', 'orcid',
25
- 'post-code', 'region', 'tel', 'website'
48
+ 'address', 'affiliation', 'alias', 'city', 'country', 'email',
49
+ 'family-names', 'fax', 'given-names', 'name-particle', 'name-suffix',
50
+ 'orcid', 'post-code', 'region', 'tel', 'website'
26
51
  ].freeze # :nodoc:
27
52
 
28
53
  # :call-seq:
54
+ # new -> Person
55
+ # new { |person| block } -> Person
29
56
  # new(given_name, family_name) -> Person
57
+ # new(given_name, family_name) { |person| block } -> Person
30
58
  #
31
- # Create a new Person with the supplied given and family names.
32
- def initialize(param, *more)
59
+ # Create a new Person with the optionally supplied given and family names.
60
+ def initialize(param = nil, *more)
33
61
  if param.is_a?(Hash)
34
62
  @fields = param
63
+ @fields.default = ''
35
64
  else
36
65
  @fields = Hash.new('')
37
- @fields['family-names'] = more[0]
38
- @fields['given-names'] = param
66
+
67
+ unless param.nil?
68
+ @fields['family-names'] = more[0]
69
+ @fields['given-names'] = param
70
+ end
39
71
  end
72
+
73
+ yield self if block_given?
40
74
  end
41
75
  end
42
76
  end