csl 1.0.0.pre3 → 1.0.0.pre4
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/lib/csl/info.rb +145 -30
- data/lib/csl/node.rb +55 -48
- data/lib/csl/style.rb +91 -35
- data/lib/csl/treelike.rb +83 -84
- data/lib/csl/version.rb +1 -1
- data/spec/csl/info_spec.rb +2 -2
- data/spec/csl/style_spec.rb +56 -13
- data/spec/fixtures/locales/locales-de-DE.xml +304 -0
- data/spec/fixtures/locales/locales-en-GB.xml +304 -0
- data/spec/fixtures/locales/locales-en-US.xml +304 -0
- data/spec/fixtures/styles/apa.csl +443 -0
- data/spec/spec_helper.rb +11 -0
- metadata +16 -8
data/lib/csl/style.rb
CHANGED
@@ -1,91 +1,147 @@
|
|
1
1
|
module CSL
|
2
|
-
|
2
|
+
|
3
3
|
class Style < Node
|
4
4
|
types << CSL::Info << CSL::Locale
|
5
|
-
|
5
|
+
|
6
6
|
@default = :apa
|
7
7
|
|
8
8
|
@root = File.expand_path('../../../vendor/styles', __FILE__).freeze
|
9
|
-
|
9
|
+
|
10
10
|
@extension = '.csl'.freeze
|
11
11
|
@prefix = ''
|
12
|
-
|
12
|
+
|
13
13
|
class << self
|
14
14
|
include Loader
|
15
|
-
|
15
|
+
|
16
16
|
attr_accessor :default
|
17
17
|
|
18
18
|
def parse(data)
|
19
19
|
node = CSL.parse!(data, self)
|
20
|
-
|
20
|
+
|
21
21
|
raise ParseError, "root node is not a style: #{node.inspect}" unless
|
22
22
|
node.is_a?(self)
|
23
|
-
|
23
|
+
|
24
24
|
node
|
25
|
-
end
|
25
|
+
end
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
attr_defaults :version => Schema.version, :xmlns => Schema.namespace
|
29
|
-
|
29
|
+
|
30
30
|
attr_struct :xmlns, :version, :class, :'default-locale',
|
31
31
|
:'initialize-with-hyphen', :'page-range-format',
|
32
32
|
:'demote-non-dropping-particle', *Schema.attr(:name, :names)
|
33
|
-
|
33
|
+
|
34
34
|
attr_children :'style-options', :info, :locale, :macro,
|
35
35
|
:citation, :bibliography
|
36
|
-
|
36
|
+
|
37
|
+
attr_reader :macros, :errors
|
38
|
+
|
37
39
|
alias options style_options
|
38
40
|
alias locales locale
|
39
|
-
|
41
|
+
|
42
|
+
alias has_macros? has_macro?
|
43
|
+
|
40
44
|
def_delegators :info, :self_link, :self_link=, :has_self_link?,
|
41
45
|
:template_link, :template_link=, :has_template_link?,
|
42
46
|
:documentation_link, :documentation_link=, :has_documentation_link?,
|
43
|
-
|
44
|
-
|
47
|
+
:independent_parent_link, :independent_parent_link=,
|
48
|
+
:has_independent_parent_link?, :title=, :id=, :has_title?, :has_id?,
|
49
|
+
:published_at, :updated_at
|
50
|
+
|
45
51
|
def initialize(attributes = {})
|
46
|
-
super(attributes, &nil)
|
47
|
-
children[:locale], children[:macro] = [], []
|
48
|
-
|
52
|
+
super(attributes, &nil)
|
53
|
+
children[:locale], children[:macro], @macros, @errors = [], [], {}, []
|
54
|
+
|
49
55
|
yield self if block_given?
|
50
56
|
end
|
51
57
|
|
58
|
+
# @override
|
59
|
+
def added_child(node)
|
60
|
+
delegate = :"added_#{node.nodename}"
|
61
|
+
send delegate, node if respond_to?(delegate, true)
|
62
|
+
node
|
63
|
+
end
|
64
|
+
|
65
|
+
# @override
|
66
|
+
def deleted_child(node)
|
67
|
+
delegate = :"deleted_#{node.nodename}"
|
68
|
+
send delegate, node if respond_to?(delegate, true)
|
69
|
+
node
|
70
|
+
end
|
71
|
+
|
52
72
|
def validate
|
53
|
-
Schema.validate self
|
73
|
+
@errors = Schema.validate self
|
54
74
|
end
|
55
|
-
|
75
|
+
|
56
76
|
def valid?
|
57
77
|
validate.empty?
|
58
78
|
end
|
59
|
-
|
79
|
+
|
60
80
|
def info
|
61
81
|
children[:info] ||= Info.new
|
62
82
|
end
|
63
|
-
|
83
|
+
|
64
84
|
alias_child :metadata, :info
|
65
|
-
|
85
|
+
|
66
86
|
# @return [String] the style's id
|
67
87
|
def id
|
68
|
-
return
|
88
|
+
return unless info.has_id?
|
69
89
|
info.id.to_s
|
70
90
|
end
|
71
|
-
|
91
|
+
|
72
92
|
# @return [String] the style's title
|
73
93
|
def title
|
74
|
-
return
|
94
|
+
return unless info.has_title?
|
75
95
|
info.title.to_s
|
76
96
|
end
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
97
|
+
|
98
|
+
alias has_template? has_template_link?
|
99
|
+
|
100
|
+
# @return [Style] the style's template
|
101
|
+
def template
|
102
|
+
return unless has_template?
|
103
|
+
load_related_style_from template_link
|
104
|
+
end
|
105
|
+
|
106
|
+
alias dependent? has_independent_parent_link?
|
107
|
+
|
108
|
+
def independent?
|
109
|
+
!dependent?
|
110
|
+
end
|
111
|
+
|
112
|
+
def independent_parent
|
113
|
+
return unless dependent?
|
114
|
+
load_related_style_from independent_parent_link
|
115
|
+
end
|
83
116
|
|
84
117
|
private
|
85
|
-
|
118
|
+
|
86
119
|
def preamble
|
87
120
|
Schema.preamble.dup
|
88
|
-
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def load_related_style_from(uri)
|
124
|
+
# TODO try local first
|
125
|
+
Style.load(uri)
|
126
|
+
end
|
127
|
+
|
128
|
+
def added_macro(node)
|
129
|
+
unless node.attribute?(:name)
|
130
|
+
raise ValidationError,
|
131
|
+
"failed to register macro #{node.inspect}: name attribute missing"
|
132
|
+
end
|
133
|
+
|
134
|
+
if macros.key?(node[:name])
|
135
|
+
raise ValidationError,
|
136
|
+
"failed to register macro #{node.inspect}: duplicate name"
|
137
|
+
end
|
138
|
+
|
139
|
+
macros[node[:name]] = node
|
140
|
+
end
|
141
|
+
|
142
|
+
def deleted_macro(node)
|
143
|
+
macros.delete node[:name]
|
144
|
+
end
|
89
145
|
end
|
90
|
-
|
146
|
+
|
91
147
|
end
|
data/lib/csl/treelike.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
module CSL
|
2
|
-
|
2
|
+
|
3
3
|
module Treelike
|
4
|
-
|
4
|
+
|
5
5
|
attr_accessor :parent
|
6
6
|
attr_reader :children
|
7
7
|
attr_writer :nodename
|
8
|
-
|
8
|
+
|
9
9
|
protected :parent=
|
10
|
-
|
10
|
+
|
11
11
|
def self.included(base)
|
12
12
|
base.extend(ClassMethods)
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
# @return [String] the node's name.
|
16
16
|
def nodename
|
17
17
|
@nodename ||= self.class.name.split(/::/)[-1].gsub(/([[:lower:]])([[:upper:]])/, '\1-\2').downcase
|
@@ -25,12 +25,13 @@ module CSL
|
|
25
25
|
enum_for :each_child
|
26
26
|
end
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
def delete_children(*nodes)
|
30
30
|
nodes.each do |node|
|
31
31
|
delete_child node
|
32
32
|
end
|
33
33
|
end
|
34
|
+
alias delete delete_children
|
34
35
|
|
35
36
|
# Deletes child nodes that are equal to the passed-in node. Returns all
|
36
37
|
# deleted children. If no children were deleted, returns nil. If the
|
@@ -38,54 +39,52 @@ module CSL
|
|
38
39
|
# deleted.
|
39
40
|
def delete_child(child)
|
40
41
|
deleted = children.delete child
|
41
|
-
|
42
|
+
|
42
43
|
case
|
43
44
|
when deleted.nil? && block_given?
|
44
45
|
yield
|
45
46
|
when deleted.nil?
|
46
47
|
nil
|
47
48
|
else
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
54
|
-
|
49
|
+
deleted.parent = nil
|
50
|
+
|
51
|
+
deleted_child deleted
|
52
|
+
deleted.deleted_from self
|
53
|
+
|
55
54
|
deleted
|
56
55
|
end
|
57
56
|
rescue => e
|
58
57
|
# TODO rollback
|
59
58
|
raise e
|
60
59
|
end
|
61
|
-
|
60
|
+
|
62
61
|
def add_children(*nodes)
|
63
62
|
nodes.each do |node|
|
64
63
|
add_child node
|
65
64
|
end
|
66
65
|
self
|
67
66
|
end
|
68
|
-
|
67
|
+
|
69
68
|
def add_child(node)
|
70
69
|
node.unlink
|
71
|
-
|
70
|
+
|
72
71
|
node.parent = self
|
73
72
|
children << node
|
74
|
-
|
73
|
+
|
75
74
|
added_child node
|
76
75
|
node.added_to self
|
77
|
-
|
76
|
+
|
78
77
|
node
|
79
78
|
rescue => e
|
80
79
|
# TODO rollback
|
81
80
|
raise e
|
82
81
|
end
|
83
|
-
|
82
|
+
|
84
83
|
def <<(node)
|
85
84
|
add_child node
|
86
85
|
self
|
87
86
|
end
|
88
|
-
|
87
|
+
|
89
88
|
# Returns the first immediate child node whose nodename matches the
|
90
89
|
# passed-in name/pattern and attribute conditions.
|
91
90
|
#
|
@@ -99,7 +98,7 @@ module CSL
|
|
99
98
|
end
|
100
99
|
end
|
101
100
|
alias > find_child
|
102
|
-
|
101
|
+
|
103
102
|
# Returns all immediate child nodes whose nodename matches the passed-in
|
104
103
|
# name/pattern and attribute conditions; returns an empty array if there
|
105
104
|
# is no match.
|
@@ -124,20 +123,20 @@ module CSL
|
|
124
123
|
def empty?
|
125
124
|
children.empty?
|
126
125
|
end
|
127
|
-
|
126
|
+
|
128
127
|
# Unlinks the node and all its children from its parent node. Returns
|
129
128
|
# the old parent node or nil.
|
130
129
|
def unlink
|
131
130
|
return nil if root?
|
132
|
-
|
131
|
+
|
133
132
|
other = parent
|
134
133
|
other.delete_child self
|
135
|
-
|
134
|
+
|
136
135
|
self.parent = nil
|
137
136
|
|
138
137
|
other
|
139
138
|
end
|
140
|
-
|
139
|
+
|
141
140
|
def each_sibling
|
142
141
|
if block_given?
|
143
142
|
unless root?
|
@@ -145,73 +144,73 @@ module CSL
|
|
145
144
|
yield node unless node.equal?(self)
|
146
145
|
end
|
147
146
|
end
|
148
|
-
|
147
|
+
|
149
148
|
self
|
150
149
|
else
|
151
150
|
enum_for :each_sibling
|
152
151
|
end
|
153
152
|
end
|
154
|
-
|
153
|
+
|
155
154
|
def siblings
|
156
155
|
@siblings = each_sibling.to_a
|
157
156
|
end
|
158
|
-
|
157
|
+
|
159
158
|
# Traverses the node's sub-tree in depth-first order.
|
160
159
|
def each_descendant
|
161
160
|
if block_given?
|
162
161
|
each_child do |child|
|
163
|
-
yield child
|
162
|
+
yield child
|
164
163
|
child.each_descendant(&Proc.new)
|
165
164
|
end
|
166
|
-
|
165
|
+
|
167
166
|
self
|
168
167
|
else
|
169
168
|
enum_for :each_descendant
|
170
169
|
end
|
171
170
|
end
|
172
|
-
|
171
|
+
|
173
172
|
# Returns all descendants of the node. See {#descendants!}
|
174
173
|
# for a memoized version.
|
175
174
|
def descendants
|
176
175
|
@descendants = each_descendant.to_a
|
177
176
|
end
|
178
|
-
|
177
|
+
|
179
178
|
def each_ancestor
|
180
179
|
if block_given?
|
181
180
|
p = parent
|
182
|
-
|
181
|
+
|
183
182
|
until p.nil?
|
184
183
|
yield p
|
185
184
|
p = p.parent
|
186
185
|
end
|
187
|
-
|
186
|
+
|
188
187
|
self
|
189
188
|
else
|
190
189
|
enum_for :each_ancestor
|
191
190
|
end
|
192
191
|
end
|
193
|
-
|
192
|
+
|
194
193
|
# @returns this node's ancestors as an array
|
195
194
|
def ancestors
|
196
195
|
@ancestors = each_ancestor.to_a
|
197
196
|
end
|
198
|
-
|
197
|
+
|
199
198
|
# @return [Fixnum] the node's current depth in the tree
|
200
199
|
def depth
|
201
200
|
@depth = ancestors.length
|
202
201
|
end
|
203
|
-
|
202
|
+
|
204
203
|
# @return [Node] the root node
|
205
204
|
def root
|
206
205
|
@root = root? ? self : parent.root!
|
207
206
|
end
|
208
|
-
|
207
|
+
|
209
208
|
# @returns [Boolean] whether or not the node is the tree's root node
|
210
209
|
def root?
|
211
210
|
parent.nil?
|
212
211
|
end
|
213
212
|
|
214
|
-
|
213
|
+
|
215
214
|
# Add memoized methods. When processing citations, styles will
|
216
215
|
# typically remain stable; therefore cite processors may opt
|
217
216
|
# to use memoized versions of the following methods. These
|
@@ -223,34 +222,34 @@ module CSL
|
|
223
222
|
instance_variable_get(ivar) || send(name)
|
224
223
|
end
|
225
224
|
end
|
226
|
-
|
225
|
+
|
227
226
|
protected
|
228
|
-
|
227
|
+
|
229
228
|
# @abstract
|
230
229
|
# Called after the node was added to another node.
|
231
230
|
def added_to(node)
|
232
231
|
end
|
233
|
-
|
232
|
+
|
234
233
|
# @abstract
|
235
234
|
# Called when the node was deleted from an other node.
|
236
235
|
def deleted_from(node)
|
237
236
|
end
|
238
|
-
|
237
|
+
|
239
238
|
private
|
240
|
-
|
239
|
+
|
241
240
|
# @abstract
|
242
241
|
# Called when the passed-in node was added to this node as a child.
|
243
242
|
def added_child(node)
|
244
243
|
end
|
245
|
-
|
244
|
+
|
246
245
|
# @abstract
|
247
246
|
# Called when the passed-in node was deleted from this node's child nodes.
|
248
247
|
def deleted_child(node)
|
249
248
|
end
|
250
|
-
|
249
|
+
|
251
250
|
|
252
251
|
module ClassMethods
|
253
|
-
|
252
|
+
|
254
253
|
# Returns a new instance of an Array or Struct to manage the Node's
|
255
254
|
# children. This method is called automatically by the Node's
|
256
255
|
# constructor.
|
@@ -261,27 +260,27 @@ module CSL
|
|
261
260
|
[]
|
262
261
|
end
|
263
262
|
end
|
264
|
-
|
263
|
+
|
265
264
|
def constantize_nodename(name)
|
266
265
|
return constantize(name) if respond_to?(:constantize)
|
267
|
-
|
266
|
+
|
268
267
|
klass = name.to_s.capitalize.gsub(/(\w)-(\w)/) { [$1, $2.upcase].join }
|
269
|
-
|
268
|
+
|
270
269
|
if const_defined?(klass)
|
271
|
-
const_get(klass)
|
270
|
+
const_get(klass)
|
272
271
|
else
|
273
272
|
nil
|
274
273
|
end
|
275
274
|
end
|
276
|
-
|
277
|
-
|
275
|
+
|
276
|
+
|
278
277
|
private
|
279
|
-
|
278
|
+
|
280
279
|
def attr_child_names_for(name)
|
281
280
|
reader = name.to_s.downcase.tr('-', '_')
|
282
281
|
[name.to_sym, reader, "set_child_#{reader}", "has_#{reader}?"]
|
283
282
|
end
|
284
|
-
|
283
|
+
|
285
284
|
# Creates a Struct for the passed-in child node names that will be
|
286
285
|
# used internally by the Node to manage its children. The Struct
|
287
286
|
# will be automatically initialized and is used similarly to the
|
@@ -295,7 +294,7 @@ module CSL
|
|
295
294
|
# method will try to coerce the passed-in value into the correct
|
296
295
|
# node type automatically.
|
297
296
|
def attr_children(*names)
|
298
|
-
|
297
|
+
|
299
298
|
names.each do |name|
|
300
299
|
name, reader, writer, predicate = attr_child_names_for(name)
|
301
300
|
|
@@ -312,7 +311,7 @@ module CSL
|
|
312
311
|
define_method(writer) do |value|
|
313
312
|
begin
|
314
313
|
klass = self.class.constantize_nodename(name)
|
315
|
-
|
314
|
+
|
316
315
|
if klass
|
317
316
|
value = klass.new(value)
|
318
317
|
else
|
@@ -320,7 +319,7 @@ module CSL
|
|
320
319
|
value = (value.is_a?(String) ? TextNode : Node).new(value)
|
321
320
|
value.nodename = name.to_s
|
322
321
|
end
|
323
|
-
|
322
|
+
|
324
323
|
rescue => e
|
325
324
|
raise ArgumentError, "failed to convert #{value.inspect} to node: #{e.message}"
|
326
325
|
end unless value.respond_to?(:nodename)
|
@@ -328,20 +327,20 @@ module CSL
|
|
328
327
|
children << value
|
329
328
|
value
|
330
329
|
end
|
331
|
-
|
330
|
+
|
332
331
|
alias_method :"#{reader}=", writer unless method_defined?(:"#{reader}=")
|
333
332
|
end
|
334
333
|
end
|
335
|
-
|
334
|
+
|
336
335
|
const_set(:Children, Struct.new(*names) {
|
337
|
-
|
336
|
+
|
338
337
|
# 1.8 Compatibility
|
339
338
|
@keys = members.map(&:to_sym).freeze
|
340
|
-
|
339
|
+
|
341
340
|
class << self
|
342
341
|
attr_reader :keys
|
343
342
|
end
|
344
|
-
|
343
|
+
|
345
344
|
def initialize(attrs = {})
|
346
345
|
super(*attrs.symbolize_keys.values_at(*keys))
|
347
346
|
end
|
@@ -351,9 +350,9 @@ module CSL
|
|
351
350
|
def keys
|
352
351
|
__class__.keys
|
353
352
|
end
|
354
|
-
|
353
|
+
|
355
354
|
alias original_each each
|
356
|
-
|
355
|
+
|
357
356
|
def count
|
358
357
|
values.reject { |c| c.nil? || c.empty? }.length
|
359
358
|
end
|
@@ -373,11 +372,11 @@ module CSL
|
|
373
372
|
to_enum
|
374
373
|
end
|
375
374
|
end
|
376
|
-
|
375
|
+
|
377
376
|
def empty?
|
378
377
|
all?(&:nil?)
|
379
378
|
end
|
380
|
-
|
379
|
+
|
381
380
|
# Adds the node as a child node. Raises ValidationError if none
|
382
381
|
# of the Struct members matches the node's name. If there is
|
383
382
|
# already a node set with this node name, the node will be pushed
|
@@ -386,7 +385,7 @@ module CSL
|
|
386
385
|
unless node.respond_to?(:nodename) && keys.include?(node.nodename.to_sym)
|
387
386
|
raise ValidationError, "not allowed to add #{node.inspect} to #{inspect}"
|
388
387
|
end
|
389
|
-
|
388
|
+
|
390
389
|
current = self[node.nodename]
|
391
390
|
case current
|
392
391
|
when Array
|
@@ -396,19 +395,19 @@ module CSL
|
|
396
395
|
else
|
397
396
|
self[node.nodename] = [current, node]
|
398
397
|
end
|
399
|
-
|
398
|
+
|
400
399
|
self
|
401
400
|
end
|
402
|
-
|
401
|
+
|
403
402
|
alias << push
|
404
|
-
|
403
|
+
|
405
404
|
# Delete items from self that are equal to node. If any items are
|
406
405
|
# found, returns the deleted items. If the items is not found,
|
407
406
|
# returns nil. If the optional code block is given, returns the
|
408
407
|
# result og block if the item is not found.
|
409
408
|
def delete(node)
|
410
409
|
return nil unless node.respond_to?(:nodename)
|
411
|
-
|
410
|
+
|
412
411
|
deleted = resolve(node.nodename)
|
413
412
|
if deleted.kind_of?(Array)
|
414
413
|
deleted = deleted.delete(node)
|
@@ -419,30 +418,30 @@ module CSL
|
|
419
418
|
deleted = nil
|
420
419
|
end
|
421
420
|
end
|
422
|
-
|
421
|
+
|
423
422
|
if deleted.nil? && block_given?
|
424
423
|
yield
|
425
424
|
else
|
426
425
|
deleted
|
427
426
|
end
|
428
427
|
end
|
429
|
-
|
428
|
+
|
430
429
|
def fetch(name, default = nil)
|
431
|
-
if block_given?
|
430
|
+
if block_given?
|
432
431
|
resolve(name) || yield(key)
|
433
432
|
else
|
434
433
|
resolve(name) || default
|
435
434
|
end
|
436
435
|
end
|
437
|
-
|
436
|
+
|
438
437
|
private
|
439
|
-
|
438
|
+
|
440
439
|
def resolve(nodename)
|
441
440
|
keys.include?(nodename.to_sym) && send(nodename)
|
442
441
|
end
|
443
442
|
})
|
444
443
|
end
|
445
|
-
|
444
|
+
|
446
445
|
def alias_child(new_name, old_name)
|
447
446
|
attr_child_names_for(new_name).zip(attr_child_names_for(old_name)).each do |nn, on|
|
448
447
|
alias_method nn, on if method_defined?(on)
|
@@ -450,7 +449,7 @@ module CSL
|
|
450
449
|
end
|
451
450
|
|
452
451
|
# Turns the node into a leaf-node.
|
453
|
-
def has_no_children
|
452
|
+
def has_no_children
|
454
453
|
undef_method :add_child
|
455
454
|
undef_method :added_child
|
456
455
|
undef_method :add_children
|
@@ -459,9 +458,9 @@ module CSL
|
|
459
458
|
undef_method :delete_child
|
460
459
|
undef_method :deleted_child
|
461
460
|
undef_method :delete_children
|
462
|
-
|
461
|
+
|
463
462
|
private :children
|
464
|
-
|
463
|
+
|
465
464
|
define_method(:has_children?) do
|
466
465
|
false
|
467
466
|
end
|
@@ -471,8 +470,8 @@ module CSL
|
|
471
470
|
end
|
472
471
|
|
473
472
|
end
|
474
|
-
|
473
|
+
|
475
474
|
end
|
476
|
-
|
475
|
+
|
477
476
|
end
|
478
477
|
end
|