citeproc-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. data/README.md +78 -0
  2. data/lib/citeproc.rb +100 -0
  3. data/lib/citeproc/bibliography.rb +57 -0
  4. data/lib/citeproc/data.rb +149 -0
  5. data/lib/citeproc/date.rb +133 -0
  6. data/lib/citeproc/formatter.rb +38 -0
  7. data/lib/citeproc/item.rb +53 -0
  8. data/lib/citeproc/name.rb +284 -0
  9. data/lib/citeproc/processor.rb +166 -0
  10. data/lib/citeproc/selector.rb +61 -0
  11. data/lib/citeproc/variable.rb +82 -0
  12. data/lib/citeproc/version.rb +3 -0
  13. data/lib/csl/locale.rb +223 -0
  14. data/lib/csl/node.rb +72 -0
  15. data/lib/csl/nodes.rb +1364 -0
  16. data/lib/csl/renderer.rb +88 -0
  17. data/lib/csl/sort.rb +53 -0
  18. data/lib/csl/style.rb +110 -0
  19. data/lib/csl/term.rb +124 -0
  20. data/lib/extensions/core.rb +43 -0
  21. data/lib/plugins/filters/bibtex.rb +12 -0
  22. data/lib/plugins/formats/default.rb +134 -0
  23. data/lib/plugins/formats/html.rb +67 -0
  24. data/lib/support/attributes.rb +99 -0
  25. data/lib/support/tree.rb +80 -0
  26. data/resource/locale/locales-af-ZA.xml +304 -0
  27. data/resource/locale/locales-ar-AR.xml +304 -0
  28. data/resource/locale/locales-bg-BG.xml +304 -0
  29. data/resource/locale/locales-ca-AD.xml +304 -0
  30. data/resource/locale/locales-cs-CZ.xml +304 -0
  31. data/resource/locale/locales-da-DK.xml +304 -0
  32. data/resource/locale/locales-de-AT.xml +304 -0
  33. data/resource/locale/locales-de-CH.xml +304 -0
  34. data/resource/locale/locales-de-DE.xml +332 -0
  35. data/resource/locale/locales-el-GR.xml +303 -0
  36. data/resource/locale/locales-en-US.xml +313 -0
  37. data/resource/locale/locales-es-ES.xml +304 -0
  38. data/resource/locale/locales-et-EE.xml +304 -0
  39. data/resource/locale/locales-fr-FR.xml +304 -0
  40. data/resource/locale/locales-he-IL.xml +304 -0
  41. data/resource/locale/locales-hu-HU.xml +304 -0
  42. data/resource/locale/locales-is-IS.xml +304 -0
  43. data/resource/locale/locales-it-IT.xml +304 -0
  44. data/resource/locale/locales-ja-JP.xml +304 -0
  45. data/resource/locale/locales-kh-KH.xml +303 -0
  46. data/resource/locale/locales-ko-KR.xml +304 -0
  47. data/resource/locale/locales-mn-MN.xml +304 -0
  48. data/resource/locale/locales-nb-NO.xml +304 -0
  49. data/resource/locale/locales-nl-NL.xml +304 -0
  50. data/resource/locale/locales-nn-NO.xml +304 -0
  51. data/resource/locale/locales-pl-PL.xml +304 -0
  52. data/resource/locale/locales-pt-BR.xml +304 -0
  53. data/resource/locale/locales-pt-PT.xml +304 -0
  54. data/resource/locale/locales-ro-RO.xml +304 -0
  55. data/resource/locale/locales-ru-RU.xml +304 -0
  56. data/resource/locale/locales-sk-SK.xml +304 -0
  57. data/resource/locale/locales-sl-SI.xml +304 -0
  58. data/resource/locale/locales-sr-RS.xml +304 -0
  59. data/resource/locale/locales-sv-SE.xml +304 -0
  60. data/resource/locale/locales-th-TH.xml +304 -0
  61. data/resource/locale/locales-tr-TR.xml +304 -0
  62. data/resource/locale/locales-uk-UA.xml +304 -0
  63. data/resource/locale/locales-vi-VN.xml +304 -0
  64. data/resource/locale/locales-zh-CN.xml +304 -0
  65. data/resource/locale/locales-zh-TW.xml +304 -0
  66. data/resource/schema/csl-categories.rnc +39 -0
  67. data/resource/schema/csl-data.rnc +98 -0
  68. data/resource/schema/csl-terms.rnc +106 -0
  69. data/resource/schema/csl-types.rnc +39 -0
  70. data/resource/schema/csl-variables.rnc +182 -0
  71. data/resource/schema/csl.rnc +941 -0
  72. data/resource/style/acta-materialia-x.csl +128 -0
  73. data/resource/style/advanced-engineering-materials-x.csl +121 -0
  74. data/resource/style/ama.csl +185 -0
  75. data/resource/style/ama2-x.csl +179 -0
  76. data/resource/style/apa-x.csl +324 -0
  77. data/resource/style/apa.csl +254 -0
  78. data/resource/style/apsa-x.csl +163 -0
  79. data/resource/style/apsa.csl +176 -0
  80. data/resource/style/asa-x.csl +203 -0
  81. data/resource/style/asa.csl +216 -0
  82. data/resource/style/asm-journals-x.csl +131 -0
  83. data/resource/style/bibtex-x2.csl +175 -0
  84. data/resource/style/bluebook-demo-x.csl +392 -0
  85. data/resource/style/bluebook-demo.csl +942 -0
  86. data/resource/style/chicago-author-date-listing.csl +434 -0
  87. data/resource/style/chicago-author-date.csl +369 -0
  88. data/resource/style/chicago-fullnote-bibliography-bb.csl +928 -0
  89. data/resource/style/chicago-fullnote-bibliography.csl +695 -0
  90. data/resource/style/chicago-note-bibliography.csl +446 -0
  91. data/resource/style/chicago-note.csl +388 -0
  92. data/resource/style/greek-chicago-x.csl +1182 -0
  93. data/resource/style/harvard1-institution-italic.csl +190 -0
  94. data/resource/style/harvard1.csl +181 -0
  95. data/resource/style/ieee.csl +129 -0
  96. data/resource/style/mhra-x.csl +312 -0
  97. data/resource/style/mhra.csl +390 -0
  98. data/resource/style/mhra_note_without_bibliography-x.csl +330 -0
  99. data/resource/style/mhra_note_without_bibliography.csl +338 -0
  100. data/resource/style/mla-x.csl +178 -0
  101. data/resource/style/mla.csl +189 -0
  102. data/resource/style/nature-x.csl +81 -0
  103. data/resource/style/nature.csl +88 -0
  104. data/resource/style/nlm.csl +117 -0
  105. data/spec/citeproc/bibliography_spec.rb +45 -0
  106. data/spec/citeproc/citeproc_spec.rb +76 -0
  107. data/spec/citeproc/date_spec.rb +85 -0
  108. data/spec/citeproc/formatter_spec.rb +101 -0
  109. data/spec/citeproc/item_spec.rb +71 -0
  110. data/spec/citeproc/name_spec.rb +30 -0
  111. data/spec/citeproc/processor_spec.rb +61 -0
  112. data/spec/citeproc/selector_spec.rb +82 -0
  113. data/spec/citeproc/variable_spec.rb +69 -0
  114. data/spec/csl/locale_spec.rb +208 -0
  115. data/spec/csl/node_spec.rb +25 -0
  116. data/spec/csl/nodes_spec.rb +140 -0
  117. data/spec/csl/style_spec.rb +62 -0
  118. data/spec/csl/term_spec.rb +56 -0
  119. data/spec/fixtures/dates.yaml +80 -0
  120. data/spec/fixtures/names.yaml +115 -0
  121. data/spec/fixtures/nodes.yaml +245 -0
  122. data/spec/spec_helper.rb +18 -0
  123. data/spec/support/attributes_spec.rb +39 -0
  124. data/spec/support/tree_spec.rb +163 -0
  125. 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