Skip to content

Commit 08aa822

Browse files
committed
Move the citation code out to a mixin: Citable.
This then auto-generates the `to_{format}` methods for each formatter that has been registered.
1 parent 9dcb161 commit 08aa822

4 files changed

Lines changed: 128 additions & 52 deletions

File tree

lib/cff/citable.rb

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright (c) 2018-2022 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+
require_relative 'formatters'
18+
19+
##
20+
module CFF
21+
# Methods to enable turning a CFF model or file into a citation.
22+
#
23+
# The core functionality is in the `citation` method. In addition, each
24+
# available output format has a `to_{format}` method generated for it as
25+
# well, e.g. `to_bibtex` or `to_apalike`. These methods take a single
26+
# parameter, `preferred_citation:`, which defaults to `true` as in the
27+
# `citation` method.
28+
module Citable
29+
# :call-seq:
30+
# citation(format, preferred_citation: true) -> String
31+
#
32+
# Output this Index in the specified format. Setting
33+
# `preferred_citation: true` will honour the `preferred_citation` field in
34+
# the index if one is present (default).
35+
#
36+
# `format` can be supplied as a String or a Symbol.
37+
#
38+
# Formats that are built-in to Ruby CFF are:
39+
#
40+
# * APAlike (e.g. `:apalike`, `'apalike'` or `'APAlike'`)
41+
# * BibTeX (e.g. `:bibtex`, `'bibtex'` or `'BibTeX'`)
42+
#
43+
# *Note:* This method assumes that this Index is valid when called.
44+
def citation(format, preferred_citation: true)
45+
formatter = Formatters.formatter_for(format)
46+
return '' if formatter.nil?
47+
48+
formatter.format(model: self, preferred_citation: preferred_citation)
49+
end
50+
51+
def self.add_to_format_method(format) # :nodoc:
52+
method = "to_#{format}"
53+
return if method_defined?(method)
54+
55+
class_eval(
56+
# def to_bibtex(preferred_citation: true)
57+
# citation(:bibtex, preferred_citation: preferred_citation)
58+
# end
59+
<<-END_TO_FORMAT, __FILE__, __LINE__ + 1
60+
def #{method}(preferred_citation: true)
61+
citation(:#{format}, preferred_citation: preferred_citation)
62+
end
63+
END_TO_FORMAT
64+
)
65+
end
66+
67+
# Add the formatters we know about already upfront.
68+
Formatters.formatters.each do |format|
69+
add_to_format_method(format)
70+
end
71+
end
72+
end

lib/cff/formatters.rb

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,36 @@ module CFF
2020
module Formatters
2121
@formatters = {}
2222

23+
# :call-seq:
24+
# formatters -> Array
25+
#
26+
# Return the list of formatters that are available.
27+
def self.formatters
28+
@formatters.keys
29+
end
30+
2331
# :call-seq:
2432
# register_formatter(class)
2533
#
2634
# Register a citation formatter. To be registered as a formatter, a
27-
# class should at least provide:
35+
# class should at least provide the following class methods:
2836
#
29-
# * the constant `CITATION_FORMAT`, which should be a minimal descriptive
30-
# name for the format, e.g. `'BibTeX'`; and
31-
# * the method `format`, which takes the to model to be formatted.
37+
# * `format`, which takes the model to be formatted
38+
# as a named parameter, and the option to cite a CFF file's
39+
# `preferred-citation`:
40+
# ```ruby
41+
# def self.format(model:, preferred_citation: true); end
42+
# ```
43+
# * `label`, which returns a short name for the formatter, e.g.
44+
# `'BibTeX'`. If your formatter class subclasses `CFF::Formatter`,
45+
# then `label` is provided for you.
3246
def self.register_formatter(clazz)
3347
return unless clazz.singleton_methods.include?(:format)
3448
return if @formatters.has_value?(clazz)
3549

36-
@formatters[clazz.label.downcase.to_sym] = clazz
50+
format = clazz.label.downcase.to_sym
51+
@formatters[format] = clazz
52+
Citable.add_to_format_method(format) if defined?(Citable)
3753
end
3854

3955
def self.formatter_for(format) # :nodoc:

lib/cff/index.rb

Lines changed: 2 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
require_relative 'reference'
2424
require_relative 'schema'
2525
require_relative 'validatable'
26-
require_relative 'formatters'
26+
require_relative 'citable'
2727

2828
require 'yaml'
2929

@@ -56,6 +56,7 @@ module CFF
5656
# * `url`
5757
# * `version`
5858
class Index < ModelPart
59+
include Citable
5960
include Licensable
6061
include Validatable
6162

@@ -117,28 +118,6 @@ def self.open(index)
117118
cff
118119
end
119120

120-
# :call-seq:
121-
# citation(format, preferred_citation: true) -> String
122-
#
123-
# Output this Index in the specified format. Setting
124-
# `preferred_citation: true` will honour the `preferred_citation` field in
125-
# the index if one is present (default).
126-
#
127-
# `format` can be supplied as a String or a Symbol.
128-
#
129-
# Formats that are built-in to Ruby CFF are:
130-
#
131-
# * APAlike (e.g. `:apalike`, `'apalike'` or `'APAlike'`)
132-
# * BibTeX (e.g. `:bibtex`, `'bibtex'` or `'BibTeX'`)
133-
#
134-
# *Note:* This method assumes that this Index is valid when called.
135-
def citation(format, preferred_citation: true)
136-
formatter = Formatters.formatter_for(format)
137-
return '' if formatter.nil?
138-
139-
formatter.format(model: self, preferred_citation: preferred_citation)
140-
end
141-
142121
# :call-seq:
143122
# type = type
144123
#
@@ -154,30 +133,6 @@ def to_yaml # :nodoc:
154133
YAML.dump fields, line_width: -1, indentation: 2
155134
end
156135

157-
# :call-seq:
158-
# to_apalike(preferred_citation: true) -> String
159-
#
160-
# Output this Index in an APA-like format. Setting
161-
# `preferred_citation: true` will honour the `preferred_citation` field in
162-
# the index if one is present (default).
163-
#
164-
# *Note:* This method assumes that this Index is valid when called.
165-
def to_apalike(preferred_citation: true)
166-
citation(:apalike, preferred_citation: preferred_citation)
167-
end
168-
169-
# :call-seq:
170-
# to_bibtex(preferred_citation: true) -> String
171-
#
172-
# Output this Index in BibTeX format. Setting
173-
# `preferred_citation: true` will honour the `preferred_citation` field in
174-
# the index if one is present (default).
175-
#
176-
# *Note:* This method assumes that this Index is valid when called.
177-
def to_bibtex(preferred_citation: true)
178-
citation(:bibtex, preferred_citation: preferred_citation)
179-
end
180-
181136
private
182137

183138
def fields

test/cff_citable_test.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright (c) 2018-2022 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+
require_relative 'test_helper'
18+
19+
require 'cff/citable'
20+
21+
class CFFCitableTest < Minitest::Test
22+
def test_to_methods_present
23+
methods = CFF::Citable.instance_methods
24+
25+
CFF::Formatters.formatters.each do |format|
26+
# Depending on the order that tests are run, we may have an extra
27+
# formatter hanging around.
28+
next if format == :testformatterlabel
29+
30+
assert_includes(methods, "to_#{format}".to_sym)
31+
end
32+
end
33+
end

0 commit comments

Comments
 (0)