csl 1.0.0.pre3 → 1.0.0.pre4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|