csl 1.0.0.pre1
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/.document +5 -0
- data/.gitignore +8 -0
- data/.gitmodules +6 -0
- data/.rspec +3 -0
- data/.simplecov +2 -0
- data/.travis.yml +13 -0
- data/.yardopts +2 -0
- data/AGPL +662 -0
- data/BSDL +29 -0
- data/Gemfile +24 -0
- data/Guardfile +14 -0
- data/README.md +39 -0
- data/Rakefile +45 -0
- data/csl.gemspec +36 -0
- data/cucumber.yml +1 -0
- data/features/locales/loading.feature +57 -0
- data/features/locales/ordinalize.feature +861 -0
- data/features/parser/info.feature +27 -0
- data/features/parser/localized_dates.feature +35 -0
- data/features/parser/terms.feature +28 -0
- data/features/step_definitions/locale_steps.rb +34 -0
- data/features/step_definitions/parser_steps.rb +28 -0
- data/features/step_definitions/style_steps.rb +16 -0
- data/features/style/loading.feature +53 -0
- data/features/support/env.rb +8 -0
- data/lib/csl.rb +54 -0
- data/lib/csl/compatibility.rb +19 -0
- data/lib/csl/errors.rb +15 -0
- data/lib/csl/extensions.rb +63 -0
- data/lib/csl/info.rb +40 -0
- data/lib/csl/loader.rb +78 -0
- data/lib/csl/locale.rb +393 -0
- data/lib/csl/locale/date.rb +48 -0
- data/lib/csl/locale/style_options.rb +10 -0
- data/lib/csl/locale/term.rb +185 -0
- data/lib/csl/node.rb +285 -0
- data/lib/csl/parser.rb +92 -0
- data/lib/csl/pretty_printer.rb +33 -0
- data/lib/csl/schema.rb +109 -0
- data/lib/csl/style.rb +53 -0
- data/lib/csl/style/bibliography.rb +15 -0
- data/lib/csl/style/citation.rb +17 -0
- data/lib/csl/style/conditional.rb +11 -0
- data/lib/csl/style/date.rb +16 -0
- data/lib/csl/style/group.rb +9 -0
- data/lib/csl/style/label.rb +14 -0
- data/lib/csl/style/layout.rb +10 -0
- data/lib/csl/style/macro.rb +9 -0
- data/lib/csl/style/names.rb +54 -0
- data/lib/csl/style/number.rb +33 -0
- data/lib/csl/style/sort.rb +21 -0
- data/lib/csl/style/text.rb +10 -0
- data/lib/csl/treelike.rb +442 -0
- data/lib/csl/version.rb +3 -0
- data/spec/csl/info_spec.rb +116 -0
- data/spec/csl/locale/date_spec.rb +63 -0
- data/spec/csl/locale/style_options_spec.rb +19 -0
- data/spec/csl/locale/term_spec.rb +96 -0
- data/spec/csl/locale_spec.rb +128 -0
- data/spec/csl/node_spec.rb +100 -0
- data/spec/csl/parser_spec.rb +92 -0
- data/spec/csl/schema_spec.rb +70 -0
- data/spec/csl/style/bibliography_spec.rb +7 -0
- data/spec/csl/style/citation_spec.rb +7 -0
- data/spec/csl/style/conditional_spec.rb +7 -0
- data/spec/csl/style/date_spec.rb +11 -0
- data/spec/csl/style/group_spec.rb +7 -0
- data/spec/csl/style/label_spec.rb +7 -0
- data/spec/csl/style/layout_spec.rb +7 -0
- data/spec/csl/style/macro_spec.rb +7 -0
- data/spec/csl/style/names_spec.rb +23 -0
- data/spec/csl/style/number_spec.rb +84 -0
- data/spec/csl/style/text_spec.rb +7 -0
- data/spec/csl/style_spec.rb +19 -0
- data/spec/csl/treelike_spec.rb +151 -0
- data/spec/spec_helper.rb +30 -0
- metadata +192 -0
data/lib/csl/parser.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
module CSL
|
2
|
+
#
|
3
|
+
# A relatively straightforward XML parser that parses CSL using either
|
4
|
+
# Nokogiri or REXML.
|
5
|
+
#
|
6
|
+
class Parser
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
attr_accessor :parser
|
10
|
+
|
11
|
+
@engines = {
|
12
|
+
:nokogiri => lambda { |source|
|
13
|
+
Nokogiri::XML::Document.parse(source, nil, nil,
|
14
|
+
Nokogiri::XML::ParseOptions::DEFAULT_XML | Nokogiri::XML::ParseOptions::NOBLANKS)
|
15
|
+
},
|
16
|
+
:default => lambda { |source|
|
17
|
+
REXML::Document.new(source, :compress_whitespace => :all, :ignore_whitespace_nodes => :all)
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
class << self
|
22
|
+
attr_reader :engines
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
require 'nokogiri'
|
27
|
+
@parser = Parser.engines[:nokogiri]
|
28
|
+
rescue LoadError
|
29
|
+
require 'rexml/document'
|
30
|
+
@parser = Parser.engines[:default]
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse(*arguments)
|
34
|
+
parse!(*arguments)
|
35
|
+
rescue
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse!(source, scope = Node)
|
40
|
+
root = parser[source].children.detect { |child| !comment?(child) }
|
41
|
+
parse_tree root, scope
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def parse_node(node, scope = Node)
|
47
|
+
attributes, text = parse_attributes(node), parse_text(node)
|
48
|
+
|
49
|
+
if text
|
50
|
+
n = TextNode.create node.name, attributes
|
51
|
+
n.text = text
|
52
|
+
n
|
53
|
+
else
|
54
|
+
scope.create node.name, attributes
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def parse_attributes(node)
|
59
|
+
Hash[*node.attributes.map { |n, a|
|
60
|
+
[n.to_sym, a.respond_to?(:value) ? a.value : a.to_s]
|
61
|
+
}.flatten]
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse_tree(node, scope = Node)
|
65
|
+
return nil if node.nil?
|
66
|
+
|
67
|
+
root = parse_node node, scope
|
68
|
+
|
69
|
+
node.children.each do |child|
|
70
|
+
root << parse_tree(child, scope) unless comment?(child)
|
71
|
+
end unless root.textnode?
|
72
|
+
|
73
|
+
root
|
74
|
+
end
|
75
|
+
|
76
|
+
def parse_text(node)
|
77
|
+
if node.respond_to?(:has_text?)
|
78
|
+
node.has_text? && node.text
|
79
|
+
else
|
80
|
+
child = node.children[0]
|
81
|
+
child && child.respond_to?(:text?) && child.text? && child.text
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def comment?(node)
|
86
|
+
node.respond_to?(:comment?) && node.comment? ||
|
87
|
+
node.respond_to?(:node_type) && [:comment, :xmldecl].include?(node.node_type)
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module CSL
|
2
|
+
module PrettyPrinter
|
3
|
+
|
4
|
+
def tags
|
5
|
+
raise 'not implemened by base class'
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_xml
|
9
|
+
tags.flatten.join
|
10
|
+
end
|
11
|
+
|
12
|
+
def pretty_print
|
13
|
+
pp(tags).join("\n")
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def tabwidth
|
19
|
+
2
|
20
|
+
end
|
21
|
+
|
22
|
+
def pp(tags, level = 0)
|
23
|
+
tags.map do |tag|
|
24
|
+
if tag.respond_to?(:map)
|
25
|
+
pp tag, level + 1
|
26
|
+
else
|
27
|
+
' ' * (level * tabwidth) + tag.to_s
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
data/lib/csl/schema.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
module CSL
|
2
|
+
|
3
|
+
class Schema
|
4
|
+
|
5
|
+
@version = '1.0.1'.freeze
|
6
|
+
@namespace = 'http://purl.org/net/xbiblio/csl'.freeze
|
7
|
+
@preamble = '<?xml version="1.0" encoding="utf-8"?>'.freeze
|
8
|
+
|
9
|
+
@types = %w{ article article-journal article-magazine article-newspaper
|
10
|
+
bill book broadcast chapter entry entry-dictionary entry-encyclopedia
|
11
|
+
figure graphic interview legal_case legislation manuscript map
|
12
|
+
motion_picture musical_score pamphlet paper-conference patent
|
13
|
+
personal_communication post post-weblog report review review-book song
|
14
|
+
speech thesis treaty webpage }.map(&:to_sym).freeze
|
15
|
+
|
16
|
+
@variables = Hash.new { |h,k| h.fetch(k.to_sym, nil) }.merge({
|
17
|
+
:date => %w{
|
18
|
+
accessed container event-date issued original-date submitted
|
19
|
+
},
|
20
|
+
|
21
|
+
:names => %w{
|
22
|
+
author collection-editor composer container-author recipient editor
|
23
|
+
editorial-director illustrator interviewer original-author translator
|
24
|
+
},
|
25
|
+
|
26
|
+
:number => %w{
|
27
|
+
chapter-number collection-number edition issue number number-of-pages
|
28
|
+
number-of-volumes volume
|
29
|
+
},
|
30
|
+
|
31
|
+
:text => %w{
|
32
|
+
abstract annote archive archive_location archive-place authority
|
33
|
+
call-number citation-label citation-number collection-title
|
34
|
+
container-title container-title-short dimensions DOI event event-place
|
35
|
+
first-reference-note-number genre ISBN ISSN jurisdiction keyword
|
36
|
+
locator medium note original-publisher original-publisher-place
|
37
|
+
original-title page page-first PMID PMCID publisher publisher-place
|
38
|
+
references section source status title title-short URL version
|
39
|
+
year-suffix
|
40
|
+
}
|
41
|
+
})
|
42
|
+
|
43
|
+
@variables.each_value { |v| v.map!(&:to_sym).freeze }
|
44
|
+
|
45
|
+
@categories = Hash.new { |h,k| h.fetch(k.to_sym, nil) }.merge(
|
46
|
+
Hash[*@variables.keys.map { |k| @variables[k].map { |n| [n,k] } }.flatten]
|
47
|
+
).freeze
|
48
|
+
|
49
|
+
@variables[:name] = @variables[:names]
|
50
|
+
@variables[:dates] = @variables[:date]
|
51
|
+
@variables[:numbers] = @variables[:number]
|
52
|
+
|
53
|
+
@variables[:all] = @variables[:any] =
|
54
|
+
[:date,:names,:text,:number].reduce([]) { |s,a| s.concat(@variables[a]) }.sort
|
55
|
+
|
56
|
+
@variables.freeze
|
57
|
+
|
58
|
+
@attributes = Hash.new { |h,k| h.fetch(k.to_sym, nil) }.merge({
|
59
|
+
:affixes => %w{
|
60
|
+
prefix suffix
|
61
|
+
},
|
62
|
+
:delimiter => %w{
|
63
|
+
delimiter
|
64
|
+
},
|
65
|
+
:display => %w{
|
66
|
+
block left-margin right-inline indent
|
67
|
+
},
|
68
|
+
:font => %w{
|
69
|
+
font-style font-variant font-weight text-decoration vertical-align
|
70
|
+
},
|
71
|
+
:quotes => %w{
|
72
|
+
quotes
|
73
|
+
},
|
74
|
+
:periods => %w{
|
75
|
+
strip-periods
|
76
|
+
},
|
77
|
+
:textcase => %w{
|
78
|
+
lowercase uppercase capitalize-first capitalize-all title sentence
|
79
|
+
},
|
80
|
+
:name => %w{
|
81
|
+
name-form name-delimiter and delimiter-precedes-et-al initialize-with
|
82
|
+
delimiter-precedes-last et-al-min etal-use-first et-al-subsequent-min
|
83
|
+
et-al-subsequent-use-first et-al-use-last
|
84
|
+
},
|
85
|
+
:names => %w{
|
86
|
+
names-delimiter
|
87
|
+
}
|
88
|
+
})
|
89
|
+
|
90
|
+
@attributes.each_value { |v| v.map!(&:to_sym).freeze }
|
91
|
+
@attributes.freeze
|
92
|
+
|
93
|
+
|
94
|
+
class << self
|
95
|
+
|
96
|
+
attr_accessor :version, :namespace, :types, :variables, :categories,
|
97
|
+
:attributes, :preamble
|
98
|
+
|
99
|
+
private :new
|
100
|
+
|
101
|
+
def attr(*arguments)
|
102
|
+
attributes.values_at(*arguments).flatten(1)
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
data/lib/csl/style.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module CSL
|
2
|
+
|
3
|
+
class Style < Node
|
4
|
+
|
5
|
+
@default = :apa
|
6
|
+
|
7
|
+
@root = File.expand_path('../../../vendor/styles', __FILE__).freeze
|
8
|
+
|
9
|
+
@extension = '.csl'.freeze
|
10
|
+
@prefix = ''
|
11
|
+
|
12
|
+
class << self
|
13
|
+
include Loader
|
14
|
+
|
15
|
+
attr_accessor :default
|
16
|
+
|
17
|
+
def parse(data)
|
18
|
+
node = CSL.parse!(data)
|
19
|
+
|
20
|
+
raise ParseError, "root node is not a style: #{node.inspect}" unless
|
21
|
+
node.is_a?(self)
|
22
|
+
|
23
|
+
node
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_defaults :version => Schema.version, :xmlns => Schema.namespace
|
29
|
+
|
30
|
+
attr_struct :xmlns, :version, :'style-class', :'default-locale',
|
31
|
+
:'initialize-with-hyphen', :'page-range-format',
|
32
|
+
:'demote-non-dropping-particle', *Schema.attr(:name, :names)
|
33
|
+
|
34
|
+
attr_children :'style-options', :info, :locale, :macro, :citation,
|
35
|
+
:bibliography
|
36
|
+
|
37
|
+
alias metadata info
|
38
|
+
alias options style_options
|
39
|
+
alias locales locale
|
40
|
+
|
41
|
+
def initialize(attributes = {})
|
42
|
+
super(attributes)
|
43
|
+
|
44
|
+
children[:locale] = []
|
45
|
+
children[:macro] = []
|
46
|
+
|
47
|
+
yield self if block_given?
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module CSL
|
2
|
+
class Style
|
3
|
+
|
4
|
+
class Bibliography < Node
|
5
|
+
|
6
|
+
attr_struct :'hanging-indent', :'second-field-align', :'line-spacing',
|
7
|
+
:'entry-spacing', :'note-distance', :'subsequent-author-substitute',
|
8
|
+
:'subsequent-author-substitute-rule', *Schema.attr(:name, :names)
|
9
|
+
|
10
|
+
attr_children :sort, :layout
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CSL
|
2
|
+
class Style
|
3
|
+
|
4
|
+
class Citation < Node
|
5
|
+
|
6
|
+
attr_struct :'cite-group-delimiter', :collapse, :'year-suffix-delimiter',
|
7
|
+
:'after-collapse-delimiter', :'disambiguate-add-names',
|
8
|
+
:'disambiguate-add-givenname', :'disambiguate-add-year-suffix',
|
9
|
+
:'givenname-disambiguation-rule', :'names-delimiter',
|
10
|
+
*Schema.attr(:names, :name)
|
11
|
+
|
12
|
+
attr_children :sort, :layout
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module CSL
|
2
|
+
class Style
|
3
|
+
|
4
|
+
class Date < Node
|
5
|
+
attr_struct :name, :form, :'range-delimiter', :'date-parts',
|
6
|
+
*Schema.attr(:affixes, :display, :font, :textcase)
|
7
|
+
end
|
8
|
+
|
9
|
+
class DatePart < Node
|
10
|
+
attr_struct :name, :form, :'range-delimiter',
|
11
|
+
*Schema.attr(:affixes, :textcase, :font, :periods)
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module CSL
|
2
|
+
class Style
|
3
|
+
|
4
|
+
class Names < Node
|
5
|
+
|
6
|
+
attr_struct :variable, *Schema.attr(:names)
|
7
|
+
|
8
|
+
attr_children :name, :'et-al', :label, :substitute
|
9
|
+
|
10
|
+
alias labels label
|
11
|
+
|
12
|
+
def initialize(attributes = {})
|
13
|
+
super(attributes)
|
14
|
+
children[:label] = []
|
15
|
+
|
16
|
+
yield self if block_given?
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
class Name < Node
|
23
|
+
|
24
|
+
attr_struct :form, *Schema.attr(:name, :affixes, :font, :delimiter)
|
25
|
+
|
26
|
+
attr_children :'name-part'
|
27
|
+
|
28
|
+
alias parts name_part
|
29
|
+
|
30
|
+
def initialize(attributes = {})
|
31
|
+
super(attributes)
|
32
|
+
children[:'name-part'] = []
|
33
|
+
|
34
|
+
yield self if block_given?
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
class NamePart < Node
|
40
|
+
has_no_children
|
41
|
+
attr_struct :name, *Schema.attr(:textcase, :affixes, :font)
|
42
|
+
end
|
43
|
+
|
44
|
+
class EtAl < Node
|
45
|
+
has_no_children
|
46
|
+
attr_struct :term, *Schema.attr(:affixes, :font)
|
47
|
+
end
|
48
|
+
|
49
|
+
class Substitute < Node
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|