citeproc-ruby 0.0.6 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.document +4 -0
- data/.gitignore +10 -0
- data/.rspec +3 -0
- data/.simplecov +4 -0
- data/.travis.yml +17 -0
- data/.yardopts +2 -0
- data/AGPL +662 -0
- data/BSDL +29 -0
- data/Gemfile +42 -0
- data/Guardfile +14 -0
- data/README.md +32 -76
- data/Rakefile +60 -0
- data/citeproc-ruby.gemspec +46 -0
- data/cucumber.yml +1 -0
- data/features/bibliography.feature +25 -0
- data/features/name_options.feature +37 -0
- data/features/names.feature +192 -0
- data/features/renderer.feature +74 -0
- data/features/sort.feature +50 -0
- data/features/step_definitions/renderer.rb +80 -0
- data/features/support/env.rb +33 -0
- data/features/support/hooks.rb +10 -0
- data/lib/citeproc/ruby.rb +32 -0
- data/lib/citeproc/ruby/engine.rb +122 -0
- data/lib/citeproc/ruby/format.rb +303 -0
- data/lib/citeproc/ruby/formats/default.rb +25 -0
- data/lib/citeproc/ruby/formats/html.rb +221 -0
- data/lib/citeproc/ruby/renderer.rb +140 -0
- data/lib/citeproc/ruby/renderer/choose.rb +106 -0
- data/lib/citeproc/ruby/renderer/date.rb +90 -0
- data/lib/citeproc/ruby/renderer/format.rb +129 -0
- data/lib/citeproc/ruby/renderer/group.rb +34 -0
- data/lib/citeproc/ruby/renderer/history.rb +40 -0
- data/lib/citeproc/ruby/renderer/label.rb +66 -0
- data/lib/citeproc/ruby/renderer/layout.rb +20 -0
- data/lib/citeproc/ruby/renderer/locale.rb +26 -0
- data/lib/citeproc/ruby/renderer/macro.rb +20 -0
- data/lib/citeproc/ruby/renderer/names.rb +401 -0
- data/lib/citeproc/ruby/renderer/number.rb +41 -0
- data/lib/citeproc/ruby/renderer/observer.rb +44 -0
- data/lib/citeproc/ruby/renderer/state.rb +96 -0
- data/lib/citeproc/ruby/renderer/text.rb +62 -0
- data/lib/citeproc/ruby/sort.rb +82 -0
- data/lib/citeproc/ruby/version.rb +5 -0
- data/spec/citeproc/ruby/engine_spec.rb +94 -0
- data/spec/citeproc/ruby/formats/default_spec.rb +159 -0
- data/spec/citeproc/ruby/formats/html_spec.rb +162 -0
- data/spec/citeproc/ruby/renderer/choose_spec.rb +293 -0
- data/spec/citeproc/ruby/renderer/date_spec.rb +173 -0
- data/spec/citeproc/ruby/renderer/group_spec.rb +88 -0
- data/spec/citeproc/ruby/renderer/history_spec.rb +38 -0
- data/spec/citeproc/ruby/renderer/label_spec.rb +225 -0
- data/spec/citeproc/ruby/renderer/layout_spec.rb +41 -0
- data/spec/citeproc/ruby/renderer/macro_spec.rb +31 -0
- data/spec/citeproc/ruby/renderer/names_spec.rb +396 -0
- data/spec/citeproc/ruby/renderer/number_spec.rb +120 -0
- data/spec/citeproc/ruby/renderer/text_spec.rb +120 -0
- data/spec/citeproc/ruby/renderer_spec.rb +65 -0
- data/spec/fixtures/items.rb +80 -0
- data/{resource/locale → spec/fixtures/locales}/locales-en-US.xml +2 -11
- data/{resource/locale → spec/fixtures/locales}/locales-fr-FR.xml +77 -66
- data/{resource/style → spec/fixtures/styles}/apa.csl +5 -5
- data/spec/spec_helper.rb +67 -14
- metadata +121 -211
- data/lib/citeproc.rb +0 -100
- data/lib/citeproc/bibliography.rb +0 -57
- data/lib/citeproc/data.rb +0 -149
- data/lib/citeproc/date.rb +0 -133
- data/lib/citeproc/formatter.rb +0 -38
- data/lib/citeproc/item.rb +0 -53
- data/lib/citeproc/name.rb +0 -284
- data/lib/citeproc/processor.rb +0 -166
- data/lib/citeproc/selector.rb +0 -61
- data/lib/citeproc/variable.rb +0 -82
- data/lib/citeproc/version.rb +0 -3
- data/lib/csl/locale.rb +0 -223
- data/lib/csl/node.rb +0 -72
- data/lib/csl/nodes.rb +0 -1418
- data/lib/csl/renderer.rb +0 -88
- data/lib/csl/sort.rb +0 -61
- data/lib/csl/style.rb +0 -110
- data/lib/csl/term.rb +0 -124
- data/lib/extensions/core.rb +0 -43
- data/lib/plugins/filters/bibtex.rb +0 -12
- data/lib/plugins/formats/default.rb +0 -134
- data/lib/plugins/formats/html.rb +0 -67
- data/lib/support/attributes.rb +0 -99
- data/lib/support/compatibility.rb +0 -83
- data/lib/support/tree.rb +0 -80
- data/resource/locale/locales-af-ZA.xml +0 -305
- data/resource/locale/locales-ar-AR.xml +0 -306
- data/resource/locale/locales-bg-BG.xml +0 -305
- data/resource/locale/locales-ca-AD.xml +0 -305
- data/resource/locale/locales-cs-CZ.xml +0 -305
- data/resource/locale/locales-da-DK.xml +0 -305
- data/resource/locale/locales-de-AT.xml +0 -304
- data/resource/locale/locales-de-CH.xml +0 -304
- data/resource/locale/locales-de-DE.xml +0 -332
- data/resource/locale/locales-el-GR.xml +0 -305
- data/resource/locale/locales-en-GB.xml +0 -304
- data/resource/locale/locales-es-ES.xml +0 -305
- data/resource/locale/locales-et-EE.xml +0 -304
- data/resource/locale/locales-eu.xml +0 -305
- data/resource/locale/locales-fa-IR.xml +0 -304
- data/resource/locale/locales-fi-FI.xml +0 -304
- data/resource/locale/locales-fr-CA.xml +0 -306
- data/resource/locale/locales-he-IL.xml +0 -304
- data/resource/locale/locales-hu-HU.xml +0 -305
- data/resource/locale/locales-is-IS.xml +0 -304
- data/resource/locale/locales-it-IT.xml +0 -305
- data/resource/locale/locales-ja-JP.xml +0 -305
- data/resource/locale/locales-kh-KH.xml +0 -303
- data/resource/locale/locales-km-KH.xml +0 -304
- data/resource/locale/locales-ko-KR.xml +0 -305
- data/resource/locale/locales-mn-MN.xml +0 -306
- data/resource/locale/locales-nb-NO.xml +0 -304
- data/resource/locale/locales-nl-NL.xml +0 -304
- data/resource/locale/locales-nn-NO.xml +0 -304
- data/resource/locale/locales-pl-PL.xml +0 -305
- data/resource/locale/locales-pt-BR.xml +0 -304
- data/resource/locale/locales-pt-PT.xml +0 -305
- data/resource/locale/locales-ro-RO.xml +0 -305
- data/resource/locale/locales-ru-RU.xml +0 -306
- data/resource/locale/locales-sk-SK.xml +0 -304
- data/resource/locale/locales-sl-SI.xml +0 -305
- data/resource/locale/locales-sr-RS.xml +0 -305
- data/resource/locale/locales-sv-SE.xml +0 -305
- data/resource/locale/locales-th-TH.xml +0 -304
- data/resource/locale/locales-tr-TR.xml +0 -305
- data/resource/locale/locales-uk-UA.xml +0 -306
- data/resource/locale/locales-vi-VN.xml +0 -305
- data/resource/locale/locales-zh-CN.xml +0 -304
- data/resource/locale/locales-zh-TW.xml +0 -305
- data/resource/schema/csl-categories.rnc +0 -39
- data/resource/schema/csl-data.rnc +0 -98
- data/resource/schema/csl-terms.rnc +0 -106
- data/resource/schema/csl-types.rnc +0 -39
- data/resource/schema/csl-variables.rnc +0 -182
- data/resource/schema/csl.rnc +0 -941
- data/resource/style/bibtex.csl +0 -177
- data/resource/style/chicago-annotated-bibliography.csl +0 -513
- data/resource/style/chicago-author-date-basque.csl +0 -707
- data/resource/style/chicago-author-date-de.csl +0 -394
- data/resource/style/chicago-author-date-listing.csl +0 -434
- data/resource/style/chicago-author-date.csl +0 -425
- data/resource/style/chicago-dated-note-biblio-no-ibid.csl +0 -472
- data/resource/style/chicago-fullnote-bibliography-bb.csl +0 -928
- data/resource/style/chicago-fullnote-bibliography-delimiter-fixes.csl +0 -972
- data/resource/style/chicago-fullnote-bibliography-no-ibid-delimiter-fixes.csl +0 -963
- data/resource/style/chicago-fullnote-bibliography-no-ibid.csl +0 -785
- data/resource/style/chicago-fullnote-bibliography.csl +0 -803
- data/resource/style/chicago-library-list.csl +0 -511
- data/resource/style/chicago-note-biblio-no-ibid.csl +0 -514
- data/resource/style/chicago-note-bibliography.csl +0 -530
- data/resource/style/chicago-note.csl +0 -388
- data/resource/style/chicago-quick-copy.csl +0 -685
- data/resource/style/ieee.csl +0 -299
- data/resource/style/mla-notes.csl +0 -796
- data/resource/style/mla-underline.csl +0 -175
- data/resource/style/mla-url.csl +0 -214
- data/resource/style/mla.csl +0 -394
- data/resource/style/vancouver-brackets.csl +0 -256
- data/resource/style/vancouver-superscript-bracket-only-year.csl +0 -165
- data/resource/style/vancouver-superscript.csl +0 -256
- data/resource/style/vancouver.csl +0 -256
- data/spec/citeproc/bibliography_spec.rb +0 -45
- data/spec/citeproc/citeproc_spec.rb +0 -80
- data/spec/citeproc/date_spec.rb +0 -89
- data/spec/citeproc/formatter_spec.rb +0 -101
- data/spec/citeproc/item_spec.rb +0 -71
- data/spec/citeproc/name_spec.rb +0 -30
- data/spec/citeproc/processor_spec.rb +0 -61
- data/spec/citeproc/selector_spec.rb +0 -82
- data/spec/citeproc/variable_spec.rb +0 -69
- data/spec/csl/locale_spec.rb +0 -208
- data/spec/csl/node_spec.rb +0 -25
- data/spec/csl/nodes_spec.rb +0 -145
- data/spec/csl/style_spec.rb +0 -62
- data/spec/csl/term_spec.rb +0 -56
- data/spec/fixtures/dates.yaml +0 -80
- data/spec/fixtures/names.yaml +0 -115
- data/spec/fixtures/nodes.yaml +0 -245
- data/spec/support/attributes_spec.rb +0 -39
- data/spec/support/tree_spec.rb +0 -163
@@ -0,0 +1,74 @@
|
|
1
|
+
Feature: Rendering CSL nodes
|
2
|
+
As a hacker of cite processors
|
3
|
+
I want to render citation items
|
4
|
+
With selected CSL nodes
|
5
|
+
|
6
|
+
Scenario: Simple Date Rendering
|
7
|
+
Given the following style node:
|
8
|
+
"""
|
9
|
+
<date variable="issued">
|
10
|
+
<date-part name="year"/>
|
11
|
+
</date>
|
12
|
+
"""
|
13
|
+
When I render the following citation items as "text":
|
14
|
+
| issued |
|
15
|
+
| November 7, 2006 |
|
16
|
+
| 2014-01-01 |
|
17
|
+
| 1999 |
|
18
|
+
Then the results should be:
|
19
|
+
| 2006 |
|
20
|
+
| 2014 |
|
21
|
+
| 1999 |
|
22
|
+
|
23
|
+
Scenario: Date Group Rendering
|
24
|
+
Given the following style node:
|
25
|
+
"""
|
26
|
+
<group prefix="(" suffix=").">
|
27
|
+
<date variable="issued">
|
28
|
+
<date-part name="year"/>
|
29
|
+
</date>
|
30
|
+
<text variable="year-suffix"/>
|
31
|
+
</group>
|
32
|
+
"""
|
33
|
+
When I render the following citation items as "text":
|
34
|
+
| issued | year-suffix |
|
35
|
+
| November 7, 2006 | |
|
36
|
+
| 2014-01-01 | |
|
37
|
+
| 1999 | a |
|
38
|
+
Then the results should be:
|
39
|
+
| (2006). |
|
40
|
+
| (2014). |
|
41
|
+
| (1999a). |
|
42
|
+
|
43
|
+
@html @formatting
|
44
|
+
Scenario: Formatted Groups
|
45
|
+
Given the following style node:
|
46
|
+
"""
|
47
|
+
<group>
|
48
|
+
<group suffix=" " font-weight="bold">
|
49
|
+
<!--group formatting is pushed down-->
|
50
|
+
<text variable="volume" suffix=","/>
|
51
|
+
</group>
|
52
|
+
<text variable="page" suffix="," font-weight="bold"/>
|
53
|
+
</group>
|
54
|
+
"""
|
55
|
+
When I render the following citation item as "html":
|
56
|
+
| volume | 5 |
|
57
|
+
| page | 23 |
|
58
|
+
Then the result should be: <b>5</b>, <b>23</b>,
|
59
|
+
|
60
|
+
Scenario: Page labels
|
61
|
+
Given the following style node:
|
62
|
+
"""
|
63
|
+
<label variable="page"/>
|
64
|
+
"""
|
65
|
+
When I render the following citation items as "text":
|
66
|
+
| page |
|
67
|
+
| 23 |
|
68
|
+
| |
|
69
|
+
| 23, 34 |
|
70
|
+
Then the results should be:
|
71
|
+
| page |
|
72
|
+
| |
|
73
|
+
| pages |
|
74
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
Feature: Sorting
|
2
|
+
As a hacker of cite processors
|
3
|
+
I want to sort citation items
|
4
|
+
According to the rules of a CSL style
|
5
|
+
|
6
|
+
Scenario: Name sorting
|
7
|
+
Given the following sort keys:
|
8
|
+
"""
|
9
|
+
<sort>
|
10
|
+
<key variable="author"/>
|
11
|
+
</sort>
|
12
|
+
"""
|
13
|
+
When I sort the following items:
|
14
|
+
| author |
|
15
|
+
| Edelweis |
|
16
|
+
| ANZ-Group |
|
17
|
+
| Aardvaark |
|
18
|
+
Then the order should be:
|
19
|
+
| ID-2 |
|
20
|
+
| ID-1 |
|
21
|
+
| ID-0 |
|
22
|
+
|
23
|
+
Scenario: Name sorting macros
|
24
|
+
Given the following sort keys:
|
25
|
+
"""
|
26
|
+
<sort>
|
27
|
+
<key macro="author"/>
|
28
|
+
</sort>
|
29
|
+
"""
|
30
|
+
And the following macro:
|
31
|
+
"""
|
32
|
+
<macro name="author">
|
33
|
+
<names variable="author">
|
34
|
+
<name name-as-sort-order="all" and="symbol" sort-separator=", "
|
35
|
+
initialize-with="." delimiter-precedes-last="never" delimiter=", "/>
|
36
|
+
<label form="short" prefix=" (" suffix=".)" text-case="capitalize-first"/>
|
37
|
+
<substitute>
|
38
|
+
<names variable="editor"/>
|
39
|
+
<text value="Anon"/>
|
40
|
+
</substitute>
|
41
|
+
</names>
|
42
|
+
</macro>
|
43
|
+
"""
|
44
|
+
When I sort the following items:
|
45
|
+
| author |
|
46
|
+
| ABC |
|
47
|
+
| Aaa |
|
48
|
+
Then the order should be:
|
49
|
+
| ID-1 |
|
50
|
+
| ID-0 |
|
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
Given(/^the "(.*?)" style's (bibliography|citation) node$/) do |style, mode|
|
3
|
+
@node = CSL::Style.load(style).send(mode).layout
|
4
|
+
end
|
5
|
+
|
6
|
+
Given(/^the following style node:$/) do |string|
|
7
|
+
@node = CSL.parse!(string, CSL::Style)
|
8
|
+
end
|
9
|
+
|
10
|
+
Given(/^the following sort keys:$/) do |string|
|
11
|
+
@sort = CSL.parse!(string, CSL::Style)
|
12
|
+
end
|
13
|
+
|
14
|
+
Given(/^the following macro:$/) do |string|
|
15
|
+
@macro = CSL.parse!(string, CSL::Style)
|
16
|
+
end
|
17
|
+
|
18
|
+
When(/^I render the following citation items as "(.*?)":$/) do |format, items|
|
19
|
+
r = CiteProc::Ruby::Renderer.new(:format => format)
|
20
|
+
|
21
|
+
@results = items.hashes.map.with_index do |data, idx|
|
22
|
+
i = CiteProc::CitationItem.new(:id => "ID-#{idx}")
|
23
|
+
|
24
|
+
data[:id] = "ID-#{idx}"
|
25
|
+
i.data = CiteProc::Item.new(data)
|
26
|
+
|
27
|
+
r.render i, @node
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
When(/^I render the following citation item as "(.*?)":$/) do |format, item|
|
32
|
+
r = CiteProc::Ruby::Renderer.new(:format => format)
|
33
|
+
|
34
|
+
i = CiteProc::CitationItem.new(:id => 'ID-1')
|
35
|
+
i.data = CiteProc::Item.new(item.rows_hash.merge(:id => 'ID-1'))
|
36
|
+
|
37
|
+
@result = r.render i, @node
|
38
|
+
end
|
39
|
+
|
40
|
+
When(/^I sort the following items:$/) do |items|
|
41
|
+
engine = CiteProc::Ruby::Engine.new
|
42
|
+
|
43
|
+
@order = items.hashes.map.with_index do |data, idx|
|
44
|
+
data[:id] = "ID-#{idx}"
|
45
|
+
CiteProc::Item.new(data)
|
46
|
+
end
|
47
|
+
|
48
|
+
unless @macro.nil?
|
49
|
+
@sort.each_child do |key|
|
50
|
+
key.stub(:macro).and_return(@macro)
|
51
|
+
key.stub(:macro?).and_return(true)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
engine.sort! @order, @sort.children
|
56
|
+
end
|
57
|
+
|
58
|
+
Then(/^the results should be:$/) do |expected|
|
59
|
+
expected = expected.raw.map(&:first)
|
60
|
+
|
61
|
+
@results.length.should == expected.length
|
62
|
+
|
63
|
+
@results.zip(expected).each do |result, expectation|
|
64
|
+
result.should == expectation
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
Then(/^the result should be: (.*)$/) do |expected|
|
69
|
+
@result.should == expected
|
70
|
+
end
|
71
|
+
|
72
|
+
Then(/^the order should be:$/) do |expected|
|
73
|
+
expected = expected.raw.map(&:first)
|
74
|
+
|
75
|
+
@order.length.should == expected.length
|
76
|
+
|
77
|
+
@order.zip(expected).each do |order, expectation|
|
78
|
+
order['id'].should == expectation
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
begin
|
2
|
+
require 'simplecov'
|
3
|
+
require 'coveralls' if ENV['CI']
|
4
|
+
rescue LoadError
|
5
|
+
# ignore
|
6
|
+
end
|
7
|
+
|
8
|
+
begin
|
9
|
+
case
|
10
|
+
when RUBY_PLATFORM < 'java'
|
11
|
+
require 'debug'
|
12
|
+
Debugger.start
|
13
|
+
when defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
|
14
|
+
require 'rubinius/debugger'
|
15
|
+
else
|
16
|
+
require 'debugger'
|
17
|
+
end
|
18
|
+
rescue LoadError
|
19
|
+
# ignore
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'cucumber/rspec/doubles'
|
23
|
+
require 'citeproc/ruby'
|
24
|
+
|
25
|
+
module Fixtures
|
26
|
+
PATH = File.expand_path('../../../spec/fixtures', __FILE__)
|
27
|
+
|
28
|
+
Dir[File.join(PATH, '*.rb')].each do |fixture|
|
29
|
+
require fixture
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
World(Fixtures)
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Before do
|
2
|
+
@style_root, @locale_root = CSL::Style.root, CSL::Locale.root
|
3
|
+
|
4
|
+
CSL::Style.root = File.join(Fixtures::PATH, 'styles')
|
5
|
+
CSL::Locale.root = File.join(Fixtures::PATH, 'locales')
|
6
|
+
end
|
7
|
+
|
8
|
+
After do
|
9
|
+
CSL::Style.root, CSL::Locale.root = @style_root, @locale_root
|
10
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'observer'
|
2
|
+
|
3
|
+
require 'csl'
|
4
|
+
require 'citeproc'
|
5
|
+
|
6
|
+
require 'citeproc/ruby/version'
|
7
|
+
|
8
|
+
require 'citeproc/ruby/format'
|
9
|
+
|
10
|
+
require 'citeproc/ruby/formats/default'
|
11
|
+
require 'citeproc/ruby/formats/html'
|
12
|
+
|
13
|
+
require 'citeproc/ruby/renderer'
|
14
|
+
|
15
|
+
require 'citeproc/ruby/renderer/locale'
|
16
|
+
require 'citeproc/ruby/renderer/format'
|
17
|
+
require 'citeproc/ruby/renderer/observer'
|
18
|
+
require 'citeproc/ruby/renderer/state'
|
19
|
+
require 'citeproc/ruby/renderer/history'
|
20
|
+
|
21
|
+
require 'citeproc/ruby/renderer/text'
|
22
|
+
require 'citeproc/ruby/renderer/number'
|
23
|
+
require 'citeproc/ruby/renderer/label'
|
24
|
+
require 'citeproc/ruby/renderer/date'
|
25
|
+
require 'citeproc/ruby/renderer/names'
|
26
|
+
require 'citeproc/ruby/renderer/layout'
|
27
|
+
require 'citeproc/ruby/renderer/macro'
|
28
|
+
require 'citeproc/ruby/renderer/group'
|
29
|
+
require 'citeproc/ruby/renderer/choose'
|
30
|
+
|
31
|
+
require 'citeproc/ruby/sort.rb'
|
32
|
+
require 'citeproc/ruby/engine.rb'
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module CiteProc
|
2
|
+
module Ruby
|
3
|
+
|
4
|
+
class Engine < CiteProc::Engine
|
5
|
+
|
6
|
+
include SortItems
|
7
|
+
|
8
|
+
@name = 'citeproc-ruby'.freeze
|
9
|
+
@type = 'CSL'.freeze
|
10
|
+
@version = CSL::Schema.version
|
11
|
+
@priority = 1
|
12
|
+
|
13
|
+
attr_reader :renderer, :style
|
14
|
+
|
15
|
+
def_delegators :renderer,
|
16
|
+
:format, :format=, :locale, :locale=
|
17
|
+
|
18
|
+
def initialize(*arguments)
|
19
|
+
super(*arguments)
|
20
|
+
@renderer = Renderer.new(self)
|
21
|
+
|
22
|
+
update! unless processor.nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def style=(new_style)
|
26
|
+
@style = CSL::Style.load new_style
|
27
|
+
end
|
28
|
+
|
29
|
+
def process(data)
|
30
|
+
node = style.citation
|
31
|
+
|
32
|
+
return unless node
|
33
|
+
return '' if data.empty?
|
34
|
+
|
35
|
+
# populate item data
|
36
|
+
data.each do |item|
|
37
|
+
item.data = processor[item.id].dup
|
38
|
+
end
|
39
|
+
|
40
|
+
# TODO implement sort in citation data
|
41
|
+
sort!(data, node.sort_keys) unless !node.sort?
|
42
|
+
|
43
|
+
# TODO citation number (after sorting?)
|
44
|
+
|
45
|
+
renderer.render_citation data, node
|
46
|
+
end
|
47
|
+
|
48
|
+
def append
|
49
|
+
raise NotImplementedByEngine
|
50
|
+
end
|
51
|
+
|
52
|
+
def bibliography(selector)
|
53
|
+
node = style.bibliography
|
54
|
+
return unless node
|
55
|
+
|
56
|
+
selection = processor.data.select do |item|
|
57
|
+
selector.matches?(item) && !selector.skip?(item)
|
58
|
+
end
|
59
|
+
|
60
|
+
sort!(selection, node.sort_keys) unless selection.empty? || !node.sort?
|
61
|
+
|
62
|
+
Bibliography.new(node.bibliography_options) do |bib|
|
63
|
+
format.bibliography(bib)
|
64
|
+
|
65
|
+
idx = 1
|
66
|
+
|
67
|
+
selection.each do |item|
|
68
|
+
begin
|
69
|
+
bib.push item.id, renderer.render_bibliography(item.cite(idx), node)
|
70
|
+
rescue => error
|
71
|
+
bib.errors << [item.id.to_s, error]
|
72
|
+
ensure
|
73
|
+
idx += 1 unless error
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def update_items
|
80
|
+
raise NotImplementedByEngine
|
81
|
+
end
|
82
|
+
|
83
|
+
def update_uncited_items
|
84
|
+
raise NotImplementedByEngine
|
85
|
+
end
|
86
|
+
|
87
|
+
# @return [String, Array<String>]
|
88
|
+
def render(mode, data)
|
89
|
+
case mode
|
90
|
+
when :bibliography
|
91
|
+
node = style.bibliography
|
92
|
+
|
93
|
+
data.map do |item|
|
94
|
+
item.data = processor[item.id].dup
|
95
|
+
renderer.render item, node
|
96
|
+
end
|
97
|
+
|
98
|
+
when :citation
|
99
|
+
node = style.citation
|
100
|
+
|
101
|
+
data.each do |item|
|
102
|
+
item.data = processor[item.id].dup
|
103
|
+
end
|
104
|
+
|
105
|
+
renderer.render_citation data, node
|
106
|
+
|
107
|
+
else
|
108
|
+
raise ArgumentError, "cannot render unknown mode: #{mode.inspect}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
def update!
|
114
|
+
renderer.format = processor.options[:format]
|
115
|
+
renderer.locale = processor.options[:locale]
|
116
|
+
|
117
|
+
@style = CSL::Style.load processor.options[:style]
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,303 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module CiteProc
|
4
|
+
module Ruby
|
5
|
+
|
6
|
+
class Format
|
7
|
+
|
8
|
+
@available = []
|
9
|
+
|
10
|
+
@squeezable = /^[\s\.,:;!?\)\(\[\]]+$/
|
11
|
+
|
12
|
+
@stopwords = {
|
13
|
+
:en => [
|
14
|
+
'about', 'above', 'across', 'afore', 'after', 'against', 'along',
|
15
|
+
'alongside', 'amid', 'amidst', 'among', 'amongst', 'anenst', 'apropos',
|
16
|
+
'apud', 'around', 'as', 'aside', 'astride', 'at', 'athwart', 'atop',
|
17
|
+
'barring', 'before', 'behind', 'below', 'beneath', 'beside', 'besides',
|
18
|
+
'between', 'beyond', 'but', 'by', 'circa', 'despite', 'd', 'down', 'during',
|
19
|
+
'except', 'for', 'forenenst', 'from', 'given', 'in', 'inside', 'into',
|
20
|
+
'lest', 'like', 'modulo' 'near', 'next', 'notwithstanding', 'of', 'off',
|
21
|
+
'on', 'onto', 'out', 'over', 'per', 'plus', 'pro', 'qua', 'sans', 'since',
|
22
|
+
'than', 'through', 'thru', 'throughout', 'thruout', 'till', 'to', 'toward',
|
23
|
+
'towards', 'under', 'underneath', 'until', 'unto', 'up', 'upon', 'versus',
|
24
|
+
'vs', 'v', 'via', 'vis-à-vis', 'with', 'within', 'without'
|
25
|
+
]
|
26
|
+
}
|
27
|
+
|
28
|
+
class << self
|
29
|
+
attr_reader :available, :stopwords
|
30
|
+
|
31
|
+
def inherited(base)
|
32
|
+
Format.available << base
|
33
|
+
end
|
34
|
+
|
35
|
+
def load(name = nil)
|
36
|
+
return new unless name
|
37
|
+
return name if name.is_a?(Format)
|
38
|
+
|
39
|
+
name = name.to_s.downcase
|
40
|
+
|
41
|
+
klass = available.detect do |format|
|
42
|
+
format.name.split('::')[-1].downcase == name
|
43
|
+
end
|
44
|
+
|
45
|
+
raise(Error, "unknown format: #{name}") unless klass
|
46
|
+
|
47
|
+
klass.new
|
48
|
+
end
|
49
|
+
|
50
|
+
def stopword?(word, locale = :en)
|
51
|
+
return unless stopwords.key?(locale)
|
52
|
+
stopwords[locale].include?(word.downcase)
|
53
|
+
end
|
54
|
+
|
55
|
+
def squeezable?(string)
|
56
|
+
squeezable === string
|
57
|
+
end
|
58
|
+
|
59
|
+
def squeezable
|
60
|
+
@squeezable ||= Format.squeezable
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
attr_reader :locale
|
65
|
+
|
66
|
+
def keys
|
67
|
+
@keys ||= (CSL::Schema.attr(:formatting) - [:prefix, :suffix, :display])
|
68
|
+
end
|
69
|
+
|
70
|
+
def squeezable?(string)
|
71
|
+
self.class.squeezable?(string)
|
72
|
+
end
|
73
|
+
|
74
|
+
def squeeze_suffix(string, suffix)
|
75
|
+
raise ArgumentError unless string.is_a?(::String)
|
76
|
+
raise ArgumentError unless suffix.is_a?(::String)
|
77
|
+
|
78
|
+
return string.dup if suffix.empty?
|
79
|
+
return suffix.dup if string.empty?
|
80
|
+
|
81
|
+
string, stripped = strip(string)
|
82
|
+
string, quotes = split_closing_quotes(string)
|
83
|
+
|
84
|
+
suffix = decode_entities(suffix)
|
85
|
+
|
86
|
+
suffix = suffix.each_char.drop_while.with_index { |c, i|
|
87
|
+
squeezable?(c) && string.end_with?(suffix[0, i + 1])
|
88
|
+
}.join('')
|
89
|
+
|
90
|
+
# Handle special cases like ?. or ;.
|
91
|
+
if suffix.start_with?('.') && string.end_with?(';', ',', '!', '?', ':')
|
92
|
+
suffix = suffix[1..-1]
|
93
|
+
end
|
94
|
+
|
95
|
+
# Handle special cases ;, and :,
|
96
|
+
if suffix.start_with?(',') && string.end_with?(';', ':')
|
97
|
+
suffix = suffix[1..-1]
|
98
|
+
end
|
99
|
+
|
100
|
+
# Handle special cases ,; and :;
|
101
|
+
if suffix.start_with?(';') && string.end_with?(',', ':')
|
102
|
+
suffix = suffix[1..-1]
|
103
|
+
end
|
104
|
+
|
105
|
+
# Handle punctiation-in-quote
|
106
|
+
if !quotes.nil? && punctuation_in_quotes?
|
107
|
+
if suffix.sub!(/^([\.,])/, '')
|
108
|
+
punctuation = ($1).to_s
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
"#{string}#{punctuation}#{quotes}#{stripped}#{suffix}"
|
113
|
+
end
|
114
|
+
|
115
|
+
def squeeze_prefix(string, prefix)
|
116
|
+
raise ArgumentError unless string.is_a?(::String)
|
117
|
+
raise ArgumentError unless prefix.is_a?(::String)
|
118
|
+
|
119
|
+
prefix = prefix.reverse.each_char.drop_while.with_index { |c, i|
|
120
|
+
squeezable?(c) && string.start_with?(prefix[-(i + 1) .. -1])
|
121
|
+
}.join('').reverse
|
122
|
+
|
123
|
+
"#{prefix}#{string}"
|
124
|
+
end
|
125
|
+
|
126
|
+
alias concat squeeze_suffix
|
127
|
+
|
128
|
+
def join(list, delimiter = nil)
|
129
|
+
raise ArgumentError unless list.is_a?(Enumerable)
|
130
|
+
return '' if list.length.zero?
|
131
|
+
return list[0] if list.length == 1
|
132
|
+
|
133
|
+
if delimiter.nil? || delimiter.empty?
|
134
|
+
list.inject do |m, n|
|
135
|
+
concat(m, n)
|
136
|
+
end
|
137
|
+
else
|
138
|
+
list.inject do |m, n|
|
139
|
+
concat(concat(m, delimiter), n)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def bibliography(bibliography, locale = nil)
|
145
|
+
bibliography.connector = "\n" * bibliography.entry_spacing
|
146
|
+
bibliography
|
147
|
+
end
|
148
|
+
|
149
|
+
def apply(input, node, locale = nil)
|
150
|
+
return '' if input.nil?
|
151
|
+
return input if input.empty? || node.nil?
|
152
|
+
|
153
|
+
return ArgumentError unless node.respond_to?(:formatting_options)
|
154
|
+
|
155
|
+
|
156
|
+
@input, @output, @node, @locale = input, input.dup, node, locale
|
157
|
+
|
158
|
+
setup!
|
159
|
+
|
160
|
+
# NB: Layout nodes apply formatting to
|
161
|
+
# affixes; all other nodes do not!
|
162
|
+
if node.is_a? CSL::Style::Layout
|
163
|
+
apply_prefix if options.key?(:prefix)
|
164
|
+
apply_suffix if options.key?(:suffix)
|
165
|
+
end
|
166
|
+
|
167
|
+
keys.each do |format|
|
168
|
+
if options.key?(format)
|
169
|
+
method = "apply_#{format}".tr('-', '_')
|
170
|
+
send method if respond_to?(method)
|
171
|
+
end
|
172
|
+
end unless options.empty?
|
173
|
+
|
174
|
+
output.gsub!(/\.+/, '') if node.strip_periods?
|
175
|
+
|
176
|
+
apply_quotes if node.quotes? && !locale.nil?
|
177
|
+
|
178
|
+
finalize_content!
|
179
|
+
|
180
|
+
unless node.is_a? CSL::Style::Layout
|
181
|
+
apply_prefix if options.key?(:prefix)
|
182
|
+
apply_suffix if options.key?(:suffix)
|
183
|
+
end
|
184
|
+
|
185
|
+
apply_display if options.key?(:display)
|
186
|
+
|
187
|
+
finalize!
|
188
|
+
|
189
|
+
output
|
190
|
+
ensure
|
191
|
+
cleanup!
|
192
|
+
end
|
193
|
+
|
194
|
+
def escape_quotes?
|
195
|
+
false
|
196
|
+
end
|
197
|
+
|
198
|
+
def close_quote
|
199
|
+
locale && locale.t('close-quote') || '"'
|
200
|
+
end
|
201
|
+
|
202
|
+
def close_inner_quote
|
203
|
+
locale && locale.t('close-inner-quote') || "'"
|
204
|
+
end
|
205
|
+
|
206
|
+
def split_closing_quotes(string)
|
207
|
+
string.split(/([#{close_inner_quote}#{close_quote}]+)$/, 2)
|
208
|
+
end
|
209
|
+
|
210
|
+
def apply_quotes
|
211
|
+
output.replace locale.quote(output, escape_quotes?)
|
212
|
+
end
|
213
|
+
|
214
|
+
def apply_text_case
|
215
|
+
case options[:'text-case']
|
216
|
+
when 'lowercase'
|
217
|
+
output.replace CiteProc.downcase output
|
218
|
+
|
219
|
+
when 'uppercase'
|
220
|
+
output.replace CiteProc.upcase output
|
221
|
+
|
222
|
+
when 'capitalize-first'
|
223
|
+
output.sub!(/^([^\p{L}]*)(\p{Ll})/) { "#{$1}#{CiteProc.upcase($2)}" }
|
224
|
+
|
225
|
+
when 'capitalize-all'
|
226
|
+
output.gsub!(/\b(\p{Ll})/) { CiteProc.upcase($1) }
|
227
|
+
|
228
|
+
when 'sentence'
|
229
|
+
output.sub!(/^([^\p{L}]*)(\p{Ll})/) { "#{$1}#{CiteProc.upcase($2)}" }
|
230
|
+
output.gsub!(/\b(\p{Lu})(\p{Lu}+)\b/) { "#{$1}#{CiteProc.downcase($2)}" }
|
231
|
+
|
232
|
+
when 'title'
|
233
|
+
return if locale && locale.language != :en
|
234
|
+
|
235
|
+
# TODO add support for stop words consisting of multiple words
|
236
|
+
#output.gsub!(/\b(\p{Lu})(\p{Lu}+)\b/) { "#{$1}#{CiteProc.downcase($2)}" }
|
237
|
+
|
238
|
+
# TODO exceptions: first, last word; followed by colon
|
239
|
+
output.gsub!(/\b(\p{Ll})(\p{L}+)\b/) do |word|
|
240
|
+
if Format.stopword?(word)
|
241
|
+
word
|
242
|
+
else
|
243
|
+
"#{CiteProc.upcase($1)}#{$2}"
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def punctuation_in_quotes?
|
251
|
+
!locale.nil? && locale.punctuation_in_quotes?
|
252
|
+
end
|
253
|
+
|
254
|
+
def apply_prefix
|
255
|
+
output.replace(squeeze_prefix(output, prefix))
|
256
|
+
end
|
257
|
+
|
258
|
+
def apply_suffix
|
259
|
+
output.replace(squeeze_suffix(output, suffix))
|
260
|
+
end
|
261
|
+
|
262
|
+
def prefix
|
263
|
+
options[:prefix].to_s
|
264
|
+
end
|
265
|
+
|
266
|
+
def suffix
|
267
|
+
options[:suffix].to_s
|
268
|
+
end
|
269
|
+
|
270
|
+
def strip(string)
|
271
|
+
string
|
272
|
+
end
|
273
|
+
|
274
|
+
protected
|
275
|
+
|
276
|
+
attr_reader :input, :output, :node
|
277
|
+
|
278
|
+
def options
|
279
|
+
@options ||= @node.formatting_options
|
280
|
+
end
|
281
|
+
|
282
|
+
def finalize!
|
283
|
+
end
|
284
|
+
|
285
|
+
def finalize_content!
|
286
|
+
end
|
287
|
+
|
288
|
+
def setup!
|
289
|
+
end
|
290
|
+
|
291
|
+
def decode_entities(string)
|
292
|
+
string.gsub(/&#x([0-9a-f]);/i) do
|
293
|
+
[Integer("0x#{$1}")].pack('U')
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
def cleanup!
|
298
|
+
@input, @output, @node, @options = nil
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
end
|
303
|
+
end
|