csl 1.0.0.pre5 → 1.0.0.pre6

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.
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