citeproc-ruby 0.0.1
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/README.md +78 -0
- data/lib/citeproc.rb +100 -0
- data/lib/citeproc/bibliography.rb +57 -0
- data/lib/citeproc/data.rb +149 -0
- data/lib/citeproc/date.rb +133 -0
- data/lib/citeproc/formatter.rb +38 -0
- data/lib/citeproc/item.rb +53 -0
- data/lib/citeproc/name.rb +284 -0
- data/lib/citeproc/processor.rb +166 -0
- data/lib/citeproc/selector.rb +61 -0
- data/lib/citeproc/variable.rb +82 -0
- data/lib/citeproc/version.rb +3 -0
- data/lib/csl/locale.rb +223 -0
- data/lib/csl/node.rb +72 -0
- data/lib/csl/nodes.rb +1364 -0
- data/lib/csl/renderer.rb +88 -0
- data/lib/csl/sort.rb +53 -0
- data/lib/csl/style.rb +110 -0
- data/lib/csl/term.rb +124 -0
- data/lib/extensions/core.rb +43 -0
- data/lib/plugins/filters/bibtex.rb +12 -0
- data/lib/plugins/formats/default.rb +134 -0
- data/lib/plugins/formats/html.rb +67 -0
- data/lib/support/attributes.rb +99 -0
- data/lib/support/tree.rb +80 -0
- data/resource/locale/locales-af-ZA.xml +304 -0
- data/resource/locale/locales-ar-AR.xml +304 -0
- data/resource/locale/locales-bg-BG.xml +304 -0
- data/resource/locale/locales-ca-AD.xml +304 -0
- data/resource/locale/locales-cs-CZ.xml +304 -0
- data/resource/locale/locales-da-DK.xml +304 -0
- data/resource/locale/locales-de-AT.xml +304 -0
- data/resource/locale/locales-de-CH.xml +304 -0
- data/resource/locale/locales-de-DE.xml +332 -0
- data/resource/locale/locales-el-GR.xml +303 -0
- data/resource/locale/locales-en-US.xml +313 -0
- data/resource/locale/locales-es-ES.xml +304 -0
- data/resource/locale/locales-et-EE.xml +304 -0
- data/resource/locale/locales-fr-FR.xml +304 -0
- data/resource/locale/locales-he-IL.xml +304 -0
- data/resource/locale/locales-hu-HU.xml +304 -0
- data/resource/locale/locales-is-IS.xml +304 -0
- data/resource/locale/locales-it-IT.xml +304 -0
- data/resource/locale/locales-ja-JP.xml +304 -0
- data/resource/locale/locales-kh-KH.xml +303 -0
- data/resource/locale/locales-ko-KR.xml +304 -0
- data/resource/locale/locales-mn-MN.xml +304 -0
- data/resource/locale/locales-nb-NO.xml +304 -0
- data/resource/locale/locales-nl-NL.xml +304 -0
- data/resource/locale/locales-nn-NO.xml +304 -0
- data/resource/locale/locales-pl-PL.xml +304 -0
- data/resource/locale/locales-pt-BR.xml +304 -0
- data/resource/locale/locales-pt-PT.xml +304 -0
- data/resource/locale/locales-ro-RO.xml +304 -0
- data/resource/locale/locales-ru-RU.xml +304 -0
- data/resource/locale/locales-sk-SK.xml +304 -0
- data/resource/locale/locales-sl-SI.xml +304 -0
- data/resource/locale/locales-sr-RS.xml +304 -0
- data/resource/locale/locales-sv-SE.xml +304 -0
- data/resource/locale/locales-th-TH.xml +304 -0
- data/resource/locale/locales-tr-TR.xml +304 -0
- data/resource/locale/locales-uk-UA.xml +304 -0
- data/resource/locale/locales-vi-VN.xml +304 -0
- data/resource/locale/locales-zh-CN.xml +304 -0
- data/resource/locale/locales-zh-TW.xml +304 -0
- data/resource/schema/csl-categories.rnc +39 -0
- data/resource/schema/csl-data.rnc +98 -0
- data/resource/schema/csl-terms.rnc +106 -0
- data/resource/schema/csl-types.rnc +39 -0
- data/resource/schema/csl-variables.rnc +182 -0
- data/resource/schema/csl.rnc +941 -0
- data/resource/style/acta-materialia-x.csl +128 -0
- data/resource/style/advanced-engineering-materials-x.csl +121 -0
- data/resource/style/ama.csl +185 -0
- data/resource/style/ama2-x.csl +179 -0
- data/resource/style/apa-x.csl +324 -0
- data/resource/style/apa.csl +254 -0
- data/resource/style/apsa-x.csl +163 -0
- data/resource/style/apsa.csl +176 -0
- data/resource/style/asa-x.csl +203 -0
- data/resource/style/asa.csl +216 -0
- data/resource/style/asm-journals-x.csl +131 -0
- data/resource/style/bibtex-x2.csl +175 -0
- data/resource/style/bluebook-demo-x.csl +392 -0
- data/resource/style/bluebook-demo.csl +942 -0
- data/resource/style/chicago-author-date-listing.csl +434 -0
- data/resource/style/chicago-author-date.csl +369 -0
- data/resource/style/chicago-fullnote-bibliography-bb.csl +928 -0
- data/resource/style/chicago-fullnote-bibliography.csl +695 -0
- data/resource/style/chicago-note-bibliography.csl +446 -0
- data/resource/style/chicago-note.csl +388 -0
- data/resource/style/greek-chicago-x.csl +1182 -0
- data/resource/style/harvard1-institution-italic.csl +190 -0
- data/resource/style/harvard1.csl +181 -0
- data/resource/style/ieee.csl +129 -0
- data/resource/style/mhra-x.csl +312 -0
- data/resource/style/mhra.csl +390 -0
- data/resource/style/mhra_note_without_bibliography-x.csl +330 -0
- data/resource/style/mhra_note_without_bibliography.csl +338 -0
- data/resource/style/mla-x.csl +178 -0
- data/resource/style/mla.csl +189 -0
- data/resource/style/nature-x.csl +81 -0
- data/resource/style/nature.csl +88 -0
- data/resource/style/nlm.csl +117 -0
- data/spec/citeproc/bibliography_spec.rb +45 -0
- data/spec/citeproc/citeproc_spec.rb +76 -0
- data/spec/citeproc/date_spec.rb +85 -0
- data/spec/citeproc/formatter_spec.rb +101 -0
- data/spec/citeproc/item_spec.rb +71 -0
- data/spec/citeproc/name_spec.rb +30 -0
- data/spec/citeproc/processor_spec.rb +61 -0
- data/spec/citeproc/selector_spec.rb +82 -0
- data/spec/citeproc/variable_spec.rb +69 -0
- data/spec/csl/locale_spec.rb +208 -0
- data/spec/csl/node_spec.rb +25 -0
- data/spec/csl/nodes_spec.rb +140 -0
- data/spec/csl/style_spec.rb +62 -0
- data/spec/csl/term_spec.rb +56 -0
- data/spec/fixtures/dates.yaml +80 -0
- data/spec/fixtures/names.yaml +115 -0
- data/spec/fixtures/nodes.yaml +245 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/attributes_spec.rb +39 -0
- data/spec/support/tree_spec.rb +163 -0
- metadata +264 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module CiteProc
|
|
2
|
+
|
|
3
|
+
module Format
|
|
4
|
+
def self.default; CiteProc::Format::Default.new; end
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
class Formatter
|
|
8
|
+
|
|
9
|
+
def format(*args)
|
|
10
|
+
@format ||= CiteProc.default_format
|
|
11
|
+
args.empty? ? @format : apply(args[0], args[1])
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def format=(format)
|
|
15
|
+
@format = Format.const_get(format.to_s.split(/[\s_-]+/).map(&:capitalize).join).new
|
|
16
|
+
rescue Exception => e
|
|
17
|
+
CiteProc.log :warn, "failed to set format to #{ format.inspect }", e
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def apply(input='', attributes={})
|
|
21
|
+
return input if attributes.nil? || input.nil? || input.empty?
|
|
22
|
+
|
|
23
|
+
format.input = input
|
|
24
|
+
|
|
25
|
+
CSL::Nodes.formatting_attributes.each do |attribute|
|
|
26
|
+
method_id = ['set', attribute.gsub(/-/, '_')].join('_')
|
|
27
|
+
|
|
28
|
+
if !attributes[attribute].nil? && format.respond_to?(method_id)
|
|
29
|
+
format.send(method_id, attributes[attribute])
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
format.finalize
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'observer'
|
|
2
|
+
|
|
3
|
+
module CiteProc
|
|
4
|
+
|
|
5
|
+
class Item
|
|
6
|
+
include Comparable
|
|
7
|
+
include Observable
|
|
8
|
+
include Support::Attributes
|
|
9
|
+
|
|
10
|
+
attr_fields Variable.fields
|
|
11
|
+
attr_fields %w{ locator label suppress-author author-only prefix suffix }
|
|
12
|
+
|
|
13
|
+
def initialize(attributes={}, filter=nil)
|
|
14
|
+
self.merge!(attributes)
|
|
15
|
+
yield self if block_given?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.filter(attributes, filter)
|
|
19
|
+
# TODO
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @see CSL::Nodes::Group
|
|
23
|
+
alias :access :[]
|
|
24
|
+
def [](key)
|
|
25
|
+
value = access(key)
|
|
26
|
+
changed
|
|
27
|
+
notify_observers(key, value)
|
|
28
|
+
value
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def merge!(other)
|
|
32
|
+
other = other.attributes unless other.is_a?(Hash)
|
|
33
|
+
other.each_pair { |key, value| self.attributes[key] = Variable.parse(value, key) }
|
|
34
|
+
self
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def reverse_merge!(other)
|
|
38
|
+
other = other.attributes unless other.is_a?(Hash)
|
|
39
|
+
other.each_pair { |key, value| self.attributes[key] ||= Variable.parse(value, key) }
|
|
40
|
+
self
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def to_s
|
|
44
|
+
self.attributes.inspect
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def <=>(other)
|
|
49
|
+
self.attributes <=> other.attributes
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
module CiteProc
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# == Name Variables
|
|
5
|
+
#
|
|
6
|
+
# When present in the item data, CSL name variables must be delivered as a
|
|
7
|
+
# list of JavaScript arrays, with one array for each name represented by the
|
|
8
|
+
# variable. Simple personal names are composed of family and given elements,
|
|
9
|
+
# containing respectively the family and given name of the individual.
|
|
10
|
+
#
|
|
11
|
+
# { "author" : [
|
|
12
|
+
# { "family" : "Doe", "given" : "Jonathan" },
|
|
13
|
+
# { "family" : "Roe", "given" : "Jane" }
|
|
14
|
+
# ],
|
|
15
|
+
# "editor" : [
|
|
16
|
+
# { "family" : "Saunders",
|
|
17
|
+
# "given" : "John Bertrand de Cusance Morant" }
|
|
18
|
+
# ]
|
|
19
|
+
# }
|
|
20
|
+
#
|
|
21
|
+
# Institutional and other names that should always be presented literally
|
|
22
|
+
# (such as "The Artist Formerly Known as Prince", "Banksy", or "Ramses IV")
|
|
23
|
+
# should be delivered as a single literal element in the name array:
|
|
24
|
+
#
|
|
25
|
+
# { "author" : [
|
|
26
|
+
# { "literal" : "Society for Putting Things on Top of Other Things" }
|
|
27
|
+
# ]
|
|
28
|
+
# }
|
|
29
|
+
#
|
|
30
|
+
# If the name is spelled using a 'byzantine' alphabet (i.e., latin or
|
|
31
|
+
# cyrillic) its sort and display order is computed according to the given
|
|
32
|
+
# arguments.
|
|
33
|
+
#
|
|
34
|
+
class Name < Variable
|
|
35
|
+
|
|
36
|
+
# Based on the regular expression in citeproc-js
|
|
37
|
+
ROMANESQUE = /^[a-zA-Z\u0080-\u017f\u0400-\u052f\u0386-\u03fb\u1f00-\u1ffe\.,\s'\u0027\u02bc\u2019-]*$/
|
|
38
|
+
|
|
39
|
+
Variable.name_fields.each { |field| Variable.types[field] = Name }
|
|
40
|
+
|
|
41
|
+
attr_fields %w{ given family literal suffix dropping-particle
|
|
42
|
+
non-dropping-particle comma-suffix static-ordering parse-names }
|
|
43
|
+
|
|
44
|
+
[[:last, :family], [:first, :given]].each do |m|
|
|
45
|
+
alias_method m[0], m[1]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def defaults
|
|
49
|
+
Hash['form', 'long', 'name-as-sort-order', 'false', 'demote-non-dropping-particle', 'display-and-sort']
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def options
|
|
53
|
+
@options ||= defaults
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def merge_options(options)
|
|
57
|
+
options.each_pair { |key, value| self.options[key] = value unless value.nil? }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def parse!(argument)
|
|
61
|
+
return super unless argument.is_a?(String)
|
|
62
|
+
parse_name!(argument)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def merge!(argument)
|
|
66
|
+
if argument['parse-names'] && argument.delete('parse-names') != 'false'
|
|
67
|
+
parse_family!(argument.delete('family'))
|
|
68
|
+
parse_given!(argument.delete('given'))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
argument.map { |key, value| self[key] = value }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def given
|
|
75
|
+
initialize? ? to_initials(self['given']) : self['given']
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def to_initials(name)
|
|
79
|
+
return name if name.nil?
|
|
80
|
+
|
|
81
|
+
name.split(/\s+|\.\s*/).map do |token|
|
|
82
|
+
token.split(/-/).map do |part|
|
|
83
|
+
# Keep all-lowercase names; otherwise keep only upper case letters
|
|
84
|
+
part.match(/^[[:lower:]]+$/) ? part.center(part.length + 2) : part.scan(/[[:upper:]]/).join.capitalize + options['initialize-with']
|
|
85
|
+
end.join(options['initialize-with-hyphen'] == 'false' ? '' : '-' ).gsub(/\s+-/, '-')
|
|
86
|
+
end.join
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Parses a string and sets :family, :given, :suffix, and :particles
|
|
90
|
+
# correspondingly.
|
|
91
|
+
#
|
|
92
|
+
# * non-dropping-particle: A string at the beginning of the family field
|
|
93
|
+
# consisting of spaces and lowercase roman or Cyrillic characters will
|
|
94
|
+
# be treated as a non-dropping-particle. The particles preceding some
|
|
95
|
+
# names should be treated as part of the last name, depending on the
|
|
96
|
+
# cultural heritage and personal preferences of the individual. To
|
|
97
|
+
# suppress parsing and treat such particles as part of the family name
|
|
98
|
+
# field, enclose the family name field content in double-quotes
|
|
99
|
+
# * dropping-particle: A string at the end of the given name field
|
|
100
|
+
# consisting of spaces and lowercase roman or Cyrillic characters will
|
|
101
|
+
# be treated as a dropping-particle.
|
|
102
|
+
# * suffix: Content following a comma in the given name field will be
|
|
103
|
+
# parse out as a name suffix. Modern typographical convention does not
|
|
104
|
+
# place a comma between suffixes such as "Jr." and the last name, when
|
|
105
|
+
# rendering the name in normal order: "John Doe Jr." If an individual
|
|
106
|
+
# prefers that the traditional comma be used in rendering their name,
|
|
107
|
+
# the comma can be force by placing a exclamation mark after the comma.
|
|
108
|
+
#
|
|
109
|
+
def parse_name!(string)
|
|
110
|
+
return if string.nil?
|
|
111
|
+
|
|
112
|
+
tokens = string.split(/,\s+/)
|
|
113
|
+
|
|
114
|
+
parse_family!(tokens[0])
|
|
115
|
+
parse_given!(tokens[1])
|
|
116
|
+
|
|
117
|
+
self
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# @see parse
|
|
121
|
+
def parse_family!(string)
|
|
122
|
+
return if string.nil?
|
|
123
|
+
|
|
124
|
+
tokens = string.scan(/^['"](.+)['"]$|^([[:lower:]\s]+)?([[:upper:]][[:alpha:]\s]*)$/).first
|
|
125
|
+
|
|
126
|
+
if tokens.nil?
|
|
127
|
+
self['family'] = string
|
|
128
|
+
else
|
|
129
|
+
self['family'] = tokens[0] || tokens[2] || string
|
|
130
|
+
self['non-dropping-particle'] = tokens[1].gsub(/^\s+|\s+$/, '') unless tokens[1].nil?
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
self
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# @see parse
|
|
137
|
+
def parse_given!(string)
|
|
138
|
+
return if string.nil?
|
|
139
|
+
|
|
140
|
+
tokens = string.scan(/^((?:[[:upper:]][[:alpha:]\.]*\s*)+)([[:lower:]\s]+)?(?:,!?\s([[:alpha:]\.\s]+))?$/).first
|
|
141
|
+
|
|
142
|
+
if tokens.nil?
|
|
143
|
+
self['given'] = string
|
|
144
|
+
else
|
|
145
|
+
self['given'] = (tokens[0] || string).gsub(/^\s+|\s+$/, '')
|
|
146
|
+
self['dropping-particle'] = tokens[1] unless tokens[1].nil?
|
|
147
|
+
self['suffix'] = tokens[2] unless tokens[2].nil?
|
|
148
|
+
self['comma-suffix'] = 'true' if string.match(/,!/)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
self
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def personal?
|
|
155
|
+
self.family?
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def romanesque?
|
|
159
|
+
(self['given'].nil? || self['given'].match(ROMANESQUE)) && (self['family'].nil? || self['family'].match(ROMANESQUE))
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
alias :byzantine? :romanesque?
|
|
163
|
+
|
|
164
|
+
def comma_suffix
|
|
165
|
+
self.comma_suffix? && self.suffix? ? comma : nil
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def comma
|
|
169
|
+
options['sort-separator'] || ', '
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def delimiter
|
|
173
|
+
romanesque? ? ' ' : ''
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def value
|
|
177
|
+
self
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
#
|
|
181
|
+
# CSL1.0 Warning
|
|
182
|
+
#
|
|
183
|
+
# @returns true if, using the current options, the name's given name is
|
|
184
|
+
# to be displayed using initials. Takes into account whether or not the
|
|
185
|
+
# family name is set (if not, given name should not be turned to initials,
|
|
186
|
+
# this is *not* sepcified in CSL 1.0).
|
|
187
|
+
#
|
|
188
|
+
def initialize?
|
|
189
|
+
options.has_key?('initialize-with') && family? && romanesque?
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def static_order?
|
|
193
|
+
static_ordering? || !romanesque?
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def sort_order?
|
|
197
|
+
['all', 'true', 'yes', 'always'].include?(options['name-as-sort-order'])
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def numeric?
|
|
201
|
+
false
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# @returns a list of strings, representing a given order of the individual
|
|
205
|
+
# tokens when displaying the name.
|
|
206
|
+
def display_order(opts={})
|
|
207
|
+
merge_options(opts)
|
|
208
|
+
|
|
209
|
+
case
|
|
210
|
+
when literal?
|
|
211
|
+
return %w{ literal }
|
|
212
|
+
|
|
213
|
+
when static_order?
|
|
214
|
+
return %w{ family given }
|
|
215
|
+
|
|
216
|
+
when options['form'] != 'short' && !sort_order?
|
|
217
|
+
return %w{ given dropping-particle non-dropping-particle family comma-suffix suffix }
|
|
218
|
+
|
|
219
|
+
when options['form'] != 'short' && sort_order? && ['never', 'sort-only'].include?(options['demote-non-dropping-particle'])
|
|
220
|
+
return %w{ non-dropping-particle family comma given dropping-particle comma suffix }
|
|
221
|
+
|
|
222
|
+
when options['form'] != 'short' && sort_order? && options['demote-non-dropping-particle'] == 'display-and-sort'
|
|
223
|
+
return %w{ family comma given dropping-particle non-dropping-particle comma suffix }
|
|
224
|
+
|
|
225
|
+
else # options['form'] == 'short'
|
|
226
|
+
return %w{ non-dropping-particle family}
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# @returns a list of strings, representing the order of precedence of the
|
|
232
|
+
# individual tokens when sorting the name.
|
|
233
|
+
def sort_order(opts={})
|
|
234
|
+
merge_options(opts)
|
|
235
|
+
|
|
236
|
+
case
|
|
237
|
+
when literal?
|
|
238
|
+
return %w{ literal }
|
|
239
|
+
|
|
240
|
+
when options['demote-non-dropping-particle'] == 'never'
|
|
241
|
+
return %w{ non-dropping-particle+family dropping-particle given suffix }
|
|
242
|
+
else
|
|
243
|
+
return %w{ family non-dropping-particle+dropping-particle given suffix }
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# @returns a string representing the name according to the given set of
|
|
248
|
+
# display order options.
|
|
249
|
+
def display(options={})
|
|
250
|
+
normalize((display_order(options).map { |token| send(token.tr('-', '_')) }.compact.join(delimiter)))
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def normalize(string)
|
|
254
|
+
string.squeeze(' ').gsub(/^[\s,]+|[\s,]+$|\s(,)/, '\1').squeeze(',')
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def to_s
|
|
258
|
+
display
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def literal_as_sort_order
|
|
262
|
+
literal.gsub(/^(the|an?|der|die|das|eine?|l[ae])\s+/i, '')
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def <=>(other)
|
|
266
|
+
|
|
267
|
+
tests = self.sort_order.zip(other.sort_order).map do |pair|
|
|
268
|
+
this, that = pair.map { |token| token.gsub(/[\s-]+/,'_').gsub(/literal/, 'literal_sort_order') }
|
|
269
|
+
|
|
270
|
+
this = this.split(/\+/).map { |token| self.send(token) }.join.downcase
|
|
271
|
+
that = that.split(/\+/).map { |token| other.send(token) }.join.downcase
|
|
272
|
+
|
|
273
|
+
# TODO should we ignore '' here?
|
|
274
|
+
this <=> that
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
tests = tests.reject(&:nil?)
|
|
278
|
+
zeroes = tests.take_while(&:zero?)
|
|
279
|
+
|
|
280
|
+
zeroes.length != tests.length ? tests[zeroes.length] : zeroes.empty? ? nil : 0
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
require 'forwardable'
|
|
2
|
+
|
|
3
|
+
module CiteProc
|
|
4
|
+
|
|
5
|
+
class Processor
|
|
6
|
+
extend Forwardable
|
|
7
|
+
|
|
8
|
+
attr_reader :style, :formatter
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@formatter = Formatter.new
|
|
12
|
+
yield self if block_given?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.process(items, options = {})
|
|
16
|
+
return '' if items.nil? || items.empty?
|
|
17
|
+
|
|
18
|
+
processor = Processor.new do |p|
|
|
19
|
+
p.style = options[:style] || CSL.default_style
|
|
20
|
+
p.locale = options[:locale] || CSL.default_locale
|
|
21
|
+
p.format = options[:format] || :default
|
|
22
|
+
p.import(items)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
if options[:mode].to_s.match(/cit(e|ation)/i)
|
|
26
|
+
processor.cite(:all).map(&:last)
|
|
27
|
+
else
|
|
28
|
+
processor.bibliography.data.join
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.cite(items, options = {})
|
|
33
|
+
process(items, options.merge(:mode => 'citation'))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def style=(resource)
|
|
38
|
+
@style = resource.is_a?(CSL::Style) ? resource : CSL::Style.new(resource)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def format(*args); @formatter.format(*args); end
|
|
42
|
+
|
|
43
|
+
def format=(format); @formatter.format = format; end
|
|
44
|
+
|
|
45
|
+
def locale=(locale)
|
|
46
|
+
@locale = locale.is_a?(CSL::Locale) ? locale : CSL::Locale.new(locale)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def locale
|
|
50
|
+
@locale ||= CSL.default_locale
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def_delegators :locale, :language, :language=, :region, :region=
|
|
54
|
+
|
|
55
|
+
# @returns the abbreviations, a self-recording hash.
|
|
56
|
+
def abbreviations
|
|
57
|
+
@abbreviations ||= new_abbreviations
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
alias :transfrom :abbreviations
|
|
61
|
+
|
|
62
|
+
def abbreviations=(abbreviations)
|
|
63
|
+
@abbreviations = new_abbreviations
|
|
64
|
+
add_abbreviations(abbreviations)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def add_abbreviations(abbreviations)
|
|
68
|
+
abbreviations.keys.each do |list|
|
|
69
|
+
abbreviations[list].keys.each do |category|
|
|
70
|
+
abbreviations[list][category].each_pair do |long, short|
|
|
71
|
+
self.abbreviations[list] ||= new_self_recording_hash
|
|
72
|
+
self.abbreviations[list][category][long] = short
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def abbreviate(category, name, list='default')
|
|
79
|
+
self.abbreviations[list][category][name]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def items
|
|
83
|
+
@items ||= {}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def import(items)
|
|
87
|
+
# TODO assign default ids if no id
|
|
88
|
+
items = to_a(items)
|
|
89
|
+
items.each do |item|
|
|
90
|
+
item = Item.new(item)
|
|
91
|
+
self.items[item['id'].to_s] = item
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def bibliography(selector = :all)
|
|
96
|
+
data = items.values.select(&Selector.new(selector)).map { |i| { 'id' => i.id } }
|
|
97
|
+
data = CitationData.new(data).populate!(items)
|
|
98
|
+
|
|
99
|
+
data = style.bibliography.process(data, self)
|
|
100
|
+
Bibliography.new(data)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
#
|
|
105
|
+
# @param data Symbol :all / or id of item
|
|
106
|
+
# @param data String id of item
|
|
107
|
+
# @param data Array list of ids or citation data
|
|
108
|
+
# @param data Hash citation data or citation items
|
|
109
|
+
#
|
|
110
|
+
# @returns a list of lists; [[1, 'Doe, 2000, p. 1'], ...]
|
|
111
|
+
#
|
|
112
|
+
def cite(data)
|
|
113
|
+
data = extract_citation_data(data)
|
|
114
|
+
|
|
115
|
+
data.populate!(items)
|
|
116
|
+
citation = style.citation.render(data, self)
|
|
117
|
+
|
|
118
|
+
[[register(citation), citation]]
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def nocite(ids, options={})
|
|
122
|
+
@bibliography + to_a(ids).map { |id| items[id] }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
alias :make_bibliography :bibliography
|
|
126
|
+
alias :update_items :cite
|
|
127
|
+
alias :update_uncited_items :nocite
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
def register(id)
|
|
133
|
+
1
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def to_a(attribute)
|
|
137
|
+
attribute.is_a?(Array) ? attribute : [attribute]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# @returns a citation data object
|
|
141
|
+
def extract_citation_data(argument)
|
|
142
|
+
case
|
|
143
|
+
when argument == :all
|
|
144
|
+
argument = items.keys.map { |id| { 'id' => id } }
|
|
145
|
+
|
|
146
|
+
when items.has_key?(argument.to_s)
|
|
147
|
+
argument = { 'id' => argument.to_s }
|
|
148
|
+
|
|
149
|
+
when argument.is_a?(Array) && items.has_key?(argument.first.to_s)
|
|
150
|
+
argument = argument.map { |id| { 'id' => id } }
|
|
151
|
+
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
CitationData.new(argument)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def new_abbreviations
|
|
158
|
+
{ 'default' => new_self_recording_hash }
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def new_self_recording_hash
|
|
162
|
+
Hash.new { |h,k| h[k] = Hash.new { |h,k| h[k] = k } }
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
end
|