csl 1.0.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/.gitignore +1 -0
- data/.simplecov +3 -1
- data/.travis.yml +4 -10
- data/AGPL +1 -1
- data/BSDL +2 -2
- data/Gemfile +22 -12
- data/README.md +20 -11
- data/Rakefile +6 -1
- data/csl.gemspec +4 -2
- data/features/parser/choose.feature +16 -0
- data/features/step_definitions/parser_steps.rb +10 -0
- data/features/support/env.rb +16 -2
- data/lib/csl.rb +7 -4
- data/lib/csl/compatibility.rb +10 -16
- data/lib/csl/info.rb +3 -2
- data/lib/csl/locale.rb +92 -19
- data/lib/csl/locale/term.rb +41 -2
- data/lib/csl/name_options.rb +44 -0
- data/lib/csl/node.rb +54 -6
- data/lib/csl/parser.rb +17 -17
- data/lib/csl/schema.rb +37 -32
- data/lib/csl/style.rb +32 -8
- data/lib/csl/style/bibliography.rb +46 -9
- data/lib/csl/style/choose.rb +4 -2
- data/lib/csl/style/citation.rb +10 -11
- data/lib/csl/style/group.rb +13 -6
- data/lib/csl/style/label.rb +1 -1
- data/lib/csl/style/layout.rb +4 -4
- data/lib/csl/style/names.rb +161 -22
- data/lib/csl/style/sort.rb +74 -18
- data/lib/csl/style/text.rb +6 -4
- data/lib/csl/treelike.rb +16 -5
- data/lib/csl/version.rb +2 -2
- data/spec/csl/locale/term_spec.rb +70 -70
- data/spec/csl/locale_spec.rb +140 -99
- data/spec/csl/parser_spec.rb +26 -26
- data/spec/csl/schema_spec.rb +15 -15
- data/spec/csl/style/layout_spec.rb +2 -2
- data/spec/csl/style/sort_spec.rb +11 -0
- data/spec/spec_helper.rb +13 -4
- metadata +20 -14
data/lib/csl/style.rb
CHANGED
@@ -3,6 +3,8 @@ module CSL
|
|
3
3
|
class Style < Node
|
4
4
|
types << CSL::Info << CSL::Locale
|
5
5
|
|
6
|
+
include InheritableNameOptions
|
7
|
+
|
6
8
|
@default = :apa
|
7
9
|
|
8
10
|
@root = '/usr/local/share/csl/styles'.freeze
|
@@ -14,9 +16,9 @@ module CSL
|
|
14
16
|
include Loader
|
15
17
|
|
16
18
|
attr_accessor :default
|
17
|
-
|
18
|
-
def load(input =
|
19
|
-
super
|
19
|
+
|
20
|
+
def load(input = nil)
|
21
|
+
super(input || Style.default)
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
@@ -45,8 +47,7 @@ module CSL
|
|
45
47
|
:independent_parent_link, :independent_parent_link=,
|
46
48
|
:has_independent_parent_link?, :title=, :id=, :has_title?, :has_id?,
|
47
49
|
:published_at, :updated_at, :citation_format, :citation_format=,
|
48
|
-
:
|
49
|
-
:default_license!
|
50
|
+
:update!, :license, :license=, :default_license?, :default_license!
|
50
51
|
|
51
52
|
def initialize(attributes = {})
|
52
53
|
super(attributes, &nil)
|
@@ -77,6 +78,7 @@ module CSL
|
|
77
78
|
validate.empty?
|
78
79
|
end
|
79
80
|
|
81
|
+
remove_method :info # generated by struct_children
|
80
82
|
def info
|
81
83
|
children[:info] ||= Info.new
|
82
84
|
end
|
@@ -114,6 +116,28 @@ module CSL
|
|
114
116
|
load_related_style_from independent_parent_link
|
115
117
|
end
|
116
118
|
|
119
|
+
def demote_non_dropping_particle
|
120
|
+
attributes[:'demote-non-dropping-particle']
|
121
|
+
end
|
122
|
+
alias demote_particle demote_non_dropping_particle
|
123
|
+
|
124
|
+
def demote_non_dropping_particle?
|
125
|
+
attribute?(:'demote-non-dropping-particle')
|
126
|
+
end
|
127
|
+
alias demote_particle? demote_non_dropping_particle?
|
128
|
+
|
129
|
+
def initialize_without_hyphen?
|
130
|
+
attribute?(:'initialize-with-hyphen') && !attributes[:'initialize-with-hyphen']
|
131
|
+
end
|
132
|
+
|
133
|
+
def has_page_range_format?
|
134
|
+
attribute?(:'page-range-format')
|
135
|
+
end
|
136
|
+
|
137
|
+
def page_range_format
|
138
|
+
attributes[:'page-range-format']
|
139
|
+
end
|
140
|
+
|
117
141
|
private
|
118
142
|
|
119
143
|
def preamble
|
@@ -124,7 +148,7 @@ module CSL
|
|
124
148
|
# TODO try local first
|
125
149
|
Style.load(uri)
|
126
150
|
end
|
127
|
-
|
151
|
+
|
128
152
|
def added_macro(node)
|
129
153
|
unless node.attribute?(:name)
|
130
154
|
raise ValidationError,
|
@@ -138,10 +162,10 @@ module CSL
|
|
138
162
|
|
139
163
|
macros[node[:name]] = node
|
140
164
|
end
|
141
|
-
|
165
|
+
|
142
166
|
def deleted_macro(node)
|
143
167
|
macros.delete node[:name]
|
144
168
|
end
|
145
169
|
end
|
146
170
|
|
147
|
-
end
|
171
|
+
end
|
@@ -1,15 +1,52 @@
|
|
1
1
|
module CSL
|
2
2
|
class Style
|
3
|
-
|
3
|
+
|
4
4
|
class Bibliography < Node
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
|
6
|
+
include InheritableNameOptions
|
7
|
+
|
8
|
+
attr_struct :'subsequent-author-substitute',
|
9
|
+
:'subsequent-author-substitute-rule',
|
10
|
+
*Schema.attr(:bibliography, :name, :names)
|
11
|
+
|
10
12
|
attr_children :sort, :layout
|
11
|
-
|
13
|
+
|
14
|
+
attr_defaults :'line-spacing' => 1, :'entry-spacing' => 1,
|
15
|
+
:'subsequent-author-substitute-rule' => 'complete-all'
|
16
|
+
|
17
|
+
alias sort? has_sort?
|
18
|
+
|
19
|
+
def bibliography_options
|
20
|
+
attributes_for(*Schema.attr(:bibliography))
|
21
|
+
end
|
22
|
+
|
23
|
+
def sort_keys
|
24
|
+
return [] unless sort?
|
25
|
+
children[:sort].descendants
|
26
|
+
end
|
27
|
+
|
28
|
+
def substitute_subsequent_authors?
|
29
|
+
attribute?(:'subsequent-author-substitute')
|
30
|
+
end
|
31
|
+
|
32
|
+
def subsequent_author_substitute
|
33
|
+
attributes[:'subsequent-author-substitute'].to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
def subsequent_author_substitute_rule
|
37
|
+
attributes[:'subsequent-author-substitute-rule'].to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
def substitute_subsequent_authors_completely?
|
41
|
+
return false unless substitute_subsequent_authors?
|
42
|
+
subsequent_author_substitute_rule == 'complete-all'
|
43
|
+
end
|
44
|
+
|
45
|
+
def substitute_subsequent_authors_individually?
|
46
|
+
return false unless substitute_subsequent_authors?
|
47
|
+
subsequent_author_substitute_rule != 'complete-all'
|
48
|
+
end
|
12
49
|
end
|
13
|
-
|
50
|
+
|
14
51
|
end
|
15
|
-
end
|
52
|
+
end
|
data/lib/csl/style/choose.rb
CHANGED
@@ -3,6 +3,8 @@ module CSL
|
|
3
3
|
|
4
4
|
class Choose < Node
|
5
5
|
|
6
|
+
alias blocks children
|
7
|
+
|
6
8
|
class Block < Node
|
7
9
|
attr_struct :match, *Schema.attr(:conditionals)
|
8
10
|
|
@@ -10,7 +12,7 @@ module CSL
|
|
10
12
|
|
11
13
|
class << self
|
12
14
|
def matches?(nodename)
|
13
|
-
nodename
|
15
|
+
nodename === ':if' || nodename === ':elseif' || nodename === ':else'
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
@@ -52,4 +54,4 @@ module CSL
|
|
52
54
|
end
|
53
55
|
|
54
56
|
end
|
55
|
-
end
|
57
|
+
end
|
data/lib/csl/style/citation.rb
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
module CSL
|
2
2
|
class Style
|
3
|
-
|
3
|
+
|
4
4
|
class Citation < Node
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
*Schema.attr(:names, :name)
|
11
|
-
|
5
|
+
|
6
|
+
include InheritableNameOptions
|
7
|
+
|
8
|
+
attr_struct(*Schema.attr(:citation, :names, :name))
|
9
|
+
|
12
10
|
attr_children :sort, :layout
|
13
|
-
|
11
|
+
|
12
|
+
alias sort? has_sort?
|
14
13
|
end
|
15
|
-
|
14
|
+
|
16
15
|
end
|
17
|
-
end
|
16
|
+
end
|
data/lib/csl/style/group.rb
CHANGED
@@ -13,18 +13,25 @@ module CSL
|
|
13
13
|
# the Group calls a variable (either directly or via a macro), and
|
14
14
|
# b) all variables that are called are empty.
|
15
15
|
class Group < Node
|
16
|
-
attr_struct(*Schema.attr(:formatting, :
|
16
|
+
attr_struct(*Schema.attr(:formatting, :delimiter))
|
17
17
|
|
18
18
|
def delimiter
|
19
19
|
attributes.fetch(:delimiter, '')
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
# Returns only those formatting options applicable to the Group
|
23
|
+
# node itself; not those which are transmitted to the enclosed
|
24
|
+
# elements.
|
25
|
+
#
|
26
|
+
# @return [Hash] the node's formatting options
|
27
|
+
def formatting_options
|
28
|
+
attributes_for :display, *Schema.attr(:affixes)
|
29
|
+
end
|
30
|
+
|
31
|
+
def inheritable_formatting_options
|
32
|
+
attributes_for :'text-case', *Schema.attr(:font)
|
26
33
|
end
|
27
34
|
end
|
28
35
|
|
29
36
|
end
|
30
|
-
end
|
37
|
+
end
|
data/lib/csl/style/label.rb
CHANGED
data/lib/csl/style/layout.rb
CHANGED
data/lib/csl/style/names.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
module CSL
|
2
4
|
class Style
|
3
5
|
|
4
6
|
class Names < Node
|
7
|
+
extend InheritsNameOptions
|
5
8
|
|
6
|
-
attr_struct :variable, *Schema.attr(:
|
9
|
+
attr_struct :variable, *Schema.attr(:delimiter, :affixes, :font)
|
7
10
|
|
8
11
|
attr_children :name, :'et-al', :label, :substitute
|
9
12
|
|
13
|
+
inherits :names_options
|
14
|
+
|
10
15
|
alias labels label
|
11
16
|
|
12
17
|
def initialize(attributes = {})
|
@@ -16,8 +21,10 @@ module CSL
|
|
16
21
|
yield self if block_given?
|
17
22
|
end
|
18
23
|
|
19
|
-
def delimiter
|
20
|
-
attributes.fetch(:delimiter
|
24
|
+
def delimiter(node = nil, style = nil)
|
25
|
+
attributes.fetch(:delimiter) do
|
26
|
+
inherited_names_options(node, style)[:delimiter] || ''
|
27
|
+
end
|
21
28
|
end
|
22
29
|
|
23
30
|
def has_variable?
|
@@ -32,15 +39,20 @@ module CSL
|
|
32
39
|
|
33
40
|
|
34
41
|
class Name < Node
|
42
|
+
extend InheritsNameOptions
|
35
43
|
|
36
44
|
attr_struct :form, *Schema.attr(:name, :affixes, :font, :delimiter)
|
37
45
|
|
38
|
-
|
39
|
-
|
40
|
-
|
46
|
+
# Having default attributes makes inheritance really difficult.
|
47
|
+
#
|
48
|
+
# attr_defaults :form => 'long', :delimiter => ', ',
|
49
|
+
# :'delimiter-precedes-last' => 'contextual', :initialize => true,
|
50
|
+
# :'sort-separator' => ', '
|
41
51
|
|
42
52
|
attr_children :'name-part'
|
43
53
|
|
54
|
+
inherits :name_options
|
55
|
+
|
44
56
|
alias parts name_part
|
45
57
|
|
46
58
|
def initialize(attributes = {})
|
@@ -50,17 +62,29 @@ module CSL
|
|
50
62
|
yield self if block_given?
|
51
63
|
end
|
52
64
|
|
65
|
+
def count?
|
66
|
+
attribute?(:form) && attributes[:form] == 'count'
|
67
|
+
end
|
68
|
+
|
69
|
+
def name_options
|
70
|
+
attributes_for :form, :initialize, :'initialize-with', :'sort-separator'
|
71
|
+
end
|
53
72
|
|
54
|
-
def
|
55
|
-
|
73
|
+
def initialize_without_hyphen?
|
74
|
+
!root? && root.respond_to?(:initialize_without_hyphen?) &&
|
75
|
+
root.initialize_without_hyphen?
|
56
76
|
end
|
57
77
|
|
58
78
|
def et_al
|
59
|
-
parent && parent.et_al
|
79
|
+
@et_al || parent && parent.et_al
|
80
|
+
end
|
81
|
+
|
82
|
+
def et_al=(et_al)
|
83
|
+
@et_al = et_al
|
60
84
|
end
|
61
85
|
|
62
86
|
# @param names [#to_i, Enumerable] the list of names (or its length)
|
63
|
-
# @return [Boolean] whether or not the should be
|
87
|
+
# @return [Boolean] whether or not the list should be truncated
|
64
88
|
def truncate?(names, subsequent = false)
|
65
89
|
names = names.length if names.respond_to?(:length)
|
66
90
|
limit = truncate_when(subsequent)
|
@@ -79,35 +103,60 @@ module CSL
|
|
79
103
|
|
80
104
|
def truncate_when(subsequent = false)
|
81
105
|
if subsequent && attribute?(:'et-al-subsequent-min')
|
82
|
-
|
106
|
+
attributes[:'et-al-subsequent-min'].to_i
|
83
107
|
else
|
84
|
-
|
108
|
+
attributes[:'et-al-min'].to_i
|
85
109
|
end
|
86
110
|
end
|
87
111
|
|
88
112
|
def truncate_at(subsequent = false)
|
89
113
|
if subsequent && attribute?(:'et-al-subsequent-use-first')
|
90
|
-
|
114
|
+
attributes.fetch(:'et-al-subsequent-use-first', 1).to_i
|
91
115
|
else
|
92
|
-
|
116
|
+
attributes.fetch(:'et-al-use-first', 1).to_i
|
93
117
|
end
|
94
118
|
end
|
95
119
|
|
120
|
+
def truncate_at!(at)
|
121
|
+
attributes[:'et-al-use-first'] = at.to_i
|
122
|
+
self
|
123
|
+
end
|
124
|
+
|
125
|
+
def truncate_when!(pos)
|
126
|
+
attributes[:'et-al-min'] = pos.to_i
|
127
|
+
self
|
128
|
+
end
|
129
|
+
|
130
|
+
def truncate_subsequent_at!(at)
|
131
|
+
attributes[:'et-al-subsequent-use-first'] = at.to_i
|
132
|
+
self
|
133
|
+
end
|
134
|
+
|
135
|
+
def truncate_subsequent_when!(pos)
|
136
|
+
attributes[:'et-al-subsequent-min'] = pos.to_i
|
137
|
+
self
|
138
|
+
end
|
139
|
+
|
96
140
|
# @return [String] the delimiter between family and given names
|
97
141
|
# in sort order
|
98
142
|
def sort_separator
|
99
|
-
attributes[:'sort-separator']
|
143
|
+
attributes[:'sort-separator'] || ', '
|
100
144
|
end
|
101
145
|
|
102
146
|
# @return [String] the delimiter between names
|
103
147
|
def delimiter
|
104
|
-
attributes[:delimiter]
|
148
|
+
attributes[:delimiter] || ', '
|
105
149
|
end
|
106
150
|
|
107
151
|
def name_as_sort_order?
|
108
152
|
attribute?(:'name-as-sort-order')
|
109
153
|
end
|
110
154
|
|
155
|
+
def name_as_sort_order_at?(position)
|
156
|
+
return false unless name_as_sort_order?
|
157
|
+
all_names_as_sort_order? || position == 1 && first_name_as_sort_order?
|
158
|
+
end
|
159
|
+
|
111
160
|
def name_as_sort_order
|
112
161
|
attributes[:'name-as-sort-order'].to_s
|
113
162
|
end
|
@@ -115,14 +164,91 @@ module CSL
|
|
115
164
|
alias sort_order name_as_sort_order
|
116
165
|
|
117
166
|
def first_name_as_sort_order?
|
118
|
-
attributes[:'name-as-sort-order'].to_s =~ /^first$/i
|
167
|
+
!!(attributes[:'name-as-sort-order'].to_s =~ /^first$/i)
|
119
168
|
end
|
120
169
|
|
121
170
|
def all_names_as_sort_order?
|
122
|
-
attributes[:'name-as-sort-order'].to_s =~ /^all$/i
|
171
|
+
!!(attributes[:'name-as-sort-order'].to_s =~ /^all$/i)
|
172
|
+
end
|
173
|
+
|
174
|
+
def first_name_as_sort_order!
|
175
|
+
attributes[:'name-as-sort-order'] = 'first'
|
176
|
+
self
|
177
|
+
end
|
178
|
+
|
179
|
+
def all_names_as_sort_order!
|
180
|
+
attributes[:'name-as-sort-order'] = 'all'
|
181
|
+
self
|
182
|
+
end
|
183
|
+
|
184
|
+
def delimiter_precedes_et_al?(names)
|
185
|
+
names = names.length if names.respond_to?(:length)
|
186
|
+
|
187
|
+
case
|
188
|
+
when delimiter_never_precedes_et_al?
|
189
|
+
false
|
190
|
+
when delimiter_always_precedes_et_al?
|
191
|
+
true
|
192
|
+
when delimiter_precedes_et_al_after_inverted_name?
|
193
|
+
name_as_sort_order_at?(names.to_i)
|
194
|
+
else
|
195
|
+
names.to_i > 1
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# @return [Boolean] whether or not the delimmiter should
|
200
|
+
# always be inserted before et-al
|
201
|
+
def delimiter_always_precedes_et_al?
|
202
|
+
!!(attributes[:'delimiter-precedes-et-al'].to_s =~ /^always$/i)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Set the :'delimiter-precedes-et-al' attribute to 'always'.
|
206
|
+
# @return [self] self
|
207
|
+
def delimiter_always_precedes_et_al!
|
208
|
+
attributes[:'delimiter-precedes-et-al'] = 'always'
|
209
|
+
self
|
210
|
+
end
|
211
|
+
|
212
|
+
alias delimiter_precedes_et_al! delimiter_always_precedes_et_al!
|
213
|
+
|
214
|
+
|
215
|
+
# @return [Boolean] whether or not the delimiter should
|
216
|
+
# never be inserted before et-al
|
217
|
+
def delimiter_never_precedes_et_al?
|
218
|
+
!!(attributes[:'delimiter-precedes-et-al'].to_s =~ /^never$/i)
|
219
|
+
end
|
220
|
+
|
221
|
+
# Set the :'delimiter-precedes-et-al' attribute to 'never'
|
222
|
+
# @return [self] self
|
223
|
+
def delimiter_never_precedes_et_al!
|
224
|
+
attributes[:'delimiter-precedes-et-al'] = 'never'
|
225
|
+
self
|
226
|
+
end
|
227
|
+
|
228
|
+
# @return [Boolean] whether or not the delimtier should
|
229
|
+
# be inserted between before et-al depending on the
|
230
|
+
# number of names rendered
|
231
|
+
def delimiter_contextually_precedes_et_al?
|
232
|
+
return true unless attribute?[:'delimiter-precedes-et-al']
|
233
|
+
!!(attributes[:'delimiter-precedes-et-al'].to_s =~ /^contextual/i)
|
234
|
+
end
|
235
|
+
|
236
|
+
# Set the :'delimiter-precedes-et-al' attribute to 'contextual'
|
237
|
+
# @return [self] self
|
238
|
+
def delimiter_contextually_precedes_et_al!
|
239
|
+
attributes[:'delimiter-precedes-et-al'] = 'contextual'
|
240
|
+
self
|
241
|
+
end
|
242
|
+
|
243
|
+
def delimiter_precedes_et_al_after_inverted_name?
|
244
|
+
!!(attributes[:'delimiter-precedes-et-al'].to_s =~ /^after-inverted-name/i)
|
245
|
+
end
|
246
|
+
|
247
|
+
def delimiter_precedes_et_al_after_inverted_name!
|
248
|
+
attributes[:'delimiter-precedes-et-al'] = 'after-inverted-name'
|
249
|
+
self
|
123
250
|
end
|
124
251
|
|
125
|
-
|
126
252
|
# @param names [#to_i, Enumerable] the list of names (or its length)
|
127
253
|
# @return [Boolean] whether or not the delimiter will be inserted between
|
128
254
|
# the penultimate and the last name
|
@@ -136,7 +262,7 @@ module CSL
|
|
136
262
|
false
|
137
263
|
when delimiter_always_precedes_last?
|
138
264
|
true
|
139
|
-
when
|
265
|
+
when delimiter_precedes_last_after_inverted_name?
|
140
266
|
if name_as_sort_order?
|
141
267
|
all_names_as_sort_order? || names.to_i == 2
|
142
268
|
else
|
@@ -180,6 +306,7 @@ module CSL
|
|
180
306
|
# @return [Boolean] whether or not the should be inserted between the
|
181
307
|
# penultimate and the last name depending on the number of names
|
182
308
|
def delimiter_contextually_precedes_last?
|
309
|
+
return true unless attribute?(:'delimiter-precedes-last')
|
183
310
|
!!(attributes[:'delimiter-precedes-last'].to_s =~ /^contextual/i)
|
184
311
|
end
|
185
312
|
|
@@ -203,20 +330,32 @@ module CSL
|
|
203
330
|
attributes[:'et-al-use-last'].to_s =~ /^true$/
|
204
331
|
end
|
205
332
|
|
333
|
+
def ellipsis
|
334
|
+
"#{delimiter}… "
|
335
|
+
end
|
336
|
+
|
206
337
|
def connector
|
207
338
|
c = attributes[:and]
|
208
339
|
c == 'symbol' ? '&' : c
|
209
340
|
end
|
341
|
+
|
342
|
+
def connector=(c)
|
343
|
+
attributes[:and] = c
|
344
|
+
end
|
210
345
|
end
|
211
346
|
|
212
347
|
class NamePart < Node
|
213
348
|
has_no_children
|
214
349
|
attr_struct :name, :'text-case', *Schema.attr(:affixes, :font)
|
350
|
+
|
351
|
+
def name
|
352
|
+
attributes[:name]
|
353
|
+
end
|
215
354
|
end
|
216
355
|
|
217
356
|
class EtAl < Node
|
218
357
|
has_no_children
|
219
|
-
attr_struct :term, *Schema.attr(:affixes, :font)
|
358
|
+
attr_struct :term, :'text-case', *Schema.attr(:affixes, :font)
|
220
359
|
|
221
360
|
attr_defaults :term => 'et-al'
|
222
361
|
end
|
@@ -226,4 +365,4 @@ module CSL
|
|
226
365
|
|
227
366
|
|
228
367
|
end
|
229
|
-
end
|
368
|
+
end
|