csl 1.0.0.pre5 → 1.0.0.pre6

Sign up to get free protection for your applications and to get access to all the features.
data/lib/csl.rb CHANGED
@@ -6,6 +6,8 @@ require 'singleton'
6
6
  require 'set'
7
7
  require 'time'
8
8
 
9
+ require 'namae'
10
+
9
11
  require 'csl/version'
10
12
 
11
13
  require 'csl/compatibility'
@@ -23,6 +25,7 @@ require 'csl/node'
23
25
  require 'csl/info'
24
26
 
25
27
  require 'csl/locale'
28
+ require 'csl/locale/ordinalize'
26
29
  require 'csl/locale/date'
27
30
  require 'csl/locale/term'
28
31
  require 'csl/locale/style_options'
data/lib/csl/info.rb CHANGED
@@ -105,16 +105,16 @@ module CSL
105
105
  # @return [self]
106
106
  def update!(timestamp = Time.now)
107
107
  ts = timestamp.respond_to?(:xmlschema) ? timestamp.xmlschema : timestamp.to_s
108
-
108
+
109
109
  if has_updated?
110
110
  updated = Updated.new { |u| u.text = ts }
111
111
  else
112
112
  updated.text = ts
113
113
  end
114
-
114
+
115
115
  self
116
116
  end
117
-
117
+
118
118
  # @return [Time,nil] when the info node's parent was published
119
119
  def published_at
120
120
  return unless has_published?
@@ -125,30 +125,30 @@ module CSL
125
125
  # @return [self]
126
126
  def publish!(timestamp = Time.now)
127
127
  ts = timestamp.respond_to?(:xmlschema) ? timestamp.xmlschema : timestamp.to_s
128
-
128
+
129
129
  if has_published?
130
130
  published = Published.new { |u| u.text = ts }
131
131
  else
132
132
  published.text = ts
133
133
  end
134
-
134
+
135
135
  self
136
136
  end
137
-
137
+
138
138
  # @return [Symbol] the parent style's citation format
139
139
  def citation_format
140
140
  return unless has_categories?
141
-
141
+
142
142
  cat = categories.detect { |c| c.attribute? :'citation-format' }
143
143
  return if cat.nil?
144
-
144
+
145
145
  cat[:'citation-format'].to_sym
146
146
  end
147
-
147
+
148
148
  def citation_format=(new_format)
149
- cat = categories.detect { |c| c.attribute? :'citation-format' }
149
+ cat = categories.detect { |c| c.attribute? :'citation-format' }
150
150
  cat = add_child Info::Category.new if cat.nil?
151
-
151
+
152
152
  cat[:'citation-format'] = new_format.to_s
153
153
  end
154
154
 
@@ -158,20 +158,22 @@ module CSL
158
158
 
159
159
  class Contributor < Node
160
160
  attr_children :name, :email, :uri
161
+ def_delegators :name, *Namae::Name.members
161
162
  end
162
163
 
163
164
  class Author < Node
164
165
  attr_children :name, :email, :uri
166
+ def_delegators :name, *Namae::Name.members
165
167
  end
166
168
 
167
169
  class Translator < Node
168
170
  attr_children :name, :email, :uri
171
+ def_delegators :name, *Namae::Name.members
169
172
  end
170
173
 
171
174
  class Link < Node
175
+ has_language
172
176
  attr_struct :href, :rel
173
-
174
- # TODO xml:lang
175
177
  end
176
178
 
177
179
  class DependentStyle < TextNode
@@ -187,6 +189,18 @@ module CSL
187
189
  end
188
190
 
189
191
  class Name < TextNode
192
+
193
+ def_delegators :namae, *Namae::Name.members
194
+
195
+ private
196
+
197
+ def namae
198
+ @namae || namae!
199
+ end
200
+
201
+ def namae!
202
+ @namae = Namae::Name.parse to_s
203
+ end
190
204
  end
191
205
 
192
206
  class Email < TextNode
@@ -196,24 +210,23 @@ module CSL
196
210
  end
197
211
 
198
212
  class Title < TextNode
199
- # TODO xml:lang
213
+ has_language
200
214
  end
201
215
 
202
216
  class TitleShort < TextNode
203
- # TODO xml:lang
217
+ has_language
204
218
  end
205
219
 
206
220
  class Summary < TextNode
207
- # TODO xml:lang
221
+ has_language
208
222
  end
209
223
 
210
224
  class Rights < TextNode
225
+ has_language
211
226
  attr_struct :license
212
- # TODO xml:lang
213
227
  end
214
228
 
215
229
  class Updated < TextNode
216
-
217
230
  def to_time
218
231
  return if empty?
219
232
  Time.parse(to_s)
data/lib/csl/locale.rb CHANGED
@@ -5,17 +5,17 @@ module CSL
5
5
  #
6
6
  class Locale < Node
7
7
  types << CSL::Info
8
-
8
+
9
9
  include Comparable
10
10
 
11
11
  @default = 'en-US'.freeze
12
12
 
13
13
  @root = File.expand_path('../../../vendor/locales', __FILE__).freeze
14
-
14
+
15
15
  @extension = '.xml'.freeze
16
16
  @prefix = 'locales-'.freeze
17
-
18
-
17
+
18
+
19
19
  # Default languages/regions.
20
20
  # Auto-detection is based on these lists.
21
21
  @regions = Hash[*%w{
@@ -28,38 +28,40 @@ module CSL
28
28
  @languages = @regions.invert.merge(Hash[*%w{
29
29
  AT de BR pt CA en CH de GB en
30
30
  }.map(&:to_sym)]).freeze
31
-
32
-
31
+
32
+
33
33
  class << self
34
34
  include Loader
35
-
35
+
36
36
  attr_accessor :default
37
37
  attr_reader :languages, :regions
38
38
 
39
39
  def parse(data)
40
40
  node = CSL.parse!(data, self)
41
-
41
+
42
42
  raise ParseError, "root node is not a locale: #{node.inspect}" unless
43
43
  node.is_a?(self)
44
-
44
+
45
45
  node
46
- end
46
+ end
47
47
  end
48
-
48
+
49
49
  attr_defaults :version => Schema.version, :xmlns => Schema.namespace
50
50
  attr_struct :xmlns, :version
51
51
 
52
52
  attr_children :'style-options', :info, :date, :terms
53
-
54
- attr_accessor :language, :region
55
-
53
+
54
+ has_language
55
+
56
+ attr_accessor :region
57
+
56
58
  alias_child :metadata, :info
57
59
  alias_child :dates, :date
58
60
  alias_child :options, :style_options
59
61
 
60
62
  private :attributes
61
63
  undef_method :[]=
62
-
64
+
63
65
  # call-seq:
64
66
  # Locale.new -> default
65
67
  # Locale.new('en') -> American English
@@ -78,10 +80,10 @@ module CSL
78
80
  when 1
79
81
  if arguments[0].is_a?(Hash)
80
82
  arguments[0] = arguments[0].symbolize_keys
81
-
83
+
82
84
  locale = arguments[0].delete(:lang) ||
83
85
  arguments[0].delete(:'xml:lang') || Locale.default
84
-
86
+
85
87
  attributes, options = arguments
86
88
  else
87
89
  attributes, locale, options = {}, arguments
@@ -91,15 +93,15 @@ module CSL
91
93
  else
92
94
  raise ArgumentError, "wrong number of arguments (#{arguments.length} for 0..2)"
93
95
  end
94
-
96
+
95
97
  super(attributes, &nil)
96
-
98
+
97
99
  set(locale) unless locale.nil?
98
-
100
+
99
101
  unless options.nil?
100
102
  children[:'style-options'] = StyleOptions.new(options)
101
103
  end
102
-
104
+
103
105
  yield self if block_given?
104
106
  end
105
107
 
@@ -107,31 +109,31 @@ module CSL
107
109
  # def initialize_copy(other)
108
110
  # @options = other.options.dup
109
111
  # end
110
-
111
-
112
+
113
+
112
114
  def added_to(node)
113
115
  raise ValidationError, "not allowed to add locale to #{node.nodename}" unless
114
116
  node.nodename == 'style'
115
117
  end
116
-
117
-
118
+
119
+
118
120
  def version
119
121
  attributes[:version]
120
122
  end
121
-
123
+
122
124
  def version=(version)
123
125
  raise ArgumentError, "failed to set version to #{version}" unless
124
126
  version.respond_to?(:to_s)
125
-
127
+
126
128
  version = version.to_s.strip
127
-
129
+
128
130
  raise ArgumentError, "failed to set version to #{version}: not a version string" unless
129
131
  version =~ /^\d[\d\.]+$/
130
132
 
131
133
  if version > Schema.version
132
134
  warn "setting version to #{version}; latest supported version is #{Schema.version}"
133
135
  end
134
-
136
+
135
137
  attributes[:version] = version
136
138
  end
137
139
 
@@ -139,24 +141,27 @@ module CSL
139
141
  def legacy?
140
142
  version < Schema.version
141
143
  end
142
-
143
- # call-seq:
144
- # locale.set('en') -> sets language to :en, region to :US
145
- # locale.set('de-AT') -> sets language to :de, region to :AT
146
- # locale.set('-DE') -> sets langauge to :de, region to :DE
144
+
145
+ # @example
146
+ # locale.set('en') #-> sets language to :en, region to :US
147
+ # locale.set('de-AT') #-> sets language to :de, region to :AT
148
+ # locale.set('-DE') #-> sets langauge to :de, region to :DE
147
149
  #
148
150
  # Sets language and region according to the passed-in locale string. If
149
151
  # the region part is not defined by the string, this method will set the
150
152
  # region to the default region for the given language.
151
153
  #
152
- # Raises ArgumentError if the argument is no valid locale string. A valid
153
- # locale string is based on the syntax of IETF language tags; it consists
154
- # of either a language or region tag (or both), separated by a hyphen.
154
+ # @raise [ArgumentError] if the argument is no valid locale string.
155
+ # A valid locale string is based on the syntax of IETF language tags;
156
+ # it consists of either a language or region tag (or both), separated
157
+ # by a hyphen.
158
+ #
159
+ # @return [self]
155
160
  def set(locale)
156
161
  language, region = locale.to_s.scan(/([a-z]{2})?(?:-([A-Z]{2}))?/)[0].map do |tag|
157
162
  tag.respond_to?(:to_sym) ? tag.to_sym : nil
158
163
  end
159
-
164
+
160
165
  case
161
166
  when language && region
162
167
  @language, @region = language, region
@@ -167,26 +172,27 @@ module CSL
167
172
  else
168
173
  raise ArgumentError, "not a valid locale string: #{locale.inspect}"
169
174
  end
170
-
175
+
171
176
  self
172
177
  end
173
-
178
+
174
179
  # Sets the locale's language and region to nil.
180
+ # @return [self]
175
181
  def clear
176
182
  @language, @region = nil
177
183
  self
178
184
  end
179
-
185
+
180
186
  def translate(*arguments)
181
187
  raise 'not implemented'
182
188
  end
183
189
 
184
190
  alias _ translate
185
191
  alias t translate
186
-
187
- # call-seq:
188
- # locale.each_term { |term| block } -> locale
189
- # locale.each_term -> enumerator
192
+
193
+ # @example
194
+ # locale.each_term { |term| block } #-> locale
195
+ # locale.each_term #-> enumerator
190
196
  #
191
197
  # Calls block once for each term defined by the locale. If no block is
192
198
  # given, an enumerator is returned instead.
@@ -198,10 +204,10 @@ module CSL
198
204
  enum_for :each_term
199
205
  end
200
206
  end
201
-
202
- # call-seq:
203
- # locale.each_date { |date_format| block } -> locale
204
- # locale.each_date -> enumerator
207
+
208
+ # @example
209
+ # locale.each_date { |date_format| block } #-> locale
210
+ # locale.each_date #-> enumerator
205
211
  #
206
212
  # Calls block once for each date format defined by the locale. If no
207
213
  # block is given, an enumerator is returned instead.
@@ -212,12 +218,12 @@ module CSL
212
218
  enum_for :each_date
213
219
  end
214
220
  end
215
-
221
+
216
222
  # @returns [Boolean] whether or not the Locale is the default locale
217
223
  def default?
218
224
  to_s == Locale.default
219
225
  end
220
-
226
+
221
227
  # @return [Boolean] whehter or not the Locale's region is the default
222
228
  # region for its language
223
229
  def default_region?
@@ -230,92 +236,10 @@ module CSL
230
236
  language && language == Locale.languages[region]
231
237
  end
232
238
 
233
- # Ordinalizes the passed-in number using either the ordinal or
234
- # long-ordinal forms defined by the locale. If a long-ordinal form is
235
- # requested but not available, the regular ordinal will be returned
236
- # instead.
237
- #
238
- # @example
239
- # Locale.load('en').ordinalize(13)
240
- # #-> "13th"
241
- #
242
- # de = Locale.load('de')
243
- # de.ordinalize(13)
244
- # #-> "13."
245
- #
246
- # de.ordinalize(3, :form => :long, :gender => :feminine)
247
- # #-> "dritte"
248
- #
249
- # @note
250
- # For CSL 1.0 (and older) locales that do not define an "ordinal-00"
251
- # term the algorithm specified by CSL 1.0 is used; otherwise uses the
252
- # CSL 1.0.1 algorithm with improved support for languages other than
253
- # English.
254
- #
255
- # @param number [#to_i] the number to ordinalize
256
- # @param options [Hash] formatting options
257
- #
258
- # @option options [:short,:long] :form (:short) which ordinals form to use
259
- # @option options [:feminine,:masculine,:neutral] :gender (:neutral)
260
- # which ordinals gender-form to use
261
- #
262
- # @raise [ArgumentError] if number cannot be converted to an integer
263
- #
264
- # @return [String] the ordinal for the passed-in number
265
- def ordinalize(number, options = {})
266
- raise ArgumentError, "unable to ordinalize #{number}; integer expected" unless
267
- number.respond_to?(:to_i)
268
-
269
- number, query = number.to_i, ordinalize_query_for(options)
270
-
271
- key = query[:name]
272
-
273
- # try to match long-ordinals first
274
- if key.start_with?('l')
275
- query[:name] = key % number.abs
276
- ordinal = terms[query]
277
-
278
- if ordinal.nil?
279
- key = 'ordinal-%02d'
280
- else
281
- return ordinal.to_s(options)
282
- end
283
- end
284
-
285
- # CSL 1.0
286
- if legacy? || terms['ordinal-00'].nil?
287
- return legacy_ordinalize(number)
288
- end
289
-
290
- # CSL 1.0.1
291
- # 1. try to find exact match
292
- # 2. if no match is found, try to match modulus of number,
293
- # dividing mod by 10 at each iteration
294
- # 3. repeat until a match is found or mod reaches 0
295
-
296
- mod = 10 ** Math.log10([number.abs, 1].max).to_i
297
-
298
- query[:name] = key % number.abs
299
- ordinal = terms[query]
300
-
301
- while ordinal.nil? && mod > 0
302
- query[:name] = key % (number.abs % mod)
303
- ordinal = terms[query]
304
- mod = mod / 10
305
- end
306
-
307
- if ordinal.nil? && query.key?(:'gender-form')
308
- query.delete(:'gender-form')
309
- ordinal = terms[query]
310
- end
311
-
312
- [number, ordinal.to_s(options)].join
313
- end
314
-
315
239
  def validate
316
240
  Schema.validate self
317
241
  end
318
-
242
+
319
243
  def valid?
320
244
  validate.empty?
321
245
  end
@@ -356,14 +280,14 @@ module CSL
356
280
  def to_s
357
281
  [language, region].compact.join('-')
358
282
  end
359
-
283
+
360
284
  # @return [String] a string representation of the Locale
361
285
  def inspect
362
286
  "#<#{self.class.name} #{to_s}>"
363
287
  end
364
-
288
+
365
289
  private
366
-
290
+
367
291
  def attribute_assignments
368
292
  if root?
369
293
  super.push('xml:lang="%s"' % to_s)
@@ -371,39 +295,11 @@ module CSL
371
295
  'xml:lang="%s"' % to_s
372
296
  end
373
297
  end
374
-
298
+
375
299
  def preamble
376
300
  Schema.preamble.dup
377
- end
378
-
379
- # @return [Hash] a valid ordinalize query; the name attribute is a format string
380
- def ordinalize_query_for(options)
381
- q = { :name => 'ordinal-%02d' }
382
-
383
- unless options.nil?
384
- if options.key?(:form) && options[:form].to_s =~ /^long(-ordinal)?$/i
385
- q[:name] = 'long-ordinal-%02d'
386
- end
387
-
388
- gender = (options[:'gender-form'] || options[:gender]).to_s
389
- unless gender.empty? || gender =~ /^n/i
390
- q[:'gender-form'] = (gender =~ /^m/i) ? 'masculine' : 'feminine'
391
- end
392
- end
393
-
394
- q
395
- end
396
-
397
- def legacy_ordinalize(number)
398
- case
399
- when (11..13).include?(number.abs % 100)
400
- [number, terms['ordinal-04']].join
401
- when (1..3).include?(number.abs % 10)
402
- [number, terms['ordinal-%02d' % (number.abs % 10)]].join
403
- else
404
- [number, terms['ordinal-04']].join
405
- end
406
301
  end
302
+
407
303
  end
408
-
304
+
409
305
  end