citeproc 0.0.3 → 0.0.6
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.
- data/lib/citeproc.rb +34 -0
- data/lib/citeproc/abbreviate.rb +29 -25
- data/lib/citeproc/assets.rb +101 -61
- data/lib/citeproc/bibliography.rb +188 -169
- data/lib/citeproc/engine.rb +28 -47
- data/lib/citeproc/item.rb +117 -108
- data/lib/citeproc/processor.rb +102 -35
- data/lib/citeproc/utilities.rb +3 -2
- data/lib/citeproc/version.rb +1 -1
- data/spec/citeproc/assets_spec.rb +59 -55
- data/spec/citeproc/bibliography_spec.rb +67 -67
- data/spec/citeproc/engine_spec.rb +2 -6
- data/spec/citeproc/processor_spec.rb +73 -1
- metadata +10 -10
data/lib/citeproc.rb
CHANGED
@@ -1,10 +1,26 @@
|
|
1
1
|
|
2
2
|
require 'multi_json'
|
3
3
|
require 'forwardable'
|
4
|
+
require 'observer'
|
5
|
+
require 'uri'
|
4
6
|
|
7
|
+
if ENV['DEBUG']
|
8
|
+
require 'ruby-debug'
|
9
|
+
Debugger.start
|
10
|
+
end
|
5
11
|
|
6
12
|
require 'citeproc/version'
|
7
13
|
|
14
|
+
#
|
15
|
+
# CiteProc processes bibliographic data and formats it according to a style
|
16
|
+
# defined in CSL (Citation Style Language).
|
17
|
+
#
|
18
|
+
module CiteProc
|
19
|
+
|
20
|
+
module Converters; end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
8
24
|
require 'citeproc/compatibility'
|
9
25
|
require 'citeproc/extensions'
|
10
26
|
|
@@ -36,3 +52,21 @@ require 'citeproc/processor'
|
|
36
52
|
require 'citeproc/utilities'
|
37
53
|
|
38
54
|
CiteProc.extend CiteProc::Utilities
|
55
|
+
|
56
|
+
|
57
|
+
CiteProc::Converters.class_eval do
|
58
|
+
|
59
|
+
# Define all converters (all classes have been loaded at this point)
|
60
|
+
CiteProc.constants.each do |name|
|
61
|
+
klass = CiteProc.const_get(name)
|
62
|
+
|
63
|
+
if klass.instance_of?(Class) && klass.respond_to?(:create)
|
64
|
+
|
65
|
+
define_method(name) do |obj|
|
66
|
+
obj.instance_of?(klass) ? obj : klass.create(obj)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
data/lib/citeproc/abbreviate.rb
CHANGED
@@ -1,30 +1,34 @@
|
|
1
1
|
require 'multi_json'
|
2
2
|
|
3
3
|
module CiteProc
|
4
|
-
|
5
|
-
|
6
|
-
attr_reader :abbreviations
|
7
|
-
attr_accessor :default_namespace
|
8
|
-
|
9
|
-
def abbreviations=(abbreviations)
|
10
|
-
@abbreviations = case abbreviations
|
11
|
-
when ::String
|
12
|
-
MultiJson.decode(abbreviations, :symbolize_keys => true)
|
13
|
-
when ::Hash
|
14
|
-
abbreviations.deep_copy
|
15
|
-
else raise ArgumentError, "failed to set abbreviations from #{abbreviations.inspect}"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
# call-seq:
|
20
|
-
# abbreviate(namespace = :default, context, word)
|
21
|
-
def abbreviate(*arguments)
|
22
|
-
raise ArgumentError, "wrong number of arguments (#{arguments.length} for 2..3)" unless (2..3).include?(arguments.length)
|
23
|
-
arguments.unshift(default_namespace || :default) if arguments.length < 3
|
24
|
-
@abbreviations.deep_fetch(*arguments)
|
25
|
-
end
|
26
|
-
|
27
|
-
alias abbrev abbreviate
|
4
|
+
module Abbreviate
|
28
5
|
|
29
|
-
|
6
|
+
attr_accessor :namespace
|
7
|
+
|
8
|
+
def abbreviations
|
9
|
+
@abbreviations ||= { :default => {} }
|
10
|
+
end
|
11
|
+
|
12
|
+
def abbreviations=(abbreviations)
|
13
|
+
@abbreviations = case abbreviations
|
14
|
+
when ::String
|
15
|
+
MultiJson.decode(abbreviations, :symbolize_keys => true)
|
16
|
+
when ::Hash
|
17
|
+
abbreviations.deep_copy
|
18
|
+
else
|
19
|
+
raise ArgumentError, "failed to set abbreviations from #{abbreviations.inspect}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# call-seq:
|
24
|
+
# abbreviate(namespace = :default, context, word)
|
25
|
+
def abbreviate(*arguments)
|
26
|
+
raise ArgumentError, "wrong number of arguments (#{arguments.length} for 2..3)" unless (2..3).include?(arguments.length)
|
27
|
+
arguments.unshift(namespace || :default) if arguments.length < 3
|
28
|
+
@abbreviations.deep_fetch(*arguments)
|
29
|
+
end
|
30
|
+
|
31
|
+
alias abbrev abbreviate
|
32
|
+
|
33
|
+
end
|
30
34
|
end
|
data/lib/citeproc/assets.rb
CHANGED
@@ -1,66 +1,106 @@
|
|
1
|
-
require 'uri'
|
2
|
-
|
3
1
|
module CiteProc
|
4
|
-
module Asset
|
5
|
-
|
6
|
-
def self.included(base)
|
7
|
-
base.extend(ClassMethods)
|
8
|
-
end
|
9
|
-
|
10
|
-
attr_accessor :asset
|
11
|
-
|
12
|
-
alias to_s asset
|
13
2
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
3
|
+
module Asset
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :asset, :location
|
10
|
+
|
11
|
+
def open?
|
12
|
+
!asset.nil?
|
13
|
+
end
|
14
|
+
|
15
|
+
def open(input)
|
16
|
+
case
|
17
|
+
when input.respond_to?(:read)
|
18
|
+
@location = nil
|
19
|
+
@asset = input.read
|
20
|
+
when input.to_s =~ /^\s*</
|
21
|
+
@location = nil
|
22
|
+
@asset = input.to_s.dup
|
23
|
+
else
|
24
|
+
case
|
25
|
+
when File.exists?(input)
|
26
|
+
@location = input
|
27
|
+
when File.exists?(self.class.extend_name(input))
|
28
|
+
@location = self.class.extend_name(input)
|
29
|
+
when File.exists?(self.class.extend_path(input))
|
30
|
+
@location = self.class.extend_path(input)
|
31
|
+
else
|
32
|
+
@location = input
|
33
|
+
end
|
34
|
+
|
35
|
+
Kernel.open(@location, 'r:UTF-8') do |io|
|
36
|
+
@asset = io.read
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
self
|
41
|
+
rescue => e
|
42
|
+
puts e.backtrace.join("\n")
|
43
|
+
raise ArgumentError, "failed to open asset #{input.inspect}: #{e.message}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def name
|
47
|
+
File.basename(location, self.class.extension).sub(Regexp.new("^#{self.class.prefix}"), '')
|
48
|
+
end
|
49
|
+
|
50
|
+
alias to_s asset
|
51
|
+
|
52
|
+
def inspect
|
53
|
+
"#<CiteProc::#{self.class.name} #{name}>"
|
54
|
+
end
|
55
|
+
|
56
|
+
module ClassMethods
|
57
|
+
|
58
|
+
attr_accessor :root, :extension, :prefix
|
59
|
+
|
60
|
+
def open(path_or_name)
|
61
|
+
new.open(path_or_name)
|
62
|
+
end
|
63
|
+
|
64
|
+
def extend_path(input)
|
65
|
+
File.join(root.to_s, extend_name(input))
|
66
|
+
end
|
67
|
+
|
68
|
+
def extend_name(input)
|
69
|
+
name = File.extname(input).empty? ? [input, extension].compact.join : input.to_s.dup
|
70
|
+
name = name.start_with?(prefix.to_s) ? name : [prefix, name].join
|
71
|
+
name
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
class Style
|
79
|
+
|
80
|
+
include Asset
|
81
|
+
|
82
|
+
@root = '/usr/local/share/citation-style-language/styles'.freeze
|
83
|
+
@extension = '.csl'.freeze
|
84
|
+
|
85
|
+
end
|
36
86
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
87
|
+
class Locale
|
88
|
+
|
89
|
+
include Asset
|
90
|
+
|
91
|
+
@root = '/usr/local/share/citation-style-language/locales'.freeze
|
92
|
+
@extension = '.xml'.freeze
|
93
|
+
@prefix = 'locales-'.freeze
|
94
|
+
|
95
|
+
|
96
|
+
def language
|
97
|
+
name.split('-')[0]
|
98
|
+
end
|
99
|
+
|
100
|
+
def region
|
101
|
+
name.split('-')[1]
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
43
105
|
|
44
|
-
def extend_name(file)
|
45
|
-
file = File.extname(file).empty? ? [file, extension].compact.join : file
|
46
|
-
file = file.start_with?(prefix.to_s) ? file : [prefix,file].join
|
47
|
-
file
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
class Style
|
54
|
-
include Asset
|
55
|
-
@root = '/usr/local/share/citation-style-language/styles'.freeze
|
56
|
-
@extension = '.csl'.freeze
|
57
|
-
end
|
58
|
-
|
59
|
-
class Locale
|
60
|
-
include Asset
|
61
|
-
@root = '/usr/local/share/citation-style-language/locales'.freeze
|
62
|
-
@extension = '.xml'.freeze
|
63
|
-
@prefix = 'locales-'
|
64
|
-
end
|
65
|
-
|
66
106
|
end
|
@@ -1,180 +1,199 @@
|
|
1
1
|
|
2
2
|
module CiteProc
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
4
|
+
# = Bibliography
|
5
|
+
#
|
6
|
+
# Typically a Bibliography is returned by the Processor. It contains a list
|
7
|
+
# of references and formatting options.
|
8
|
+
#
|
9
|
+
# A Bibliography can be created from (and exported to) CiteProc JSON
|
10
|
+
# bibliographies; these consist of a two-element list, composed of a
|
11
|
+
# JavaScript array containing certain formatting parameters, and a list of
|
12
|
+
# strings representing bibliography entries.
|
13
|
+
#
|
14
|
+
# == Bibliography Formatting Options
|
15
|
+
#
|
16
|
+
# :offset => 0
|
17
|
+
# Some citation styles apply a label (either a number or an alphanumeric
|
18
|
+
# code) to each bibliography entry, and use this label to cite
|
19
|
+
# bibliography items in the main text. In the bibliography, the labels
|
20
|
+
# may either be hung in the margin, or they may be set flush to the
|
21
|
+
# margin, with the citations indented by a uniform amount to the right.
|
22
|
+
# In the latter case, the amount of indentation needed depends on the
|
23
|
+
# maximum width of any label. The :offset option gives the maximum
|
24
|
+
# number of characters that appear in any label used in the
|
25
|
+
# bibliography. The client that controls the final rendering of the
|
26
|
+
# bibliography string should use this value to calculate and apply a
|
27
|
+
# suitable indentation length.
|
28
|
+
#
|
29
|
+
# :entry_spacing => 0
|
30
|
+
# An integer representing the spacing between entries in the
|
31
|
+
# bibliography.
|
32
|
+
#
|
33
|
+
# :line_spacing => 0
|
34
|
+
# An integer representing the spacing between the lines within each
|
35
|
+
# bibliography entry.
|
36
|
+
#
|
37
|
+
# :indent => 0
|
38
|
+
# The number of em-spaces to apply in hanging indents within the
|
39
|
+
# bibliography.
|
40
|
+
#
|
41
|
+
# :align => false
|
42
|
+
# When the second-field-align CSL option is set, this returns either
|
43
|
+
# "flush" or "margin". The calling application should align text in
|
44
|
+
# bibliography output as described in the CSL specification. Where
|
45
|
+
# second-field-align is not set, this return value is set to false.
|
46
|
+
#
|
47
|
+
class Bibliography
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
49
|
+
@defaults = {
|
50
|
+
:offset => 0,
|
51
|
+
:entry_spacing => 0,
|
52
|
+
:line_spacing => 0,
|
53
|
+
:indent => 0,
|
54
|
+
:align => false
|
55
|
+
}.freeze
|
56
|
+
|
57
|
+
|
58
|
+
# citeproc-js and csl attributes are often inconsistent or difficult
|
59
|
+
# to use as symbols/method names, so we're using different names at the
|
60
|
+
# cost of making conversion to and from the json format more difficult
|
61
|
+
|
62
|
+
@cp2rb = {
|
63
|
+
'maxoffset' => :offset,
|
64
|
+
'entryspacing' => :entry_spacing,
|
65
|
+
'linespacing' => :line_spacing,
|
66
|
+
'hangingindent' => :indent,
|
67
|
+
'second-field-align' => :align
|
68
|
+
}
|
69
|
+
|
70
|
+
@rb2cp = @cp2rb.invert.freeze
|
71
|
+
@cp2rb.freeze
|
72
|
+
|
73
|
+
class << self
|
74
|
+
|
75
|
+
attr_reader :defaults, :cp2rb, :rb2cp
|
76
|
+
|
77
|
+
# Create a new Bibliography from the passed-in string or array, or nil
|
78
|
+
# if the input cannot be parsed.
|
79
|
+
def create(input)
|
80
|
+
create!(input)
|
81
|
+
rescue
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
|
85
|
+
# Create a new Bibliography from the passed-in string or array. Raises
|
86
|
+
# ParseError if the input cannot be parsed.
|
87
|
+
def create!(input)
|
88
|
+
case
|
89
|
+
when input.is_a?(String)
|
90
|
+
create!(MultiJson.decode(input))
|
91
|
+
|
92
|
+
when input.is_a?(Array) && input.length == 2
|
93
|
+
options, references = input
|
85
94
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
95
|
+
new do |b|
|
96
|
+
b.concat(references)
|
97
|
+
b.errors.concat(options.fetch('bibliography_errors', []))
|
98
|
+
|
99
|
+
b.prefix, b.suffix = options['bibstart'], options['bibend']
|
100
|
+
|
101
|
+
(options.keys & cp2rb.keys).each do |k|
|
102
|
+
b.options[cp2rb[k]] = options[k]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
else
|
107
|
+
raise ParseError, "failed to create Bibliography from #{input.inspect}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
include Comparable
|
114
|
+
include Enumerable
|
115
|
+
|
116
|
+
extend Forwardable
|
108
117
|
|
109
|
-
|
118
|
+
attr_reader :options, :errors, :references
|
110
119
|
|
111
|
-
|
120
|
+
attr_accessor :prefix, :suffix
|
112
121
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
def_delegators :@references, :[], :[]=, :<<, :push, :unshift, :pop,
|
117
|
-
:concat, :include?, :index, :length, :empty?
|
118
|
-
|
119
|
-
def initialize(options = {})
|
120
|
-
@options = Bibliography.defaults.merge(options)
|
121
|
-
@errors, @references = [], []
|
122
|
-
|
123
|
-
yield self if block_given?
|
124
|
-
end
|
125
|
-
|
126
|
-
def initialize_copy(other)
|
127
|
-
@options = other.options.dup
|
128
|
-
@errors, @references = other.errors.dup, other.references.dup
|
129
|
-
end
|
122
|
+
# Bibliographies quack sorta like an Array
|
123
|
+
def_delegators :@references, :length, :empty?, :[], :include?, :index
|
130
124
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
125
|
+
# Some delegators should return self
|
126
|
+
[:push, :<<, :unshift, :concat].each do |m|
|
127
|
+
define_method(m) do |*arguments, &block|
|
128
|
+
references.send(m, *arguments, &block)
|
129
|
+
self
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
def initialize(options = {})
|
135
|
+
@options = Bibliography.defaults.merge(options)
|
136
|
+
@errors, @references = [], []
|
137
|
+
|
138
|
+
yield self if block_given?
|
139
|
+
end
|
140
|
+
|
141
|
+
def initialize_copy(other)
|
142
|
+
@options = other.options.dup
|
143
|
+
@errors, @references = other.errors.dup, other.references.dup
|
144
|
+
end
|
150
145
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
146
|
+
def has_errors?
|
147
|
+
!errors.empty?
|
148
|
+
end
|
149
|
+
|
150
|
+
alias errors? has_errors?
|
151
|
+
|
152
|
+
def join(connector = "\n")
|
153
|
+
[prefix, references.join(connector), suffix].join
|
154
|
+
end
|
155
|
+
|
156
|
+
def each
|
157
|
+
if block_given?
|
158
|
+
references.each(&Proc.new)
|
159
|
+
self
|
160
|
+
else
|
161
|
+
to_enum
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def <=>(other)
|
166
|
+
return nil unless other.respond_to?(:references)
|
167
|
+
references <=> other.references
|
168
|
+
end
|
169
|
+
|
170
|
+
def citeproc_options
|
171
|
+
Hash[*options.map { |k,v|
|
172
|
+
[Bibliography.rb2cp[k] || k.to_s, v]
|
173
|
+
}.flatten]
|
174
|
+
end
|
175
|
+
|
176
|
+
def to_citeproc
|
177
|
+
[
|
178
|
+
citeproc_options.merge({
|
179
|
+
'bibstart' => prefix,
|
180
|
+
'bibend' => suffix,
|
181
|
+
'bibliography_errors' => errors
|
182
|
+
}),
|
183
|
+
|
184
|
+
references
|
185
|
+
|
186
|
+
]
|
187
|
+
end
|
188
|
+
|
189
|
+
def to_json
|
190
|
+
MultiJson.encode(to_citeproc)
|
191
|
+
end
|
192
|
+
|
193
|
+
def inspect
|
194
|
+
"#<CiteProc::Bibliography @references=[#{references.length}], @errors=[#{errors.length}]>"
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
|
+
|
180
199
|
end
|