csl 1.0.0.pre10 → 1.0.0.pre11

Sign up to get free protection for your applications and to get access to all the features.
@@ -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