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.
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