cff 0.2.0 → 0.9.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,77 @@
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
+ # Generates an APALIKE citation string
20
+ class ApaFormatter < Formatter # :nodoc:
21
+
22
+ def self.format(model:, preferred_citation: true) # rubocop:disable Metrics/AbcSize
23
+ model = select_and_check_model(model, preferred_citation)
24
+ return if model.nil?
25
+
26
+ output = []
27
+ output << combine_authors(
28
+ model.authors.map { |author| format_author(author) }
29
+ )
30
+
31
+ _, year = month_and_year_from_model(model)
32
+ output << "(#{year})" unless year.empty?
33
+
34
+ version = " (Version #{model.version})" unless model.version.to_s.empty?
35
+ output << "#{model.title}#{version}#{software_label(model)}"
36
+ output << publication_data_from_model(model)
37
+ output << url(model)
38
+
39
+ output.reject(&:empty?).join('. ')
40
+ end
41
+
42
+ def self.publication_data_from_model(model)
43
+ return '' unless model.respond_to?(:journal) && !model.journal.empty?
44
+
45
+ vol = model.volume.to_s.empty? ? '' : "#{model.volume}(#{model.issue})"
46
+
47
+ [model.journal, vol, model.start.to_s].reject(&:empty?).join(', ')
48
+ end
49
+
50
+ # Prefer a DOI over the other URI options.
51
+ def self.url(model)
52
+ model.doi.empty? ? super : "https://doi.org/#{model.doi}"
53
+ end
54
+
55
+ def self.software_label(model)
56
+ return '' if model.is_a?(Reference) && !model.type.include?('software')
57
+
58
+ ' [Computer software]'
59
+ end
60
+
61
+ def self.combine_authors(authors)
62
+ return authors[0].chomp('.') if authors.length == 1
63
+
64
+ "#{authors[0..-2].join(', ')}, & #{authors[-1]}".chomp('.')
65
+ end
66
+
67
+ def self.format_author(author)
68
+ return author.name if author.is_a?(Entity)
69
+
70
+ particle =
71
+ author.name_particle.empty? ? '' : "#{author.name_particle} "
72
+ suffix = author.name_suffix.empty? ? '.' : "., #{author.name_suffix}"
73
+
74
+ "#{particle}#{author.family_names}, #{initials(author.given_names)}#{suffix}"
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,122 @@
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
+ # Generates an BibTex citation string
20
+ class BibtexFormatter < Formatter # :nodoc:
21
+
22
+ def self.format(model:, preferred_citation: true) # rubocop:disable Metrics/AbcSize
23
+ model = select_and_check_model(model, preferred_citation)
24
+ return if model.nil?
25
+
26
+ values = {}
27
+ values['author'] = combine_authors(
28
+ model.authors.map { |author| format_author(author) }
29
+ )
30
+ values['title'] = "{#{model.title}}"
31
+
32
+ publication_data_from_model(model, values)
33
+
34
+ month, year = month_and_year_from_model(model)
35
+ values['month'] = month
36
+ values['year'] = year
37
+
38
+ values['url'] = url(model)
39
+
40
+ values.reject! { |_, v| v.empty? }
41
+ sorted_values = values.sort.map do |key, value|
42
+ "#{key} = {#{value}}"
43
+ end
44
+ sorted_values.insert(0, generate_reference(values))
45
+
46
+ "@#{bibtex_type(model)}{#{sorted_values.join(",\n")}\n}"
47
+ end
48
+
49
+ # Get various bits of information about the reference publication.
50
+ # Reference: https://www.bibtex.com/format/
51
+ def self.publication_data_from_model(model, fields)
52
+ %w[doi journal volume].each do |field|
53
+ fields[field] = model.send(field).to_s if model.respond_to?(field)
54
+ end
55
+
56
+ # BibTeX 'number' is CFF 'issue'.
57
+ fields['number'] = model.issue.to_s if model.respond_to?(:issue)
58
+
59
+ fields['pages'] = pages_from_model(model)
60
+ end
61
+
62
+ # CFF 'pages' is the number of pages, which has no equivalent in BibTeX.
63
+ # Reference: https://www.bibtex.com/f/pages-field/
64
+ def self.pages_from_model(model)
65
+ return '' if !model.respond_to?(:start) || model.start.to_s.empty?
66
+
67
+ start = model.start.to_s
68
+ finish = model.end.to_s
69
+ if finish.empty?
70
+ start
71
+ else
72
+ start == finish ? start : "#{start}--#{finish}"
73
+ end
74
+ end
75
+
76
+ # Do what we can to map between CFF reference types and bibtex types.
77
+ # Reference: https://www.bibtex.com/e/entry-types/
78
+ def self.bibtex_type(model)
79
+ return 'misc' unless model.is_a?(Reference)
80
+
81
+ case model.type
82
+ when 'article', 'book', 'manual', 'unpublished'
83
+ model.type
84
+ when 'conference', 'proceedings'
85
+ 'proceedings'
86
+ when 'conference-paper'
87
+ 'inproceedings'
88
+ when 'magazine-article', 'newspaper-article'
89
+ 'article'
90
+ when 'pamphlet'
91
+ 'booklet'
92
+ else
93
+ 'misc'
94
+ end
95
+ end
96
+
97
+ def self.format_author(author)
98
+ return "{#{author.name}}" if author.is_a?(Entity)
99
+
100
+ particle =
101
+ author.name_particle.empty? ? '' : "#{author.name_particle} "
102
+
103
+ [
104
+ "#{particle}#{author.family_names}",
105
+ author.name_suffix,
106
+ author.given_names
107
+ ].reject(&:empty?).join(', ')
108
+ end
109
+
110
+ def self.combine_authors(authors)
111
+ authors.join(' and ')
112
+ end
113
+
114
+ def self.generate_reference(fields)
115
+ [
116
+ fields['author'].split(',', 2)[0].tr(' -', '_'),
117
+ fields['title'].split[0..2],
118
+ fields['year']
119
+ ].compact.join('_').tr('-$£%&(){}+!?/\\:;\'"~#', '')
120
+ end
121
+ end
122
+ end
@@ -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,72 @@
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
+ # * `description`
29
+ # * `type`
30
+ # * `value`
31
+ class Identifier < ModelPart
32
+
33
+ ALLOWED_FIELDS = ['description', 'type', 'value'].freeze # :nodoc:
34
+
35
+ # 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 = ['doi', 'url', 'swh', 'other'].freeze
37
+
38
+ # :call-seq:
39
+ # new -> Identifier
40
+ # new { |id| block } -> Identifier
41
+ # new(type, value) -> Identifier
42
+ # new(type, value) { |id| block } -> Identifier
43
+ #
44
+ # Create a new Identifier with the optionally supplied type and value.
45
+ # If the supplied type is invalid, then neither the type or value are set.
46
+ def initialize(param = nil, *more)
47
+ if param.is_a?(Hash)
48
+ @fields = param
49
+ @fields.default = ''
50
+ else
51
+ @fields = Hash.new('')
52
+
53
+ unless param.nil?
54
+ self.type = param
55
+ @fields['value'] = more[0] unless @fields['type'].empty?
56
+ end
57
+ end
58
+
59
+ yield self if block_given?
60
+ end
61
+
62
+ # :call-seq:
63
+ # type = type
64
+ #
65
+ # Sets the type of this Identifier. The type is restricted to a
66
+ # [defined set of identifier types](https://github.com/citation-file-format/citation-file-format/blob/main/README.md#identifier-type-strings).
67
+ def type=(type)
68
+ type = type.downcase
69
+ @fields['type'] = type if IDENTIFIER_TYPES.include?(type)
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,45 @@
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
+ LICENSES = SCHEMA_FILE['definitions']['license-enum']['enum'].dup.freeze # :nodoc:
24
+
25
+ # :call-seq:
26
+ # license = license
27
+ # license = Array
28
+ #
29
+ # Set the license, or licenses, of this work. Only licenses that conform
30
+ # to the [SPDX License List](https://spdx.org/licenses/) will be accepted.
31
+ # If you need specify a different license you should set `license-url`
32
+ # with a link to the license instead.
33
+ def license=(lic)
34
+ list = [*lic].select { |l| LICENSES.include?(l) }
35
+ @fields['license'] = case list.length
36
+ when 0
37
+ @fields['license']
38
+ when 1
39
+ list[0]
40
+ else
41
+ list
42
+ end
43
+ end
44
+ end
45
+ 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.
@@ -12,54 +14,176 @@
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
  # Model is the core data structure for a CITATION.cff file. It can be
19
21
  # accessed direcly, or via File.
20
- class Model
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`
46
+ class Model < ModelPart
21
47
 
22
- include Util
48
+ include Licensable
49
+ include Validatable
23
50
 
24
51
  ALLOWED_FIELDS = [
25
- 'abstract',
26
- 'cff-version',
27
- 'commit',
28
- 'date-released',
29
- 'doi',
30
- 'license',
31
- 'license-url',
32
- 'message',
33
- 'repository',
34
- 'repository-artifact',
35
- 'repository-code',
36
- 'title',
37
- 'url',
38
- 'version'
39
- ].freeze # :nodoc:
52
+ 'abstract', 'authors', 'cff-version', 'contact', 'commit',
53
+ 'date-released', 'doi', 'identifiers', 'keywords', 'license',
54
+ 'license-url', 'message', 'preferred-citation', 'references',
55
+ 'repository', 'repository-artifact', 'repository-code', 'title',
56
+ 'url', 'version'
57
+ ].freeze # :nodoc:
40
58
 
41
59
  # The default message to use if none is explicitly set.
42
- DEFAULT_MESSAGE = "If you use this software in your work, please cite it using the following metadata"
60
+ DEFAULT_MESSAGE = 'If you use this software in your work, please cite ' \
61
+ 'it using the following metadata'
43
62
 
44
63
  # :call-seq:
45
64
  # new(title) -> Model
65
+ # new(title) { |model| block } -> Model
46
66
  #
47
67
  # Initialize a new Model with the supplied title.
48
68
  def initialize(param)
49
- @authors = []
50
- @contact = []
51
- @keywords = []
52
-
53
- if Hash === param
54
- build_model(param)
69
+ if param.is_a?(Hash)
70
+ @fields = build_model(param)
71
+ @fields.default = ''
55
72
  else
56
73
  @fields = Hash.new('')
57
74
  @fields['cff-version'] = DEFAULT_SPEC_VERSION
58
75
  @fields['message'] = DEFAULT_MESSAGE
59
76
  @fields['title'] = param
60
77
  end
78
+
79
+ %w[authors contact identifiers keywords references].each do |field|
80
+ @fields[field] = [] if @fields[field].empty?
81
+ end
82
+
83
+ yield self if block_given?
84
+ end
85
+
86
+ # :call-seq:
87
+ # read(String) -> Model
88
+ #
89
+ # Read a CFF Model from a String and parse it for subsequent manipulation.
90
+ def self.read(model)
91
+ new(YAML.safe_load(model, permitted_classes: [Date, Time]))
92
+ end
93
+
94
+ # :call-seq:
95
+ # open(String) -> Model
96
+ # open(String) { |cff| block } -> Model
97
+ #
98
+ # With no associated block, Model.open is a synonym for ::read. If the
99
+ # optional code block is given, it will be passed the parsed model as an
100
+ # argument and the Model will be returned when the block terminates.
101
+ def self.open(model)
102
+ cff = Model.read(model)
103
+
104
+ yield cff if block_given?
105
+
106
+ cff
107
+ end
108
+
109
+ # :call-seq:
110
+ # date_released = date
111
+ #
112
+ # Set the `date-released` field. If a non-Date object is passed in it will
113
+ # be parsed into a Date.
114
+ def date_released=(date)
115
+ date = Date.parse(date) unless date.is_a?(Date)
116
+
117
+ @fields['date-released'] = date
118
+ end
119
+
120
+ def to_yaml # :nodoc:
121
+ YAML.dump fields, line_width: -1, indentation: 2
122
+ end
123
+
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
+ private
153
+
154
+ def fields
155
+ %w[authors contact identifiers references].each do |field|
156
+ normalize_modelpart_array!(@fields[field])
157
+ end
158
+
159
+ fields_to_hash(@fields)
61
160
  end
62
161
 
162
+ def build_model(fields) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
163
+ build_actor_collection!(fields['authors'] || [])
164
+ build_actor_collection!(fields['contact'] || [])
165
+ (fields['identifiers'] || []).map! do |i|
166
+ Identifier.new(i)
167
+ end
168
+ (fields['references'] || []).map! do |r|
169
+ Reference.new(r)
170
+ end
171
+ fields['preferred-citation'] &&=
172
+ Reference.new(fields['preferred-citation'])
173
+
174
+ # Only attempt an update of the `cff-version` field if it is present.
175
+ fields['cff-version'] &&= update_cff_version(fields['cff-version'])
176
+
177
+ fields
178
+ end
179
+
180
+ public
181
+
182
+ # Some documentation of "hidden" methods is provided here, out of the
183
+ # way of the main class code.
184
+
185
+ ##
186
+ # :method: authors
63
187
  # :call-seq:
64
188
  # authors -> Array
65
189
  #
@@ -71,10 +195,18 @@ module CFF
71
195
  # ```
72
196
  #
73
197
  # Authors can be a Person or Entity.
74
- def authors
75
- @authors
76
- end
77
198
 
199
+ ##
200
+ # :method: authors=
201
+ # :call-seq:
202
+ # authors = array_of_authors -> Array
203
+ #
204
+ # Replace the list of authors for this citation.
205
+ #
206
+ # Authors can be a Person or Entity.
207
+
208
+ ##
209
+ # :method: contact
78
210
  # :call-seq:
79
211
  # contact -> Array
80
212
  #
@@ -86,23 +218,37 @@ module CFF
86
218
  # ```
87
219
  #
88
220
  # Contacts can be a Person or Entity.
89
- def contact
90
- @contact
91
- end
92
221
 
222
+ ##
223
+ # :method: contact=
93
224
  # :call-seq:
94
- # date_released = date
225
+ # contact = array_of_contacts -> Array
95
226
  #
96
- # Set the `date-released` field. If a non-Date object is passed in it will
97
- # be parsed into a Date.
98
- def date_released=(date)
99
- unless Date === date
100
- date = Date.parse(date)
101
- end
227
+ # Replace the list of contacts for this citation.
228
+ #
229
+ # Contacts can be a Person or Entity.
102
230
 
103
- @fields['date-released'] = date
104
- end
231
+ ##
232
+ # :method: identifiers
233
+ # :call-seq:
234
+ # identifiers -> Array
235
+ #
236
+ # Return the list of identifiers for this citation. To add a identifier to
237
+ # the list, use:
238
+ #
239
+ # ```
240
+ # model.identifiers << identifier
241
+ # ```
105
242
 
243
+ ##
244
+ # :method: identifiers=
245
+ # :call-seq:
246
+ # identifiers = array_of_identifiers -> Array
247
+ #
248
+ # Replace the list of identifiers for this citation.
249
+
250
+ ##
251
+ # :method: keywords
106
252
  # :call-seq:
107
253
  # keywords -> Array
108
254
  #
@@ -113,60 +259,48 @@ module CFF
113
259
  # model.keywords << keyword
114
260
  # ```
115
261
  #
116
- # Keywords will be converted to Strings on output to a CFF file.
117
- def keywords
118
- @keywords
119
- end
262
+ # Keywords will be converted to Strings on output.
120
263
 
264
+ ##
265
+ # :method: keywords=
121
266
  # :call-seq:
122
- # version = version
267
+ # keywords = array_of_keywords -> Array
123
268
  #
124
- # Set the `version` field.
125
- def version=(version)
126
- @fields['version'] = version.to_s
127
- end
128
-
129
- def to_yaml # :nodoc:
130
- fields = @fields.dup
131
- fields['authors'] = array_field_to_yaml(@authors) unless @authors.empty?
132
- fields['contact'] = array_field_to_yaml(@contact) unless @contact.empty?
133
- fields['keywords'] = @keywords.map { |k| k.to_s } unless @keywords.empty?
134
-
135
- YAML.dump fields, :line_width => -1, :indentation => 2
136
- end
137
-
138
- def method_missing(name, *args) # :nodoc:
139
- n = method_to_field(name.id2name)
140
- super unless ALLOWED_FIELDS.include?(n.chomp('='))
141
-
142
- if n.end_with?('=')
143
- @fields[n.chomp('=')] = args[0] || ''
144
- else
145
- @fields[n]
146
- end
147
- end
148
-
149
- private
150
-
151
- def build_model(fields)
152
- build_entity_collection(@authors, fields['authors'])
153
- build_entity_collection(@contact, fields['contact'])
154
- @keywords = fields['keywords']
269
+ # Replace the list of keywords for this citation.
270
+ #
271
+ # Keywords will be converted to Strings on output.
155
272
 
156
- @fields = delete_from_hash(fields, 'authors', 'contact', 'keywords')
157
- end
273
+ ##
274
+ # :method: preferred_citation
275
+ # :call-seq:
276
+ # preferred_citation -> Reference
277
+ #
278
+ # Return the preferred citation for this citation.
158
279
 
159
- def build_entity_collection(field, source)
160
- source.each do |s|
161
- field << (s.has_key?('given-names') ? Person.new(s) : Entity.new(s))
162
- end
163
- end
280
+ ##
281
+ # :method: preferred_citation=
282
+ # :call-seq:
283
+ # preferred_citation = Reference
284
+ #
285
+ # Replace the preferred citation for this citation.
164
286
 
165
- def array_field_to_yaml(field)
166
- field.reject do |f|
167
- !f.respond_to?(:fields)
168
- end.map { |f| f.fields }
169
- end
287
+ ##
288
+ # :method: references
289
+ # :call-seq:
290
+ # references -> Array
291
+ #
292
+ # Return the list of references for this citation. To add a reference to the
293
+ # list, use:
294
+ #
295
+ # ```
296
+ # model.references << reference
297
+ # ```
170
298
 
299
+ ##
300
+ # :method: references=
301
+ # :call-seq:
302
+ # references = array_of_references -> Array
303
+ #
304
+ # Replace the list of references for this citation.
171
305
  end
172
306
  end