csl 1.0.0.pre10 → 1.0.0.pre11

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.
@@ -11,32 +11,72 @@ module CSL
11
11
 
12
12
  def initialize(attributes = {})
13
13
  super(attributes)
14
- @registry, children[:term] = Hash.new { |h,k| h[k] = [] }, []
14
+ children[:term] = []
15
+
16
+ @registry = Term::Registry.new
17
+ @ordinals = Term::Registry.new
15
18
 
16
19
  yield self if block_given?
17
20
  end
18
21
 
19
22
  alias each each_child
20
23
 
21
- def lookup(query)
22
- query = { :name => query } unless query.is_a?(Hash)
24
+ def lookup(name, options = {})
25
+ options[:name] = name = name.to_s
26
+
27
+ term = registry[name].detect { |t| t.match?(options) }
28
+ return term unless term.nil? && options.delete(:'gender-form')
29
+
30
+ registry[name].detect { |t| t.match?(options) }
31
+ end
32
+ alias [] lookup
33
+
34
+ def ordinalize_modulo(number, divisor, options = {})
35
+ ordinal = ordinalize(number, options)
36
+ return unless ordinal && ordinal.match_modulo?(divisor)
37
+ ordinal
38
+ end
23
39
 
24
- terms = if query[:name].is_a?(Regexp)
25
- registry.select { |name, _| name =~ query[:name] }.flatten(1)
40
+ def ordinalize(number, options = {})
41
+ return unless has_ordinals?
42
+
43
+ if number == :default
44
+ options[:name] = 'ordinal'
26
45
  else
27
- registry[query[:name].to_s]
46
+ options[:name] = 'ordinal-%02d' % number
28
47
  end
48
+
49
+ if options[:form].to_s =~ /^long/i
50
+ options.delete :form
51
+ options[:name][0,0] = 'long-'
52
+ end
53
+
54
+ ordinal = ordinals[number].detect { |t| t.match?(options) }
55
+ return ordinal unless ordinal.nil? && options.delete(:'gender-form')
56
+
57
+ ordinals[number].detect { |t| t.match?(options) }
58
+ end
29
59
 
30
- terms.detect { |t| t.match?(query) }
60
+ # @return [Boolean] whether or not regular terms are registered at this node
61
+ def has_terms?
62
+ not registry.empty?
31
63
  end
32
- alias [] lookup
33
64
 
34
- def lookup_modulo(query, divisor)
35
- term = lookup(query)
36
- return if term.nil? || !term.match_modulo?(divisor)
37
- term
65
+ # @return [Boolean] whether or not ordinal terms are registered at this node
66
+ def has_ordinals?
67
+ not ordinals.empty?
68
+ end
69
+
70
+ def has_legacy_ordinals?
71
+ has_ordinals? && !ordinals.key?(:default)
38
72
  end
39
73
 
74
+ def drop_ordinals
75
+ tmp = ordinals.values.flatten(1)
76
+ ordinals.clear
77
+ delete_children tmp
78
+ end
79
+
40
80
  private
41
81
 
42
82
  # @!attribute [r] registry
@@ -44,17 +84,36 @@ module CSL
44
84
  # term objects for quick term look-up
45
85
  attr_reader :registry
46
86
 
87
+ # @!attribute [r] ordinals
88
+ # @return [Hash] a private registry to map ordinals to the respective
89
+ # term objects for quick ordinal look-up
90
+ attr_reader :ordinals
91
+
47
92
  def added_child(term)
48
93
  raise ValidationError, "failed to register term #{term.inspect}: name attribute missing" unless
49
94
  term.attribute?(:name)
50
95
 
51
- registry[term[:name]].push(term)
96
+ if term.ordinal?
97
+ store = ordinals[term.ordinal]
98
+ else
99
+ store = registry[term[:name]]
100
+ end
101
+
102
+ delete_children store.select { |value| term.exact_match? value }
103
+
104
+ store.push(term)
105
+
52
106
  term
53
107
  end
54
108
 
55
109
  def deleted_child(term)
56
- registry[term[:name]].delete(term)
110
+ if term.ordinal?
111
+ ordinals[term.ordinal].delete(term)
112
+ else
113
+ registry[term[:name]].delete(term)
114
+ end
57
115
  end
116
+
58
117
  end
59
118
 
60
119
  class Term < Node
@@ -76,7 +135,7 @@ module CSL
76
135
  # passed-in divisor.
77
136
  def match_modulo?(divisor)
78
137
  return false unless ordinal?
79
-
138
+
80
139
  case attributes.match
81
140
  when '2-digits'
82
141
  divisor.to_i == 100
@@ -88,24 +147,30 @@ module CSL
88
147
  true
89
148
  end
90
149
  end
91
-
92
150
  alias matches_modulo? match_modulo?
93
-
151
+
94
152
  # @return [Boolean] whether or not this term is an ordinal term
95
153
  def ordinal?
96
- /^ordinal(-\d\d+)?$/ === attributes.name
154
+ /^(long-)?ordinal(-\d\d+)?$/ === attributes.name
97
155
  end
98
-
156
+
157
+ # @return [Fixnum, :default, nil]
158
+ def ordinal
159
+ return unless ordinal?
160
+ return :default if attributes.name == 'ordinal'
161
+ attributes.name[/\d+/].to_i
162
+ end
163
+
99
164
  def gendered?
100
- !attributes.gender.blank?
165
+ not attributes.gender.blank?
101
166
  end
102
167
 
103
168
  def neutral?
104
- !gendered?
169
+ not gendered?
105
170
  end
106
171
 
107
172
  def textnode?
108
- !text.blank?
173
+ not text.blank?
109
174
  end
110
175
 
111
176
  def singularize
@@ -200,6 +265,11 @@ module CSL
200
265
  end
201
266
  end
202
267
 
268
+ class Registry < ::Hash
269
+ def initialize
270
+ super { |h,k| h[k] = [] }
271
+ end
272
+ end
203
273
  end
204
274
 
205
275
  TextNode.types << Term
data/lib/csl/node.rb CHANGED
@@ -66,29 +66,44 @@ module CSL
66
66
  end
67
67
  end
68
68
 
69
+ def parse(data)
70
+ parse!(data)
71
+ rescue
72
+ nil
73
+ end
74
+
75
+ def parse!(data)
76
+ node = CSL.parse!(data, self)
77
+
78
+ raise ParseError, "root node not #{self.name}: #{node.inspect}" unless
79
+ node.class == self || Node.equal?(self)
80
+
81
+ node
82
+ end
83
+
69
84
  private
70
85
 
71
- def has_language
72
- attr_accessor :language
73
-
74
- define_method :has_language? do
75
- !language.nil?
76
- end
77
-
78
- public :language, :language=, :has_language?
79
-
80
- alias_method :original_attribute_assignments, :attribute_assignments
81
-
82
- define_method :attribute_assignments do
83
- if has_language?
84
- original_attribute_assignments.unshift('xml:lang="%s"' % language)
85
- else
86
- original_attribute_assignments
87
- end
88
- end
89
-
90
- private :original_attribute_assignments, :attribute_assignments
91
- end
86
+ def has_language
87
+ attr_accessor :language
88
+
89
+ define_method :has_language? do
90
+ !language.nil?
91
+ end
92
+
93
+ public :language, :language=, :has_language?
94
+
95
+ alias_method :original_attribute_assignments, :attribute_assignments
96
+
97
+ define_method :attribute_assignments do
98
+ if has_language?
99
+ original_attribute_assignments.unshift('xml:lang="%s"' % language)
100
+ else
101
+ original_attribute_assignments
102
+ end
103
+ end
104
+
105
+ private :original_attribute_assignments, :attribute_assignments
106
+ end
92
107
 
93
108
  def attr_defaults(attributes)
94
109
  @default_attributes = attributes
@@ -120,13 +135,17 @@ module CSL
120
135
  __class__.keys
121
136
  end
122
137
 
138
+ def symbolize_keys
139
+ self
140
+ end
141
+
123
142
  def values
124
143
  super.compact
125
144
  end
126
145
 
127
- # def to_a
128
- # keys.zip(values_at(*keys)).reject { |k,v| v.nil? }
129
- # end
146
+ def to_hash
147
+ Hash[keys.zip(values_at(*keys)).reject { |_, v| v.nil? }]
148
+ end
130
149
 
131
150
  # @return [Boolean] true if all the attribute values are nil;
132
151
  # false otherwise.
@@ -192,6 +211,23 @@ module CSL
192
211
  yield self if block_given?
193
212
  end
194
213
 
214
+ def initialize_copy(other)
215
+ super
216
+ @attributes = self.class.create_attributes(other.attributes)
217
+ @children = self.class.create_children
218
+ @parent, @ancestors, @descendants, @siblings, @root, @depth = nil
219
+ end
220
+
221
+ def deep_copy
222
+ copy = dup
223
+
224
+ each_child do |child|
225
+ copy.add_child child.deep_copy
226
+ end
227
+
228
+ copy
229
+ end
230
+
195
231
  # Iterates through the Node's attributes
196
232
  def each
197
233
  if block_given?
@@ -215,9 +251,9 @@ module CSL
215
251
  !attributes.empty?
216
252
  end
217
253
 
218
- def has_language?
219
- false
220
- end
254
+ def has_language?
255
+ false
256
+ end
221
257
 
222
258
  def textnode?
223
259
  false
@@ -251,8 +287,13 @@ module CSL
251
287
  #
252
288
  # @see #exact_match?
253
289
  #
254
- # If the optional
255
- # @param name [String,Regexp] must match the nodename
290
+ # @example
291
+ # node.match?(name, conditions)
292
+ # node.match?(conditions)
293
+ # node.match?(other_node)
294
+ #
295
+ # @param name [String,Regexp,Node] must match the nodename; alternatively
296
+ # you can pass a node
256
297
  # @param conditions [Hash] the conditions
257
298
  #
258
299
  # @return [Boolean] whether or not the query matches the node
@@ -286,7 +327,13 @@ module CSL
286
327
  #
287
328
  # @see #match?
288
329
  #
289
- # @param name [String,Regexp] must match the nodename
330
+ # @example
331
+ # node.exact_match?(name, conditions)
332
+ # node.exact_match?(conditions)
333
+ # node.exact_match?(other_node)
334
+ #
335
+ # @param name [String,Regexp,Node] must match the nodename; alternatively
336
+ # you can pass a node
290
337
  # @param conditions [Hash] the conditions
291
338
  #
292
339
  # @return [Boolean] whether or not the query matches the node exactly
@@ -303,20 +350,20 @@ module CSL
303
350
  end
304
351
  alias matches_exactly? exact_match?
305
352
 
306
- # @option filter [Array] a list of attribute names
307
- # @return [Hash] the node's attributes matching the filter
308
- def attributes_for(*filter)
309
- filter.flatten!
353
+ # @option filter [Array] a list of attribute names
354
+ # @return [Hash] the node's attributes matching the filter
355
+ def attributes_for(*filter)
356
+ filter.flatten!
310
357
 
311
- Hash[map { |name, value|
312
- !value.nil? && filter.include?(name) ? [name, value.to_s] : nil
313
- }.compact]
314
- end
358
+ Hash[map { |name, value|
359
+ !value.nil? && filter.include?(name) ? [name, value.to_s] : nil
360
+ }.compact]
361
+ end
315
362
 
316
- # @return [Hash] the node's formatting options
317
- def formatting_options
318
- attributes_for Schema.attr(:formatting)
319
- end
363
+ # @return [Hash] the node's formatting options
364
+ def formatting_options
365
+ attributes_for Schema.attr(:formatting)
366
+ end
320
367
 
321
368
  def <=>(other)
322
369
  [nodename, attributes, children] <=> [other.nodename, other.attributes, other.children]
@@ -349,6 +396,11 @@ module CSL
349
396
  alias to_s pretty_print
350
397
 
351
398
 
399
+ protected
400
+
401
+ def match_conditions
402
+ end
403
+
352
404
  private
353
405
 
354
406
  def attribute_assignments
@@ -359,6 +411,8 @@ module CSL
359
411
 
360
412
  def match_conditions_for(name, conditions)
361
413
  case name
414
+ when Node
415
+ [name.nodename, name.attributes.to_hash]
362
416
  when Hash
363
417
  conditions, name = name, nodename
364
418
  when Symbol
data/lib/csl/parser.rb CHANGED
@@ -37,7 +37,7 @@ module CSL
37
37
  end
38
38
 
39
39
  def parse!(source, scope = Node)
40
- root = parser[source].children.detect { |child| !comment?(child) }
40
+ root = parser[source].children.detect { |child| !skip?(child) }
41
41
  parse_tree root, scope
42
42
  end
43
43
 
@@ -62,8 +62,8 @@ module CSL
62
62
  end
63
63
 
64
64
  def parse_tree(node, scope = Node)
65
- return nil if node.nil?
66
-
65
+ return nil if node.nil?
66
+
67
67
  root = parse_node node, scope
68
68
  scope = specialize_scope(root, scope)
69
69
 
@@ -83,23 +83,25 @@ module CSL
83
83
  end
84
84
  end
85
85
 
86
- def comment?(node)
87
- node.respond_to?(:comment?) && node.comment? ||
88
- node.respond_to?(:node_type) && [:comment, :xmldecl].include?(node.node_type)
89
- end
86
+ def comment?(node)
87
+ node.respond_to?(:comment?) && node.comment? ||
88
+ node.respond_to?(:node_type) &&
89
+ [:comment, :xmldecl, :processing_instruction, 7].include?(node.node_type)
90
+ end
91
+ alias skip? comment?
90
92
 
91
- def specialize_scope(root, scope = Node)
92
- case root
93
- when Style
94
- Style
95
- when Locale
96
- Locale
97
- when Info
98
- Info
99
- else
100
- scope
101
- end
102
- end
93
+ def specialize_scope(root, scope = Node)
94
+ case root
95
+ when Style
96
+ Style
97
+ when Locale
98
+ Locale
99
+ when Info
100
+ Info
101
+ else
102
+ scope
103
+ end
104
+ end
103
105
  end
104
106
 
105
107
  end
data/lib/csl/style.rb CHANGED
@@ -5,7 +5,7 @@ module CSL
5
5
 
6
6
  @default = :apa
7
7
 
8
- @root = File.expand_path('../../../vendor/styles', __FILE__).freeze
8
+ @root = '/usr/local/share/csl/styles'.freeze
9
9
 
10
10
  @extension = '.csl'.freeze
11
11
  @prefix = ''
@@ -14,15 +14,6 @@ module CSL
14
14
  include Loader
15
15
 
16
16
  attr_accessor :default
17
-
18
- def parse(data)
19
- node = CSL.parse!(data, self)
20
-
21
- raise ParseError, "root node is not a style: #{node.inspect}" unless
22
- node.is_a?(self)
23
-
24
- node
25
- end
26
17
 
27
18
  def load(input = Style.default)
28
19
  super