citeproc-rb 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 (52) hide show
  1. data/README +93 -0
  2. data/Rakefile +73 -0
  3. data/TODO +25 -0
  4. data/VERSION +1 -0
  5. data/data/locales/locales_en.xml +89 -0
  6. data/data/locales/locales_nl.xml +96 -0
  7. data/data/styles/ama.csl +184 -0
  8. data/data/styles/apa.csl +230 -0
  9. data/data/styles/apsa.csl +175 -0
  10. data/data/styles/asa.csl +215 -0
  11. data/data/styles/chicago-author-date.csl +361 -0
  12. data/data/styles/chicago-fullnote-bibliography.csl +665 -0
  13. data/data/styles/chicago-note-bibliography.csl +437 -0
  14. data/data/styles/chicago-note.csl +383 -0
  15. data/data/styles/harvard.csl +185 -0
  16. data/data/styles/ieee.csl +111 -0
  17. data/data/styles/mhra.csl +317 -0
  18. data/data/styles/mla.csl +173 -0
  19. data/data/styles/nature.csl +87 -0
  20. data/data/styles/nlm.csl +118 -0
  21. data/examples/citeproc_driver.rb +174 -0
  22. data/lib/citeproc.rb +28 -0
  23. data/lib/citeproc/csl.rb +647 -0
  24. data/lib/citeproc/csl_parser.rb +479 -0
  25. data/lib/citeproc/csl_processor.rb +485 -0
  26. data/lib/citeproc/csl_reference.rb +228 -0
  27. data/lib/citeproc/filters/bibo/bibo.rb +635 -0
  28. data/lib/citeproc/filters/bibo/bibo_input_filter.rb +242 -0
  29. data/lib/citeproc/filters/bibo/bibo_utils.rb +75 -0
  30. data/lib/citeproc/filters/csl_citation/csl_citation.rb +137 -0
  31. data/lib/citeproc/filters/csl_citation/csl_citation_input_filter.rb +274 -0
  32. data/lib/citeproc/formatters/base_formatter.rb +115 -0
  33. data/lib/citeproc/formatters/xhtml_formatter.rb +144 -0
  34. data/lib/citeproc/input_filter.rb +97 -0
  35. data/test/fixtures/bibo_test_data.n3 +191 -0
  36. data/test/fixtures/bibo_test_data.yaml +96 -0
  37. data/test/fixtures/bibo_test_how_to_cook.n3 +63 -0
  38. data/test/fixtures/bibo_test_zotero.rdf +129 -0
  39. data/test/fixtures/csl_test_data.json +62 -0
  40. data/test/fixtures/csl_test_data.yaml +8 -0
  41. data/test/fixtures/locales/test_locale_en.xml +89 -0
  42. data/test/fixtures/styles/test_csl_parse.csl +230 -0
  43. data/test/fixtures/styles/test_groups.csl +32 -0
  44. data/test/fixtures/styles/test_names.csl +46 -0
  45. data/test/fixtures/styles/test_sort.csl +125 -0
  46. data/test/fixtures/styles/test_text_terms.csl +40 -0
  47. data/test/test_all.rb +14 -0
  48. data/test/test_bibo.rb +20 -0
  49. data/test/test_csl.rb +136 -0
  50. data/test/test_csl_parser.rb +222 -0
  51. data/test/test_sample_bibo.rb +82 -0
  52. metadata +101 -0
@@ -0,0 +1,274 @@
1
+ # == Synopsis
2
+ #
3
+ # Resolves CSL variables to a simple Citation model.
4
+ #
5
+ # == Author
6
+ #
7
+ # Liam Magee
8
+ #
9
+ # == Copyright
10
+ #
11
+ # Copyright (c) 2007, Liam Magee.
12
+ # Licensed under the same terms as Ruby - see http://www.ruby-lang.org/en/LICENSE.txt.
13
+ #
14
+
15
+ module CSL
16
+ class CslInputFilter < Citeproc::InputFilter
17
+
18
+
19
+ # Loads the citations from +source+, based on the content_type passed in the +params+.
20
+ def load_citations(source, params)
21
+ io = source.read
22
+ content_type = params[:content_type]
23
+
24
+ # Load the citations from the specified content type
25
+ case content_type.downcase
26
+ when "yaml"
27
+ require 'yaml'
28
+ results = YAML.load(io)
29
+ when "json"
30
+ require 'json'
31
+ results = JSON.parse(io)
32
+ else
33
+ raise RuntimeError.new("Unsupported content-type: " + content_type.inspect + ". Must be either YAML or JSON.")
34
+ end
35
+
36
+ results.each do |key, value|
37
+ citation = load_citation_from_hash(value)
38
+ @citations[key] = citation
39
+ end if results
40
+ end
41
+
42
+
43
+
44
+ # Loads an individual citation from the +hash+ object
45
+ def load_citation_from_hash(hash)
46
+ citation = Citation.new
47
+ # Much easier, but requires active_support (sudo gem install active_support --include-dependencies)
48
+ # Overkill for just this method?
49
+ if require "active_support"
50
+ hash.each do |k, v|
51
+ citation.send("#{k.underscore}=", v)
52
+ end
53
+ # Long hand... potentially incomplete
54
+ else
55
+ citation.type = hash[:type.to_s]
56
+ citation.title = hash[:title.to_s]
57
+ citation.container_title = hash[:containerTitle.to_s]
58
+ citation.collection_title = hash[:collectionTitle.to_s]
59
+ citation.date_issued = hash[:dateIssued.to_s]
60
+ citation.date_accessed = hash[:dateAccessed.to_s]
61
+ citation.publisher = hash[:publisher.to_s]
62
+ citation.publisher_place = hash[:publisherPlace.to_s]
63
+ citation.issue = hash[:issue.to_s]
64
+ citation.url = hash[:url.to_s]
65
+ citation.pages = hash[:pages.to_s]
66
+ citation.authors = hash[:authors.to_s]
67
+ citation.editors = hash[:editors.to_s]
68
+ end
69
+ citation
70
+ end
71
+
72
+
73
+
74
+ def extract_contributor(role, sort_key = nil)
75
+ contributors = @current_citation.contributors(role, sort_key)
76
+ contributors.each do |contrib|
77
+ yield contrib
78
+ end
79
+ end
80
+
81
+
82
+
83
+ # Tests the document class, and returns the corresponding CSL type
84
+ def resolve_type
85
+ @current_citation.type
86
+ end
87
+
88
+ def extract_date(variable)
89
+ case variable
90
+ when "issued"
91
+ convert_date(@current_citation.date_issued)
92
+ when "event" # Kludgy way of handling event - treat as presentation
93
+ # @current_citation.presented_at.time.start if @current_citation.presented_at and @current_citation.presented_at.time
94
+ when "accessed"
95
+ convert_date(@current_citation.date_accessed)
96
+ when "container"
97
+ # @current_citation.belongs_to.date if @current_citation.belongs_to
98
+ end
99
+ end
100
+
101
+ def convert_date(date)
102
+ if date
103
+ date = date + "-01-01" if date.length == 4
104
+ Date.parse(date)
105
+ end
106
+ end
107
+
108
+
109
+ # Very simple check on the value of the locator (or, in specific cases, of other document
110
+ # variables) to determine whether
111
+ # It may be be better to simply return the locator value here
112
+ def extract_label(variable)
113
+ singular = true
114
+ if variable == 'page'
115
+ singlular = (@current_citation.pages and @current_citation.pages.include?('-'))
116
+ locator_type = variable
117
+ else
118
+ locator_type = @current_citation.locator_type
119
+ case @current_citation.locator_type
120
+ when 'issue'
121
+ singlular = (@current_citation.issue and @current_citation.issue.include?('-'))
122
+ when 'page'
123
+ singlular = (@current_citation.pages and @current_citation.pages.include?('-'))
124
+ when 'volume'
125
+ singlular = (@current_citation.volume and @current_citation.volume.include?('-'))
126
+ else
127
+ singlular = (@current_citation.locator and @current_citation.locator.include?('-'))
128
+ end
129
+ end
130
+ return locator_type, singular
131
+ end
132
+
133
+
134
+
135
+ def extract_variable(variable)
136
+ case variable
137
+
138
+ ## the primary title for the cited item
139
+ when 'author'
140
+ @current_citation.authors
141
+
142
+ when 'editor'
143
+ @current_citation.editors
144
+
145
+ ## the primary title for the cited item
146
+ when 'title'
147
+ @current_citation.title
148
+
149
+ ## the secondary title for the cited item; for a book chapter, this
150
+ ## would be a book title, for an article the journal title, etc.
151
+ when 'container-title'
152
+ @current_citation.container_title
153
+
154
+ ## the tertiary title for the cited item; for example, a series title
155
+ when 'collection-title'
156
+ @current_citation.collection_title
157
+
158
+ ## title of a related original version; often useful in cases of translation
159
+ when "original-title"
160
+ @current_citation.title
161
+
162
+ ## the name of the publisher
163
+ when "publisher"
164
+ @current_citation.publisher
165
+
166
+ ## the location of the publisher
167
+ when "publisher-place"
168
+ @current_citation.publisher_place
169
+
170
+ ## the name of the archive
171
+ when "archive"
172
+ # @current_citation.title
173
+
174
+ ## the location of the archive
175
+ when "archive-place"
176
+ # @current_citation.title
177
+
178
+ ## the location within an archival collection (for example, box and folder)
179
+ when "archive_location"
180
+ # @current_citation.title
181
+
182
+ ## the name or title of a related event such as a conference or hearing
183
+ when "event"
184
+ # @current_citation.presented_at.name if @current_citation.presented_at
185
+
186
+ ## the location or place for the related event
187
+ when "event-place"
188
+ # @current_citation.presented_at.place.name if @current_citation.presented_at and @current_citation.presented_at.place
189
+
190
+ ##
191
+ when "page"
192
+ @current_citation.pages
193
+
194
+ ## a description to locate an item within some larger container or
195
+ ## collection; a volume or issue number is a kind of locator, for example.
196
+ when "locator"
197
+ @current_citation.locator
198
+
199
+ ## version description
200
+ when "version"
201
+ @current_citation.version
202
+
203
+ ## volume number for the container periodical
204
+ when "volume"
205
+ @current_citation.volume
206
+
207
+ ## refers to the number of items in multi-volume books and such
208
+ when "number-of-volumes"
209
+ # ??
210
+
211
+ ## the issue number for the container publication
212
+ when "issue"
213
+ @current_citation.issue
214
+
215
+ ##
216
+ when "chapter-number"
217
+ @current_citation.number
218
+
219
+ ## medium description (DVD, CD, etc.)
220
+ when "medium"
221
+ @current_citation.format
222
+
223
+ ## the (typically publication) status of an item; for example "forthcoming"
224
+ when "status"
225
+ @current_citation.status
226
+
227
+ ## an edition description
228
+ when "edition"
229
+ @current_citation.edition
230
+
231
+ ##
232
+ when "genre"
233
+ @current_citation.genre
234
+
235
+ ## a short inline note, often used to refer to additional details of the resource
236
+ when "note"
237
+ @current_citation.note
238
+
239
+ ## notes made by a reader about the content of the resource
240
+ when "annote"
241
+ @current_citation.annote
242
+
243
+ ##
244
+ when "abstract"
245
+ @current_citation.abstract
246
+
247
+ ##
248
+ when "keyword"
249
+ @current_citation.keyword
250
+
251
+ ## a document number; useful for reports and such
252
+ when "number"
253
+ @current_citation.number
254
+
255
+ ##
256
+ when "URL"
257
+ @current_citation.uri
258
+
259
+ ##
260
+ when "DOI"
261
+ @current_citation.doi
262
+
263
+ ##
264
+ when "ISBN"
265
+ @current_citation.isbn10
266
+
267
+
268
+ else
269
+ extract_date(variable)
270
+ end
271
+
272
+ end
273
+ end
274
+ end
@@ -0,0 +1,115 @@
1
+ # == Synopsis
2
+ #
3
+ # Formats a series of document references according to a given CSL style.
4
+ #
5
+ # == Author
6
+ #
7
+ # Liam Magee
8
+ #
9
+ # == Copyright
10
+ #
11
+ # Copyright (c) 2008, Liam Magee.
12
+ # Licensed under the same terms as Ruby - see http://www.ruby-lang.org/en/LICENSE.txt.
13
+ #
14
+
15
+ module Citeproc
16
+
17
+ class BaseFormatter
18
+ def format(node)
19
+ result = ''
20
+ if node.is_a?(Array)
21
+ node.each do |n|
22
+ tmp = format(n)
23
+ if tmp
24
+ result << tmp
25
+ result << "\n"
26
+ end
27
+ end
28
+ elsif node
29
+ a = ''
30
+ b = []
31
+ a << delimit_node(node.element, node.results)
32
+ node.children.compact.each{|c| b << format(c) }
33
+ a << delimit_node(node.element, b)
34
+
35
+ result = format_node(node.element, a)
36
+ end
37
+ result
38
+ end
39
+
40
+
41
+ private
42
+
43
+ def format_node(formatter, text)
44
+ return text if !formatter.is_a?(Formatting)
45
+ return '' if !text or text.empty?
46
+ results = ''
47
+
48
+ results << formatter.prefix if formatter.prefix and !text.start_with?(formatter.prefix)
49
+ results << '"' if formatter.quotes
50
+
51
+ # Add the actual text
52
+ results << format_text(text, formatter.text_case)
53
+
54
+ results << '"' if formatter.quotes
55
+ results << formatter.suffix if formatter.suffix and !text.end_with?(formatter.suffix)
56
+
57
+ results
58
+ end
59
+
60
+ # The following performs direct text changes - should be toggled off for mark-up?
61
+ #lowercase, uppercase, capitalize-first, capitalize-all, title, sentence.
62
+ def format_text(text, textcase)
63
+ case textcase
64
+ when "lowercase"
65
+ text.downcase!
66
+ when "uppercase"
67
+ text.upcase!
68
+ when "capitalize-first"
69
+ text[0] = text[0..0].upcase
70
+ # Need to separate out these cases
71
+ when "capitalize-all", "title", "sentence"
72
+ # Tries a simple exclusion of common words from title capitalisation
73
+ # Suffers from a lack of internationalisation...
74
+ text = text.split(' ').collect{|t|
75
+ t.capitalize unless t == text.split(' ')[0] and ['and', 'the', 'a', 'of', 'by', 'to', 'at'].include?(t)
76
+ }.join(' ')
77
+ end
78
+ text
79
+ end
80
+
81
+
82
+ def delimit_node(node, elements)
83
+ results = ""
84
+ clean = elements.compact.reject{|e| e if e.to_s.strip.empty?}
85
+ len = clean.length - 1
86
+ delim = node.is_a?(Delimiter)
87
+ (0..len).each do |i|
88
+ case i
89
+ when 0
90
+ # Do nothing
91
+ when len
92
+ if delim
93
+ if node.delimiter_precedes_last and node.delimiter_precedes_last == "never"
94
+ # Still do nothing
95
+ elsif delim and node.delimiter
96
+ results << node.delimiter
97
+ end
98
+ if node.and == "text"
99
+ results.strip!
100
+ results << " and "
101
+ elsif node.and == "symbol"
102
+ results.strip!
103
+ results << " & "
104
+ end
105
+ end
106
+ else
107
+ results << node.delimiter if delim and node.delimiter
108
+ end
109
+ # Finally, add the content
110
+ results << clean[i].to_s
111
+ end
112
+ results
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,144 @@
1
+ # == Synopsis
2
+ #
3
+ # Formats a series of document references according to a given CSL style.
4
+ #
5
+ # == Author
6
+ #
7
+ # Liam Magee
8
+ #
9
+ # == Copyright
10
+ #
11
+ # Copyright (c) 2008, Liam Magee.
12
+ # Licensed under the same terms as Ruby - see http://www.ruby-lang.org/en/LICENSE.txt.
13
+ #
14
+
15
+ module Citeproc
16
+
17
+ class XhtmlFormatter
18
+
19
+ def format(node)
20
+ result = ''
21
+ if node.is_a?(Array)
22
+ node.each do |n|
23
+ tmp = format(n)
24
+ if tmp
25
+ result << "<div>"
26
+ result << tmp
27
+ result << "</div>\n"
28
+ end
29
+ end
30
+ elsif node
31
+ a = ''
32
+ b = []
33
+ a << delimit_node(node.element, node.results)
34
+ node.children.compact.each{|c| b << format(c) }
35
+ a << delimit_node(node.element, b)
36
+
37
+ result = format_node(node.element, a)
38
+ end
39
+ result
40
+ end
41
+
42
+
43
+ private
44
+
45
+ def format_node(formatter, text)
46
+ return text if !formatter.is_a?(Formatting)
47
+ return '' if !text or text.empty?
48
+ results = ''
49
+
50
+ text_content = format_text(text, formatter.text_case)
51
+ already_spanned = text_content.starts_with?("<span") and text_content.ends_with?("</span>")
52
+
53
+
54
+ if !already_spanned
55
+ # Note: these attributes should only apply to certain kinds of formatters, i.e. those based on mark-up
56
+ results << '<span' # Could be block/inline/div - needs to be conditional on both 'display' attribute and mark-up variant (XHTML, XSL-FO)
57
+ style = ''
58
+ style << "font-family: #{formatter.font_family}; " if formatter.font_family
59
+ style << "font-style: #{formatter.font_style}; " if formatter.font_style
60
+ style << "font-variant: #{formatter.font_variant}; " if formatter.font_variant
61
+ style << "font-weight: #{formatter.font_weight}; " if formatter.font_weight
62
+ style << "text-decoration: #{formatter.text_decoration}; " if formatter.text_decoration
63
+ style << "text-transform: #{formatter.text_transform}; " if formatter.text_transform
64
+ style << "vertical-align: #{formatter.vertical_align}; " if formatter.vertical_align
65
+ style << "enforce-case: #{formatter.enforce_case}; " if formatter.enforce_case
66
+ display = formatter.formatting[:display.to_s]
67
+ style << "display: #{display}; " if display
68
+ style << "quotes: #{formatter.quotes}; " if formatter.quotes
69
+ results << %Q' style="#{style}"' if !style.empty?
70
+ results << ">"
71
+ end
72
+
73
+ results << formatter.prefix if formatter.prefix and !text.start_with?(formatter.prefix)
74
+ results << '"' if formatter.quotes
75
+
76
+ # Add the actual text
77
+ results << text_content
78
+
79
+ results << '"' if formatter.quotes
80
+ results << formatter.suffix if formatter.suffix and !text.end_with?(formatter.suffix)
81
+
82
+ if !already_spanned
83
+ results << "</span>"
84
+ end
85
+
86
+ results
87
+ end
88
+
89
+ # The following performs direct text changes - should be toggled off for mark-up?
90
+ #lowercase, uppercase, capitalize-first, capitalize-all, title, sentence.
91
+ def format_text(text, textcase)
92
+ case textcase
93
+ when "lowercase"
94
+ text.downcase!
95
+ when "uppercase"
96
+ text.upcase!
97
+ when "capitalize-first"
98
+ text[0] = text[0..0].upcase
99
+ # Need to separate out these cases
100
+ when "capitalize-all", "title", "sentence"
101
+ # Tries a simple exclusion of common words from title capitalisation
102
+ # Suffers from a lack of internationalisation...
103
+ text = text.split(' ').collect{|t|
104
+ t.capitalize unless t == text.split(' ')[0] and ['and', 'the', 'a', 'of', 'by', 'to', 'at'].include?(t)
105
+ }.join(' ')
106
+ end
107
+ text
108
+ end
109
+
110
+
111
+ def delimit_node(node, elements)
112
+ results = ""
113
+ clean = elements.compact.reject{|e| e if e.to_s.strip.empty?}
114
+ len = clean.length - 1
115
+ delim = node.is_a?(Delimiter)
116
+ (0..len).each do |i|
117
+ case i
118
+ when 0
119
+ # Do nothing
120
+ when len
121
+ if delim
122
+ if node.delimiter_precedes_last and node.delimiter_precedes_last == "never"
123
+ # Still do nothing
124
+ elsif delim and node.delimiter
125
+ results << node.delimiter
126
+ end
127
+ if node.and == "text"
128
+ results.strip!
129
+ results << " and "
130
+ elsif node.and == "symbol"
131
+ results.strip!
132
+ results << " &amp; "
133
+ end
134
+ end
135
+ else
136
+ results << node.delimiter if delim and node.delimiter
137
+ end
138
+ # Finally, add the content
139
+ results << clean[i].to_s
140
+ end
141
+ results
142
+ end
143
+ end
144
+ end