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.
Files changed (77) hide show
  1. data/.document +5 -0
  2. data/.gitignore +8 -0
  3. data/.gitmodules +6 -0
  4. data/.rspec +3 -0
  5. data/.simplecov +2 -0
  6. data/.travis.yml +13 -0
  7. data/.yardopts +2 -0
  8. data/AGPL +662 -0
  9. data/BSDL +29 -0
  10. data/Gemfile +24 -0
  11. data/Guardfile +14 -0
  12. data/README.md +39 -0
  13. data/Rakefile +45 -0
  14. data/csl.gemspec +36 -0
  15. data/cucumber.yml +1 -0
  16. data/features/locales/loading.feature +57 -0
  17. data/features/locales/ordinalize.feature +861 -0
  18. data/features/parser/info.feature +27 -0
  19. data/features/parser/localized_dates.feature +35 -0
  20. data/features/parser/terms.feature +28 -0
  21. data/features/step_definitions/locale_steps.rb +34 -0
  22. data/features/step_definitions/parser_steps.rb +28 -0
  23. data/features/step_definitions/style_steps.rb +16 -0
  24. data/features/style/loading.feature +53 -0
  25. data/features/support/env.rb +8 -0
  26. data/lib/csl.rb +54 -0
  27. data/lib/csl/compatibility.rb +19 -0
  28. data/lib/csl/errors.rb +15 -0
  29. data/lib/csl/extensions.rb +63 -0
  30. data/lib/csl/info.rb +40 -0
  31. data/lib/csl/loader.rb +78 -0
  32. data/lib/csl/locale.rb +393 -0
  33. data/lib/csl/locale/date.rb +48 -0
  34. data/lib/csl/locale/style_options.rb +10 -0
  35. data/lib/csl/locale/term.rb +185 -0
  36. data/lib/csl/node.rb +285 -0
  37. data/lib/csl/parser.rb +92 -0
  38. data/lib/csl/pretty_printer.rb +33 -0
  39. data/lib/csl/schema.rb +109 -0
  40. data/lib/csl/style.rb +53 -0
  41. data/lib/csl/style/bibliography.rb +15 -0
  42. data/lib/csl/style/citation.rb +17 -0
  43. data/lib/csl/style/conditional.rb +11 -0
  44. data/lib/csl/style/date.rb +16 -0
  45. data/lib/csl/style/group.rb +9 -0
  46. data/lib/csl/style/label.rb +14 -0
  47. data/lib/csl/style/layout.rb +10 -0
  48. data/lib/csl/style/macro.rb +9 -0
  49. data/lib/csl/style/names.rb +54 -0
  50. data/lib/csl/style/number.rb +33 -0
  51. data/lib/csl/style/sort.rb +21 -0
  52. data/lib/csl/style/text.rb +10 -0
  53. data/lib/csl/treelike.rb +442 -0
  54. data/lib/csl/version.rb +3 -0
  55. data/spec/csl/info_spec.rb +116 -0
  56. data/spec/csl/locale/date_spec.rb +63 -0
  57. data/spec/csl/locale/style_options_spec.rb +19 -0
  58. data/spec/csl/locale/term_spec.rb +96 -0
  59. data/spec/csl/locale_spec.rb +128 -0
  60. data/spec/csl/node_spec.rb +100 -0
  61. data/spec/csl/parser_spec.rb +92 -0
  62. data/spec/csl/schema_spec.rb +70 -0
  63. data/spec/csl/style/bibliography_spec.rb +7 -0
  64. data/spec/csl/style/citation_spec.rb +7 -0
  65. data/spec/csl/style/conditional_spec.rb +7 -0
  66. data/spec/csl/style/date_spec.rb +11 -0
  67. data/spec/csl/style/group_spec.rb +7 -0
  68. data/spec/csl/style/label_spec.rb +7 -0
  69. data/spec/csl/style/layout_spec.rb +7 -0
  70. data/spec/csl/style/macro_spec.rb +7 -0
  71. data/spec/csl/style/names_spec.rb +23 -0
  72. data/spec/csl/style/number_spec.rb +84 -0
  73. data/spec/csl/style/text_spec.rb +7 -0
  74. data/spec/csl/style_spec.rb +19 -0
  75. data/spec/csl/treelike_spec.rb +151 -0
  76. data/spec/spec_helper.rb +30 -0
  77. metadata +192 -0
@@ -0,0 +1,33 @@
1
+ module CSL
2
+ class Style
3
+
4
+ class Number < Node
5
+ attr_struct :form, *Schema.attr(:affixes, :display, :font, :textcase)
6
+
7
+
8
+ # @return [Boolean] whether or not the number's format is set to
9
+ # :numeric; also returns true if the number's form attribute is not
10
+ # set or nil.
11
+ def numeric?
12
+ !attribute?(:form) || attributes.form.to_sym == :numeric
13
+ end
14
+
15
+ # @return [Boolean] whether or not the number's format is set to :ordinal
16
+ def ordinal?
17
+ attribute?(:form) && attributes.form.to_sym == :ordinal
18
+ end
19
+
20
+ # @return [Boolean] whether or not the number's format is set to :'long-ordinal'
21
+ def long_ordinal?
22
+ attribute?(:form) && attributes.form.to_sym == :'long-ordinal'
23
+ end
24
+
25
+ # @return [Boolean] whether or not the number's format is set to :roman
26
+ def roman?
27
+ attribute?(:form) && attributes.form.to_sym == :roman
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ module CSL
2
+
3
+ class Sort < Node
4
+
5
+ attr_children :key
6
+
7
+ alias keys key
8
+
9
+ def initialize(attributes = {})
10
+ super(attributes)
11
+ children[:key] = []
12
+
13
+ yield self if block_given?
14
+ end
15
+
16
+ class Key < Node
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,10 @@
1
+ module CSL
2
+ class Style
3
+
4
+ class Text < Node
5
+ attr_struct :macro, :term, :form, :plural, :value,
6
+ *Schema.attr(:affixes, :display, :font, :quotes, :periods, :textcase)
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,442 @@
1
+ module CSL
2
+
3
+ module Treelike
4
+
5
+ attr_accessor :parent
6
+ attr_reader :children
7
+ attr_writer :nodename
8
+
9
+ protected :parent=
10
+
11
+ def self.included(base)
12
+ base.extend(ClassMethods)
13
+ end
14
+
15
+ # @eturn [String] the node's name.
16
+ def nodename
17
+ @nodename ||= self.class.name.split(/::/)[-1].gsub(/([[:lower:]])([[:upper:]])/, '\1-\2').downcase
18
+ end
19
+
20
+ def each_child
21
+ if block_given?
22
+ children.each(&Proc.new)
23
+ self
24
+ else
25
+ enum_for :each_child
26
+ end
27
+ end
28
+
29
+ def delete_children(*nodes)
30
+ nodes.each do |node|
31
+ delete_child node
32
+ end
33
+ end
34
+
35
+ # Deletes child nodes that are equal to the passed-in node. Returns all
36
+ # deleted children. If no children were deleted, returns nil. If the
37
+ # optional block is given, returns the result block if no children were
38
+ # deleted.
39
+ def delete_child(child)
40
+ deleted = children.delete child
41
+
42
+ case
43
+ when deleted.nil? && block_given?
44
+ yield
45
+ when deleted.nil?
46
+ nil
47
+ else
48
+ [*deleted].each do |node|
49
+ node.parent = nil
50
+
51
+ deleted_child node
52
+ node.deleted_from self
53
+ end
54
+
55
+ deleted
56
+ end
57
+ rescue => e
58
+ # TODO rollback
59
+ raise e
60
+ end
61
+
62
+ def add_children(*nodes)
63
+ nodes.each do |node|
64
+ add_child node
65
+ end
66
+ end
67
+
68
+ def add_child(node)
69
+ node.unlink
70
+
71
+ node.parent = self
72
+ children << node
73
+
74
+ added_child node
75
+ node.added_to self
76
+
77
+ node
78
+ rescue => e
79
+ # TODO rollback
80
+ raise e
81
+ end
82
+
83
+ def <<(node)
84
+ add_child node
85
+ self
86
+ end
87
+
88
+ # Returns the first immediate child node whose nodename matches the
89
+ # passed-in name or pattern; returns nil there is no match.
90
+ def find_child_by_name(name)
91
+ children.detect do |child|
92
+ name === child.nodename
93
+ end
94
+ end
95
+ alias > find_child_by_name
96
+
97
+ # Returns all immediate child nodes whose nodename matches the passed-in
98
+ # name or pattern; returns an empty array if there is no match.
99
+ def find_children_by_name(name)
100
+ children.select do |child|
101
+ name === child.nodename
102
+ end
103
+ end
104
+ alias >> find_children_by_name
105
+
106
+ # Returns true if the node has child nodes; false otherwise.
107
+ def has_children?
108
+ !empty?
109
+ end
110
+
111
+ def empty?
112
+ children.empty?
113
+ end
114
+
115
+ # Unlinks the node and all its children from its parent node. Returns
116
+ # the old parent node or nil.
117
+ def unlink
118
+ return nil if root?
119
+
120
+ other = parent
121
+ other.delete_child self
122
+
123
+ self.parent = nil
124
+
125
+ other
126
+ end
127
+
128
+ def each_sibling
129
+ if block_given?
130
+ unless root?
131
+ parent.children.each do |node|
132
+ yield node unless node.equal?(self)
133
+ end
134
+ end
135
+
136
+ self
137
+ else
138
+ enum_for :each_sibling
139
+ end
140
+ end
141
+
142
+ def siblings
143
+ @siblings = each_sibling.to_a
144
+ end
145
+
146
+ def each_descendant
147
+ if block_given?
148
+ each_child do |child|
149
+ yield child
150
+ child.each_descendant(&Proc.new)
151
+ end
152
+
153
+ self
154
+ else
155
+ enum_for :each_descendant
156
+ end
157
+ end
158
+
159
+ # Returns all descendants of the node. See #descendants1 for a memoized
160
+ # version.
161
+ def descendants
162
+ @descendants = each_descendant.to_a
163
+ end
164
+
165
+ def each_ancestor
166
+ if block_given?
167
+ p = parent
168
+
169
+ until p.nil?
170
+ yield p
171
+ p = p.parent
172
+ end
173
+
174
+ self
175
+ else
176
+ enum_for :each_ancestor
177
+ end
178
+ end
179
+
180
+ # Returns this node's ancestors as an array.
181
+ def ancestors
182
+ @ancestors = each_ancestor.to_a
183
+ end
184
+
185
+ # Returns the node's current depth in the tree.
186
+ def depth
187
+ @depth = ancestors.length
188
+ end
189
+
190
+ # Returns the root node.
191
+ def root
192
+ @root = root? ? self : parent.root!
193
+ end
194
+
195
+ # Returns true if the node is a root node, or false.
196
+ def root?
197
+ parent.nil?
198
+ end
199
+
200
+
201
+ # Add memoized methods. When processing citations, styles will
202
+ # typically remain stable; therefore cite processors may opt
203
+ # to use memoized versions of the following methods. These
204
+ # versions are marked with an exclamation mark as a reminder
205
+ # that the return values are cached and potentially outdated.
206
+ %w{ ancestors descendants siblings root depth }.each do |name|
207
+ ivar = "@#{name}"
208
+ define_method("#{name}!") do
209
+ instance_variable_get(ivar) || send(name)
210
+ end
211
+ end
212
+
213
+ protected
214
+
215
+ # @abstract
216
+ # Called after the node was added to another node.
217
+ def added_to(node)
218
+ end
219
+
220
+ # @abstract
221
+ # Called when the node was deleted from an other node.
222
+ def deleted_from(node)
223
+ end
224
+
225
+ private
226
+
227
+ # @abstract
228
+ # Called when the passed-in node was added to this node as a child.
229
+ def added_child(node)
230
+ end
231
+
232
+ # @abstract
233
+ # Called when the passed-in node was deleted from this node's child nodes.
234
+ def deleted_child(node)
235
+ end
236
+
237
+
238
+ module ClassMethods
239
+
240
+ # Returns a new instance of an Array or Struct to manage the Node's
241
+ # children. This method is called automatically by the Node's
242
+ # constructor.
243
+ def create_children
244
+ if const?(:Children)
245
+ const_get(:Children).new
246
+ else
247
+ []
248
+ end
249
+ end
250
+
251
+ def constantize_nodename(name)
252
+ klass = name.to_s.capitalize.gsub(/(\w)-(\w)/) { [$1, $2.upcase].join }
253
+
254
+ case
255
+ when respond_to?(:constantize)
256
+ constantize(klass)
257
+ when const_defined?(klass)
258
+ const_get(klass)
259
+ else
260
+ nil
261
+ end
262
+ end
263
+
264
+
265
+ private
266
+
267
+ # Creates a Struct for the passed-in child node names that will be
268
+ # used internally by the Node to manage its children. The Struct
269
+ # will be automatically initialized and is used similarly to the
270
+ # standard Array that normally holds the child nodes. The benefit of
271
+ # using the Struct is that all child nodes are accessible by name and
272
+ # need not be looked up; this improves performance, however, note that
273
+ # a node defining it's children that way can only contain nodes of the
274
+ # given types.
275
+ #
276
+ # This method also generates accessors for each child.
277
+ def attr_children(*names)
278
+
279
+ names.each do |name|
280
+ name = name.to_sym
281
+ reader = name.to_s.downcase.tr('-', '_')
282
+ writer = "set_child_#{reader}"
283
+
284
+
285
+ define_method(reader) do
286
+ children[name]
287
+ end unless method_defined?(reader)
288
+
289
+ unless method_defined?(writer)
290
+ define_method(writer) do |value|
291
+ begin
292
+ klass = self.class.constantize_nodename(name)
293
+
294
+ if klass
295
+ value = klass.new(value)
296
+ else
297
+ # try to force convert value
298
+ value = (value.is_a?(String) ? TextNode : Node).new(value)
299
+ value.nodename = name.to_s
300
+ end
301
+
302
+ rescue => e
303
+ raise ArgumentError, "failed to convert #{value.inspect} to node: #{e.message}"
304
+ end unless value.respond_to?(:nodename)
305
+
306
+ children << value
307
+ end
308
+
309
+ alias_method :"#{reader}=", writer
310
+
311
+ end
312
+ end
313
+
314
+ const_set(:Children, Struct.new(*names) {
315
+
316
+ # 1.8 Compatibility
317
+ @keys = members.map(&:to_sym).freeze
318
+
319
+ class << self
320
+ attr_reader :keys
321
+ end
322
+
323
+ def initialize(attrs = {})
324
+ super(*attrs.symbolize_keys.values_at(*keys))
325
+ end
326
+
327
+ # @return [<Symbol>] a list of symbols representing the names/keys
328
+ # of the attribute variables.
329
+ def keys
330
+ self.class.keys
331
+ end
332
+
333
+ alias original_each each
334
+
335
+ # Iterates through all children. Nil values are skipped and Arrays
336
+ # expanded.
337
+ def each
338
+ if block_given?
339
+ original_each do |node|
340
+ if node.kind_of?(Array)
341
+ node.select { |n| !n.nil? }.each(&Proc.new)
342
+ else
343
+ yield node unless node.nil?
344
+ end
345
+ end
346
+ else
347
+ to_enum
348
+ end
349
+ end
350
+
351
+ def empty?
352
+ all?(&:nil?)
353
+ end
354
+
355
+ # Adds the node as a child node. Raises ValidationError if none
356
+ # of the Struct members matches the node's name. If there is
357
+ # already a node set with this node name, the node will be pushed
358
+ # to an array for that name.
359
+ def push(node)
360
+ unless node.respond_to?(:nodename) && keys.include?(node.nodename.to_sym)
361
+ raise ValidationError, "not allowed to add #{node.inspect} to #{inspect}"
362
+ end
363
+
364
+ current = self[node.nodename]
365
+ case current
366
+ when Array
367
+ current.push(node)
368
+ when nil
369
+ self[node.nodename] = node
370
+ else
371
+ self[node.nodename] = [current, node]
372
+ end
373
+
374
+ self
375
+ end
376
+
377
+ alias << push
378
+
379
+ # Delete items from self that are equal to node. If any items are
380
+ # found, returns the deleted items. If the items is not found,
381
+ # returns nil. If the optional code block is given, returns the
382
+ # result og block if the item is not found.
383
+ def delete(node)
384
+ return nil unless node.respond_to?(:nodename)
385
+
386
+ deleted = resolve(node.nodename)
387
+ if deleted.kind_of?(Array)
388
+ deleted = deleted.delete(node)
389
+ else
390
+ if deleted == node
391
+ self[node.nodename] = nil
392
+ else
393
+ deleted = nil
394
+ end
395
+ end
396
+
397
+ if deleted.nil? && block_given?
398
+ yield
399
+ else
400
+ deleted
401
+ end
402
+ end
403
+
404
+ def fetch(name, default = nil)
405
+ if block_given?
406
+ resolve(name) || yield(key)
407
+ else
408
+ resolve(name) || default
409
+ end
410
+ end
411
+
412
+ private
413
+
414
+ def resolve(nodename)
415
+ keys.include?(nodename.to_sym) && send(nodename)
416
+ end
417
+ })
418
+ end
419
+
420
+ # Turns the Node into a leaf-node.
421
+ def has_no_children
422
+ undef_method :add_child
423
+ undef_method :added_child
424
+ undef_method :add_children
425
+ undef_method :<<
426
+
427
+ undef_method :delete_child
428
+ undef_method :deleted_child
429
+ undef_method :delete_children
430
+
431
+ private :children
432
+
433
+ define_method(:has_children?) do
434
+ false
435
+ end
436
+
437
+ end
438
+
439
+ end
440
+
441
+ end
442
+ end