paru 0.2.1 → 0.2.2
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/bin/do-pandoc.rb +64 -0
- data/bin/pandoc2yaml.rb +94 -0
- data/lib/paru.rb +26 -10
- data/lib/paru/error.rb +7 -0
- data/lib/paru/filter.rb +35 -0
- data/lib/paru/filter/alignment.rb +2 -0
- data/lib/paru/filter/ast_manipulation.rb +2 -0
- data/lib/paru/filter/attr.rb +2 -0
- data/lib/paru/filter/block.rb +2 -0
- data/lib/paru/filter/block_quote.rb +3 -1
- data/lib/paru/filter/bullet_list.rb +3 -1
- data/lib/paru/filter/citation.rb +2 -0
- data/lib/paru/filter/cite.rb +3 -1
- data/lib/paru/filter/code.rb +3 -1
- data/lib/paru/filter/code_block.rb +3 -1
- data/lib/paru/filter/definition_list.rb +3 -1
- data/lib/paru/filter/definition_list_item.rb +2 -0
- data/lib/paru/filter/div.rb +3 -1
- data/lib/paru/filter/document.rb +2 -0
- data/lib/paru/filter/emph.rb +3 -1
- data/lib/paru/filter/empty_block.rb +2 -0
- data/lib/paru/filter/empty_inline.rb +2 -0
- data/lib/paru/filter/header.rb +3 -1
- data/lib/paru/filter/horizontal_rule.rb +3 -1
- data/lib/paru/filter/image.rb +3 -1
- data/lib/paru/filter/inline.rb +2 -0
- data/lib/paru/filter/line_block.rb +3 -1
- data/lib/paru/filter/line_break.rb +3 -1
- data/lib/paru/filter/link.rb +3 -1
- data/lib/paru/filter/list.rb +2 -0
- data/lib/paru/filter/list_attributes.rb +2 -0
- data/lib/paru/filter/markdown.rb +2 -0
- data/lib/paru/filter/math.rb +3 -1
- data/lib/paru/filter/meta.rb +3 -1
- data/lib/paru/filter/meta_blocks.rb +3 -1
- data/lib/paru/filter/meta_bool.rb +3 -1
- data/lib/paru/filter/meta_inlines.rb +3 -1
- data/lib/paru/filter/meta_list.rb +3 -1
- data/lib/paru/filter/meta_map.rb +3 -1
- data/lib/paru/filter/meta_string.rb +3 -1
- data/lib/paru/filter/meta_value.rb +4 -2
- data/lib/paru/filter/node.rb +2 -0
- data/lib/paru/filter/note.rb +3 -1
- data/lib/paru/filter/null.rb +3 -1
- data/lib/paru/filter/ordered_list.rb +3 -1
- data/lib/paru/filter/para.rb +3 -1
- data/lib/paru/filter/plain.rb +3 -1
- data/lib/paru/filter/quoted.rb +3 -1
- data/lib/paru/filter/raw_block.rb +3 -1
- data/lib/paru/filter/raw_inline.rb +3 -1
- data/lib/paru/filter/small_caps.rb +3 -1
- data/lib/paru/filter/soft_break.rb +3 -1
- data/lib/paru/filter/space.rb +3 -1
- data/lib/paru/filter/span.rb +3 -1
- data/lib/paru/filter/str.rb +3 -1
- data/lib/paru/filter/strikeout.rb +3 -1
- data/lib/paru/filter/strong.rb +3 -1
- data/lib/paru/filter/subscript.rb +3 -1
- data/lib/paru/filter/superscript.rb +3 -1
- data/lib/paru/filter/table.rb +3 -1
- data/lib/paru/filter/table_row.rb +2 -0
- data/lib/paru/filter/target.rb +2 -0
- data/lib/paru/filter/version.rb +2 -0
- data/lib/paru/pandoc.rb +2 -0
- data/lib/paru/pandoc_options.yaml +2 -0
- data/lib/paru/selector.rb +2 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f9744e21c17335e591270f4332e03a7fcbd7230
|
4
|
+
data.tar.gz: 250f63c5dd1858e568b57613824c27676d0b4220
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 73498311b6eef5f2edea155981816aa529f84b8adb1097d4625f73e2afc0dd1d2808acd5bc14e9a010903a1304c297136ee51d2959634344f38ef1a3ed61960f
|
7
|
+
data.tar.gz: 012ba505e5af505770b98647e8e2585a3a36330f7f48aa3072d03debdbfee7c8d1712869cdd5465048ef38554298d67e0ea366399cf470cffcd7638b5a497faa
|
data/bin/do-pandoc.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "yaml"
|
3
|
+
require 'optparse'
|
4
|
+
require "paru/pandoc"
|
5
|
+
require_relative "./pandoc2yaml.rb"
|
6
|
+
|
7
|
+
include Pandoc2Yaml
|
8
|
+
|
9
|
+
parser = OptionParser.new do |opts|
|
10
|
+
opts.banner = "do-pandoc.rb runs pandoc on an input file using the pandoc configuration specified in that input file."
|
11
|
+
opts.banner << "\n\nUsage: do-pandoc.rb some-pandoc-markdownfile.md"
|
12
|
+
opts.separator ""
|
13
|
+
opts.separator "Common options"
|
14
|
+
|
15
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
16
|
+
puts opts
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
|
20
|
+
opts.on("-v", "--version", "Show version") do
|
21
|
+
puts "do-pandoc.rb is part of paru version 0.2.2"
|
22
|
+
exit
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
parser.parse! ARGV
|
27
|
+
|
28
|
+
input_document = ARGV.pop
|
29
|
+
|
30
|
+
if ARGV.size != 0 then
|
31
|
+
warn "Expecting exactly one argument: the pandoc file to convert"
|
32
|
+
puts ""
|
33
|
+
puts parser
|
34
|
+
exit
|
35
|
+
end
|
36
|
+
|
37
|
+
document = File.expand_path input_document
|
38
|
+
if not File.exist? document
|
39
|
+
warn "Cannot find file: #{input_document}"
|
40
|
+
exit
|
41
|
+
end
|
42
|
+
|
43
|
+
if !File.readable? document
|
44
|
+
warn "Cannot read file: #{input_document}"
|
45
|
+
exit
|
46
|
+
end
|
47
|
+
metadata = YAML.load Pandoc2Yaml.extract_metadata(document)
|
48
|
+
|
49
|
+
if metadata.has_key? "pandoc" then
|
50
|
+
begin
|
51
|
+
pandoc = Paru::Pandoc.new
|
52
|
+
to_stdout = true
|
53
|
+
metadata["pandoc"].each do |option, value|
|
54
|
+
pandoc.send option, value
|
55
|
+
to_stdout = false if option == "output"
|
56
|
+
end
|
57
|
+
output = pandoc << File.read(document)
|
58
|
+
puts output if to_stdout
|
59
|
+
rescue Exception => e
|
60
|
+
warn "Something went wrong while using pandoc:\n\n#{e.message}"
|
61
|
+
end
|
62
|
+
else
|
63
|
+
warn "Unsure what to do: no pandoc options in #{input}"
|
64
|
+
end
|
data/bin/pandoc2yaml.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
##
|
3
|
+
# pandoc2yaml.rb extracts the metadata from a pandoc markdown file and prints
|
4
|
+
# that metadata out again as a pandoc markdown file with nothing in it but that
|
5
|
+
# metadata
|
6
|
+
#
|
7
|
+
# Usage:
|
8
|
+
#
|
9
|
+
# pandoc2yaml.rb input_file
|
10
|
+
#
|
11
|
+
##
|
12
|
+
module Pandoc2Yaml
|
13
|
+
require "json"
|
14
|
+
require "paru/pandoc"
|
15
|
+
|
16
|
+
# Paru converters:
|
17
|
+
# Note. When converting metadata back to the pandoc markdown format, you have
|
18
|
+
# to use the option "standalone", otherwise the metadata is skipped
|
19
|
+
PANDOC_2_JSON = Paru::Pandoc.new {from "markdown"; to "json"}
|
20
|
+
JSON_2_PANDOC = Paru::Pandoc.new {from "json"; to "markdown"; standalone}
|
21
|
+
|
22
|
+
# When converting a pandoc document to JSON, or vice versa, the JSON object
|
23
|
+
# has the following three properties:
|
24
|
+
VERSION = "pandoc-api-version"
|
25
|
+
META = "meta"
|
26
|
+
BLOCKS = "blocks"
|
27
|
+
|
28
|
+
def extract_metadata input_document
|
29
|
+
json = JSON.parse(PANDOC_2_JSON << File.read(input_document))
|
30
|
+
yaml = ""
|
31
|
+
|
32
|
+
version, metadata = json.values_at(VERSION, META)
|
33
|
+
|
34
|
+
if not metadata.empty? then
|
35
|
+
metadata_document = {
|
36
|
+
VERSION => version,
|
37
|
+
META => metadata,
|
38
|
+
BLOCKS => []
|
39
|
+
}
|
40
|
+
|
41
|
+
yaml = JSON_2_PANDOC << JSON.generate(metadata_document)
|
42
|
+
end
|
43
|
+
|
44
|
+
yaml
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
if __FILE__ == $0
|
49
|
+
include Pandoc2Yaml
|
50
|
+
require 'optparse'
|
51
|
+
|
52
|
+
parser = OptionParser.new do |opts|
|
53
|
+
opts.banner = "pandoc2yaml.rb mines a pandoc markdown file for its YAML metadata"
|
54
|
+
opts.banner << "\n\nUsage: pandoc2yaml.rb some-pandoc-markdownfile.md"
|
55
|
+
opts.separator ""
|
56
|
+
opts.separator "Common options"
|
57
|
+
|
58
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
59
|
+
puts opts
|
60
|
+
exit
|
61
|
+
end
|
62
|
+
|
63
|
+
opts.on("-v", "--version", "Show version") do
|
64
|
+
puts "pandoc2yaml.rb is part of paru version 0.2.2"
|
65
|
+
exit
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
parser.parse! ARGV
|
70
|
+
|
71
|
+
input_document = ARGV.pop
|
72
|
+
|
73
|
+
if ARGV.size != 0 then
|
74
|
+
warn "Expecting exactly one argument: the pandoc file to strip for metadata"
|
75
|
+
puts ""
|
76
|
+
puts parser
|
77
|
+
exit
|
78
|
+
end
|
79
|
+
|
80
|
+
document = File.expand_path input_document
|
81
|
+
if not File.exist? document
|
82
|
+
warn "Cannot find file: #{input_document}"
|
83
|
+
exit
|
84
|
+
end
|
85
|
+
|
86
|
+
if !File.readable? document
|
87
|
+
warn "Cannot read file: #{input_document}"
|
88
|
+
exit
|
89
|
+
end
|
90
|
+
|
91
|
+
output_metadata = Pandoc2Yaml.extract_metadata document
|
92
|
+
|
93
|
+
puts output_metadata
|
94
|
+
end
|
data/lib/paru.rb
CHANGED
@@ -1,18 +1,34 @@
|
|
1
|
+
# = This is the API documentation for paru generated by RDoc.
|
2
|
+
#
|
3
|
+
# Paru is a simple Ruby wrapper around {pandoc}[http://www.pandoc.org], the
|
4
|
+
# great multi-format document converter. Paru supports automating pandoc by
|
5
|
+
# writing Ruby programs and using pandoc in your Ruby programs. Paru also
|
6
|
+
# supports writing pandoc filters in Ruby. In {paru's user
|
7
|
+
# manual}[https://heerdebeer.org/Software/markdown/paru/] the use of paru is
|
8
|
+
# explained in detail, from explaining how to install and use paru, creating
|
9
|
+
# and using filters, to putting it all together in a real-world use case like
|
10
|
+
# generating that manual page.
|
11
|
+
#
|
12
|
+
# This document, however, describes paru's API.
|
13
|
+
#
|
14
|
+
# == Licence
|
15
|
+
#
|
1
16
|
# Copyright 2015, 2016 Huub de Beer <Huub@heerdebeer.org>
|
2
17
|
#
|
3
18
|
# This file is part of Paru
|
4
19
|
#
|
5
|
-
# Paru is free software: you can redistribute it and/or modify
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
20
|
+
# Paru is free software: you can redistribute it and/or modify it under the
|
21
|
+
# terms of the GNU General Public License as published by the Free Software
|
22
|
+
# Foundation, either version 3 of the License, or (at your option) any later
|
23
|
+
# version.
|
9
24
|
#
|
10
|
-
# Paru is distributed in the hope that it will be useful,
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
25
|
+
# Paru is distributed in the hope that it will be useful, but WITHOUT ANY
|
26
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
27
|
+
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
28
|
+
# details.
|
14
29
|
#
|
15
|
-
# You should have received a copy of the GNU General Public License
|
16
|
-
#
|
30
|
+
# You should have received a copy of the GNU General Public License along with
|
31
|
+
# Paru. If not, see <http://www.gnu.org/licenses/>.
|
32
|
+
|
17
33
|
require "paru/pandoc"
|
18
34
|
require "paru/filter"
|
data/lib/paru/error.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
#--
|
1
2
|
# Copyright 2015, 2016 Huub de Beer <Huub@heerdebeer.org>
|
2
3
|
#
|
3
4
|
# This file is part of Paru
|
@@ -14,7 +15,13 @@
|
|
14
15
|
#
|
15
16
|
# You should have received a copy of the GNU General Public License
|
16
17
|
# along with Paru. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#++
|
19
|
+
|
17
20
|
module Paru
|
21
|
+
|
22
|
+
# An error class to use as a basis for paru specific errors. It has not yet
|
23
|
+
# been used in that capacity much, however.
|
24
|
+
|
18
25
|
class Error < RuntimeError
|
19
26
|
end
|
20
27
|
end
|
data/lib/paru/filter.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
#--
|
1
2
|
# Copyright 2015, 2016 Huub de Beer <Huub@heerdebeer.org>
|
2
3
|
#
|
3
4
|
# This file is part of Paru
|
@@ -14,11 +15,19 @@
|
|
14
15
|
#
|
15
16
|
# You should have received a copy of the GNU General Public License
|
16
17
|
# along with Paru. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#++
|
19
|
+
|
17
20
|
module Paru
|
18
21
|
|
19
22
|
require_relative "./selector"
|
20
23
|
require_relative "filter/document"
|
21
24
|
|
25
|
+
# Paru filter is a wrapper around pandoc's JSON api, which is based on
|
26
|
+
# {pandoc-types}[https://hackage.haskell.org/package/pandoc-types-1.17.0.4/docs/Text-Pandoc-Definition.html].
|
27
|
+
# Pandoc treats block elements and inline elements differently.
|
28
|
+
#
|
29
|
+
# Pandoc's block elements are:
|
30
|
+
|
22
31
|
PANDOC_BLOCK = [
|
23
32
|
"Plain",
|
24
33
|
"Para",
|
@@ -35,6 +44,9 @@ module Paru
|
|
35
44
|
"Div",
|
36
45
|
"Null"
|
37
46
|
]
|
47
|
+
|
48
|
+
# Pandoc's inline elements are
|
49
|
+
|
38
50
|
PANDOC_INLINE = [
|
39
51
|
"Str",
|
40
52
|
"Emph",
|
@@ -55,18 +67,31 @@ module Paru
|
|
55
67
|
"Note",
|
56
68
|
"Span"
|
57
69
|
]
|
70
|
+
|
71
|
+
# All of pandoc's type together:
|
72
|
+
|
58
73
|
PANDOC_TYPES = PANDOC_BLOCK + PANDOC_INLINE
|
59
74
|
|
75
|
+
|
76
|
+
# Filter is used to write your own pandoc filter in Ruby. A Filter is
|
77
|
+
# almost always created and immediately executed via the +run+ method as
|
78
|
+
|
60
79
|
class Filter
|
61
80
|
|
62
81
|
def self.run &block
|
63
82
|
Filter.new().filter(&block)
|
64
83
|
end
|
65
84
|
|
85
|
+
|
86
|
+
# Create a new Document node from JSON formatted pandoc document structure
|
87
|
+
# on STDIN
|
88
|
+
|
66
89
|
def document
|
67
90
|
PandocFilter::Document.from_JSON $stdin.read
|
68
91
|
end
|
69
92
|
|
93
|
+
# Create a filter using +block+.
|
94
|
+
|
70
95
|
def filter &block
|
71
96
|
@selectors = Hash.new
|
72
97
|
@filtered_nodes = []
|
@@ -80,15 +105,25 @@ module Paru
|
|
80
105
|
puts @doc.to_JSON
|
81
106
|
end
|
82
107
|
|
108
|
+
|
109
|
+
# +current_node+ points to the node that is *now* being processed while
|
110
|
+
# running this filter.
|
111
|
+
|
83
112
|
def current_node
|
84
113
|
@filtered_nodes.last
|
85
114
|
end
|
86
115
|
|
116
|
+
# Specify what nodes to filter with a +selector+. If the +current_node+
|
117
|
+
# matches that selector, it is passed to the block to this +with+ method.
|
118
|
+
|
87
119
|
def with selector
|
88
120
|
@selectors[selector] = Selector.new selector unless @selectors.has_key? selector
|
89
121
|
yield current_node if @selectors[selector].matches? current_node, @filtered_nodes
|
90
122
|
end
|
91
123
|
|
124
|
+
# While running a filter you can access the document's metadata through
|
125
|
+
# the +metadata+ method.
|
126
|
+
|
92
127
|
def metadata
|
93
128
|
@doc.meta
|
94
129
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
#--
|
1
2
|
# Copyright 2015, 2016 Huub de Beer <Huub@heerdebeer.org>
|
2
3
|
#
|
3
4
|
# This file is part of Paru
|
@@ -14,6 +15,7 @@
|
|
14
15
|
#
|
15
16
|
# You should have received a copy of the GNU General Public License
|
16
17
|
# along with Paru. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#++
|
17
19
|
module Paru
|
18
20
|
module PandocFilter
|
19
21
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
#--
|
1
2
|
# Copyright 2015, 2016 Huub de Beer <Huub@heerdebeer.org>
|
2
3
|
#
|
3
4
|
# This file is part of Paru
|
@@ -14,6 +15,7 @@
|
|
14
15
|
#
|
15
16
|
# You should have received a copy of the GNU General Public License
|
16
17
|
# along with Paru. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#++
|
17
19
|
module Paru
|
18
20
|
module PandocFilter
|
19
21
|
module ASTManipulation
|
data/lib/paru/filter/attr.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
#--
|
1
2
|
# Copyright 2015, 2016 Huub de Beer <Huub@heerdebeer.org>
|
2
3
|
#
|
3
4
|
# This file is part of Paru
|
@@ -14,6 +15,7 @@
|
|
14
15
|
#
|
15
16
|
# You should have received a copy of the GNU General Public License
|
16
17
|
# along with Paru. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#++
|
17
19
|
module Paru
|
18
20
|
module PandocFilter
|
19
21
|
class Attr
|
data/lib/paru/filter/block.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
#--
|
1
2
|
# Copyright 2015, 2016 Huub de Beer <Huub@heerdebeer.org>
|
2
3
|
#
|
3
4
|
# This file is part of Paru
|
@@ -14,6 +15,7 @@
|
|
14
15
|
#
|
15
16
|
# You should have received a copy of the GNU General Public License
|
16
17
|
# along with Paru. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#++
|
17
19
|
module Paru
|
18
20
|
module PandocFilter
|
19
21
|
require_relative "./node"
|
@@ -1,3 +1,4 @@
|
|
1
|
+
#--
|
1
2
|
# Copyright 2015, 2016 Huub de Beer <Huub@heerdebeer.org>
|
2
3
|
#
|
3
4
|
# This file is part of Paru
|
@@ -14,11 +15,12 @@
|
|
14
15
|
#
|
15
16
|
# You should have received a copy of the GNU General Public License
|
16
17
|
# along with Paru. If not, see <http://www.gnu.org/licenses/>.
|
17
|
-
|
18
|
+
#++
|
18
19
|
module Paru
|
19
20
|
module PandocFilter
|
20
21
|
require_relative "./block"
|
21
22
|
|
23
|
+
# BlockQuote [Block]
|
22
24
|
class BlockQuote < Block
|
23
25
|
def has_block?
|
24
26
|
true
|
@@ -1,3 +1,4 @@
|
|
1
|
+
#--
|
1
2
|
# Copyright 2015, 2016 Huub de Beer <Huub@heerdebeer.org>
|
2
3
|
#
|
3
4
|
# This file is part of Paru
|
@@ -14,11 +15,12 @@
|
|
14
15
|
#
|
15
16
|
# You should have received a copy of the GNU General Public License
|
16
17
|
# along with Paru. If not, see <http://www.gnu.org/licenses/>.
|
17
|
-
|
18
|
+
#++
|
18
19
|
module Paru
|
19
20
|
module PandocFilter
|
20
21
|
require_relative "./list"
|
21
22
|
|
23
|
+
# BulletList [[Block]]
|
22
24
|
class BulletList < List
|
23
25
|
end
|
24
26
|
end
|
data/lib/paru/filter/citation.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
#--
|
1
2
|
# Copyright 2015, 2016 Huub de Beer <Huub@heerdebeer.org>
|
2
3
|
#
|
3
4
|
# This file is part of Paru
|
@@ -14,6 +15,7 @@
|
|
14
15
|
#
|
15
16
|
# You should have received a copy of the GNU General Public License
|
16
17
|
# along with Paru. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#--
|
17
19
|
module Paru
|
18
20
|
module PandocFilter
|
19
21
|
require_relative "./inline"
|
data/lib/paru/filter/cite.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
#--
|
1
2
|
# Copyright 2015, 2016 Huub de Beer <Huub@heerdebeer.org>
|
2
3
|
#
|
3
4
|
# This file is part of Paru
|
@@ -14,11 +15,12 @@
|
|
14
15
|
#
|
15
16
|
# You should have received a copy of the GNU General Public License
|
16
17
|
# along with Paru. If not, see <http://www.gnu.org/licenses/>.
|
17
|
-
|
18
|
+
#++
|
18
19
|
module Paru
|
19
20
|
module PandocFilter
|
20
21
|
require_relative "./inline"
|
21
22
|
|
23
|
+
# Cite [Citation] [Inline]
|
22
24
|
class Cite < Inline
|
23
25
|
attr_accessor :citations
|
24
26
|
|