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.
data/README.md CHANGED
@@ -4,26 +4,39 @@ CSL-Ruby is a Ruby parser and library for the Citation Style Language (CSL),
4
4
  an XML-based format to describe the formatting of citations, notes and
5
5
  bibliographies.
6
6
 
7
- [![Build Status](https://secure.travis-ci.org/berkmancenter/csl-ruby.png?branch=master)](http://travis-ci.org/berkmancenter/csl-ruby)
7
+ [![Build Status](https://secure.travis-ci.org/inukshuk/csl-ruby.png?branch=master)](http://travis-ci.org/inukshuk/csl-ruby)
8
+
9
+ Styles and Locales
10
+ ------------------
11
+ You can load CSL styles and locales by passing a respective XML string, file
12
+ name, or URL. You can also load styles and locales by name if the
13
+ corresponding files are installed in your local styles and locale directories.
14
+ By default, CSL-Ruby looks for CSL styles and locale files in
15
+
16
+ /usr/local/share/csl/styles
17
+ /usr/local/share/csl/locales
18
+
19
+ You can change these locations by changing the value of `CSL::Style.root` and
20
+ `CSL::Locale.root` respectively.
21
+
22
+ Alternatively, you can `gem install csl-styles` to install all official CSL
23
+ styles and locales.
8
24
 
9
25
  Development
10
26
  -----------
11
- The CSL-Ruby source code is [hosted on GitHub](https://github.com/berkmancenter/csl-ruby).
27
+ The CSL-Ruby source code is [hosted on GitHub](https://github.com/inukshuk/csl-ruby).
12
28
  You can check out a copy of the latest code using Git:
13
29
 
14
- $ git clone https://github.com/berkmancenter/csl-ruby.git
30
+ $ git clone https://github.com/inukshuk/csl-ruby.git
15
31
 
16
- To get started, install the development dependencies, fetch the latest CSL
17
- styles and locales, and run all tests:
32
+ To get started, install the development dependencies and run all tests:
18
33
 
19
34
  $ cd csl-ruby
20
- $ git submodule init
21
- $ git submodule update
22
35
  $ bundle install
23
36
  $ rake
24
37
 
25
38
  If you've found a bug or have a question, please open an issue on the
26
- [issue tracker](https://github.com/berkmancenter/csl-ruby/issues).
39
+ [issue tracker](https://github.com/inukshuk/csl-ruby/issues).
27
40
  Or, for extra credit, clone the CSL-Ruby repository, write a failing
28
41
  example, fix the bug and submit a pull request.
29
42
 
data/lib/csl/loader.rb CHANGED
@@ -5,11 +5,13 @@ module CSL
5
5
  # appropriate root, prefix and extension values and a parse method that
6
6
  # will be passed the contents of the asset data.
7
7
  #
8
+ # @note
9
+ # Base classes are exepcted to define a #parse method.
8
10
  module Loader
9
-
11
+
10
12
  attr_accessor :root, :prefix, :extension
11
-
12
- # call-seq:
13
+
14
+ # @example
13
15
  # Style.load(:apa) -> style
14
16
  # Style.load('chicago-author.csl') -> style
15
17
  # Locale.load('en') -> locale
@@ -18,6 +20,9 @@ module CSL
18
20
  # Resolves the passed-in path/name or string and loads the asset data.
19
21
  # The data will be passed on to the #parse method of the base class.
20
22
  # Typically, this will return a new instance of the class.
23
+ #
24
+ # @note
25
+ # The base class is exepcted to define a #parse method.
21
26
  def load(input)
22
27
  case
23
28
  when input.respond_to?(:read)
@@ -25,7 +30,7 @@ module CSL
25
30
  when input.to_s =~ /^\s*</
26
31
  data = input
27
32
  else
28
-
33
+
29
34
  case
30
35
  when File.exists?(input.to_s)
31
36
  location = input
@@ -43,16 +48,23 @@ module CSL
43
48
  end
44
49
 
45
50
  parse(data)
46
-
51
+
47
52
  rescue => e
48
53
  raise ParseError, "failed to load #{input.inspect}: #{e.message}"
49
54
  end
50
-
55
+
56
+ def list
57
+ Dir["#{root}/#{prefix}*#{extension}"].map do |path|
58
+ File.basename(path, extension).sub(/^#{prefix}/, '')
59
+ end
60
+ end
61
+ alias ls list
62
+
51
63
  # Extends the passed-in string to a full path.
52
64
  def extend_path(string)
53
65
  File.join(root.to_s, extend_name(string))
54
66
  end
55
-
67
+
56
68
  # Extends the passed-in string to a style/locale name, by prefixing and
57
69
  # appending the default name prefix and extension.
58
70
  def extend_name(string)
@@ -61,18 +73,13 @@ module CSL
61
73
  else
62
74
  name = string.to_s.dup
63
75
  end
64
-
76
+
65
77
  unless name.start_with?(prefix.to_s)
66
78
  name = [prefix, name].join
67
79
  end
68
-
69
- name
70
- end
71
80
 
72
- # The base class is exepcted to redefine the #parse method.
73
- def parse(data)
74
- raise 'Not Implemented'
81
+ name
75
82
  end
76
83
  end
77
-
84
+
78
85
  end
data/lib/csl/locale.rb CHANGED
@@ -7,14 +7,15 @@ module CSL
7
7
  types << CSL::Info
8
8
 
9
9
  include Comparable
10
-
10
+
11
11
  @default = 'en-US'.freeze
12
12
 
13
- @root = File.expand_path('../../../vendor/locales', __FILE__).freeze
13
+ @root = '/usr/local/share/csl/locales'.freeze
14
14
 
15
15
  @extension = '.xml'.freeze
16
16
  @prefix = 'locales-'.freeze
17
17
 
18
+ @tag_pattern = /^[a-z]{2}(-[A-Z]{2})?|-[A-Z]{2}$/
18
19
 
19
20
  # Default languages/regions.
20
21
  # Auto-detection is based on these lists.
@@ -22,11 +23,11 @@ module CSL
22
23
  af ZA ar AR bg BG ca AD cs CZ da DK de DE el GR en US es ES et EE fa IR
23
24
  fr FR he IL hu HU is IS it IT ja JP km KH ko KR mn MN nb NO nl NL nn NO
24
25
  pl PL pt PT ro RO ru RU sk SK sl SI sr RS sv SE th TH tr TR uk UA vi VN
25
- zh CN zh TW
26
+ zh CN
26
27
  }.map(&:to_sym)].freeze
27
28
 
28
29
  @languages = @regions.invert.merge(Hash[*%w{
29
- AT de BR pt CA en CH de GB en
30
+ AT de BR pt CA en CH de GB en TW zh
30
31
  }.map(&:to_sym)]).freeze
31
32
 
32
33
 
@@ -36,18 +37,39 @@ module CSL
36
37
  attr_accessor :default
37
38
  attr_reader :languages, :regions
38
39
 
39
- def parse(data)
40
- node = CSL.parse!(data, self)
41
-
42
- raise ParseError, "root node is not a locale: #{node.inspect}" unless
43
- node.is_a?(self)
44
-
45
- node
46
- end
47
-
48
40
  def load(input = Locale.default)
41
+ input = normalize input if input.to_s =~ tag_pattern
49
42
  super
50
43
  end
44
+
45
+ # Normalizes an IETF tag; adds a language's default region or a
46
+ # region's default language.
47
+ #
48
+ # @example
49
+ # Locale.normalize("en") #-> "en-US"
50
+ # Locale.normalize("-BR") #-> "pt-BR"
51
+ #
52
+ # @raise [ArgumentError] if the passed-in string is no IETF tag
53
+ #
54
+ # @param tag [String] an IETF tag to be normalized
55
+ # @return [String] the normalized IETF tag
56
+ def normalize(tag)
57
+ tag = tag.to_s.strip
58
+
59
+ raise ArgumentError, "not a valid IETF tag: #{tag.inspect}" unless
60
+ tag =~ tag_pattern
61
+
62
+ language, region = tag.split(/-/)
63
+
64
+ return [language, regions[language.to_sym]].compact.join('-') if region.nil?
65
+ return [languages[region.to_sym], region].join('-') if language.empty?
66
+
67
+ tag
68
+ end
69
+
70
+ private
71
+
72
+ attr_reader :tag_pattern
51
73
  end
52
74
 
53
75
  attr_defaults :version => Schema.version, :xmlns => Schema.namespace
@@ -56,20 +78,20 @@ module CSL
56
78
  attr_children :'style-options', :info, :date, :terms
57
79
 
58
80
  has_language
59
-
81
+
60
82
  attr_accessor :region
61
83
 
62
84
  alias_child :metadata, :info
63
85
  alias_child :dates, :date
64
86
  alias_child :options, :style_options
65
87
 
66
- private :attributes
88
+ protected :attributes
67
89
  undef_method :[]=
68
90
 
69
91
  # @example
70
92
  # Locale.new #-> default
71
93
  # Locale.new('en') #-> American English
72
- # Locale.new('en', :'punctuation-in-quote' => fales) #-> with style-options
94
+ # Locale.new('en', :'punctuation-in-quote' => false) #-> with style-options
73
95
  # Locale.new(:lang => 'en-GB', :version => '1.0') #-> British English
74
96
  #
75
97
  # Returns a new locale. In the first form, the language/regions is set
@@ -90,7 +112,7 @@ module CSL
90
112
 
91
113
  attributes, options = arguments
92
114
  else
93
- attributes, locale, options = {}, arguments
115
+ attributes, locale, options = {}, *arguments
94
116
  end
95
117
  when 2
96
118
  attributes, locale, options = {}, *arguments
@@ -109,10 +131,10 @@ module CSL
109
131
  yield self if block_given?
110
132
  end
111
133
 
112
- # TODO
113
- # def initialize_copy(other)
114
- # @options = other.options.dup
115
- # end
134
+ def initialize_copy(other)
135
+ super
136
+ @language, @region = other.language, other.region
137
+ end
116
138
 
117
139
 
118
140
  def added_to(node)
@@ -162,21 +184,7 @@ module CSL
162
184
  #
163
185
  # @return [self]
164
186
  def set(locale)
165
- language, region = locale.to_s.scan(/([a-z]{2})?(?:-([A-Z]{2}))?/)[0].map do |tag|
166
- tag.respond_to?(:to_sym) ? tag.to_sym : nil
167
- end
168
-
169
- case
170
- when language && region
171
- @language, @region = language, region
172
- when language
173
- @language, @region = language, Locale.regions[language]
174
- when region
175
- @language, @region = Locale.languages[region], region
176
- else
177
- raise ArgumentError, "not a valid locale string: #{locale.inspect}"
178
- end
179
-
187
+ @language, @region = Locale.normalize(locale).split(/-/).map(&:to_sym)
180
188
  self
181
189
  end
182
190
 
@@ -246,6 +254,22 @@ module CSL
246
254
  validate.empty?
247
255
  end
248
256
 
257
+ # @return [Locale]
258
+ def merge(*others)
259
+ deep_copy.merge!(*others)
260
+ end
261
+
262
+ # @return [self]
263
+ def merge!(*others)
264
+ others.each do |other|
265
+ merge_options other
266
+ merge_dates other
267
+ end
268
+
269
+ self
270
+ end
271
+
272
+
249
273
  # Locales are sorted first by language, then by region; sort order is
250
274
  # alphabetical with the following exceptions: the default locale is
251
275
  # prioritised; in case of a language match the default region of that
@@ -302,6 +326,38 @@ module CSL
302
326
  Schema.preamble.dup
303
327
  end
304
328
 
329
+ # @param other [Locale] an other locale whose options should be merged
330
+ # @return [self]
331
+ def merge_options(other)
332
+ return self unless other.has_options?
333
+
334
+ if has_options?
335
+ options.attributes.merge! other.options.attributes
336
+ else
337
+ add_child other.options.dup
338
+ end
339
+
340
+ self
341
+ end
342
+
343
+ # @param other [Locale] an other locale whose date nodes should be merged
344
+ # @return [self]
345
+ def merge_dates(other)
346
+ return self unless other.has_dates?
347
+
348
+ if has_dates?
349
+ other.each_date do |date|
350
+ delete_children each_date.select { |d| d[:form] == date[:form] }
351
+ add_child date.deep_copy
352
+ end
353
+ else
354
+ other.each_date do |date|
355
+ add_child date.deep_copy
356
+ end
357
+ end
358
+
359
+ self
360
+ end
305
361
  end
306
362
 
307
363
  end
@@ -1,39 +1,39 @@
1
- module CSL
1
+ module CSL
2
2
  class Locale
3
-
3
+
4
4
  # A localized Date comprises a set of formatting rules for dates.
5
5
  class Date < Node
6
-
7
- attr_struct :form, :'text-case', *Schema.attr(:font, :delimiter)
6
+
7
+ attr_struct :form, *Schema.attr(:formatting, :delimiter)
8
8
  attr_children :'date-part'
9
-
9
+
10
10
  alias parts date_part
11
11
  alias locale parent
12
-
12
+
13
13
  def initialize(attributes = {})
14
14
  super(attributes)
15
15
  children[:'date-part'] = []
16
-
16
+
17
17
  yield self if block_given?
18
18
  end
19
-
19
+
20
20
  def added_to(node)
21
21
  raise ValidationError, "parent must be locale node: was #{node.inspect}" unless node.is_a?(Locale)
22
22
  end
23
-
23
+
24
24
  %w{ text numeric }.each do |type|
25
25
  define_method("#{type}?") { attributes.form == type }
26
26
  end
27
-
27
+
28
28
  end
29
29
 
30
30
  # DatePart represent the localized formatting options for an individual
31
31
  # date part (day, month, or year).
32
32
  class DatePart < Node
33
- has_no_children
34
-
35
- attr_struct :name, :form, :'range-delimiter', :'text-case',
36
- *Schema.attr(:affixes, :font, :periods)
33
+ has_no_children
34
+
35
+ attr_struct :name, :form, :'range-delimiter',
36
+ *Schema.attr(:formatting, :periods)
37
37
 
38
38
  %w{ day month year }.each do |part|
39
39
  define_method("#{part}?") do
@@ -42,7 +42,7 @@ module CSL
42
42
  end
43
43
 
44
44
  end
45
-
46
-
45
+
46
+
47
47
  end
48
48
  end
@@ -37,7 +37,9 @@ module CSL
37
37
  raise ArgumentError, "unable to ordinalize #{number}; integer expected" unless
38
38
  number.respond_to?(:to_i)
39
39
 
40
- number, query = number.to_i, ordinalize_query_for(options)
40
+ number = number.to_i
41
+
42
+
41
43
 
42
44
  key = query[:name]
43
45
 
@@ -97,8 +99,17 @@ module CSL
97
99
 
98
100
  private
99
101
 
100
- # @return [Hash] a valid ordinalize query; the name attribute is a format string
101
- def ordinalize_query_for(options)
102
+ def normalize_gender_options(options)
103
+ gender = (options[:'gender-form'] || options[:gender]).to_s
104
+ unless gender.empty? || gender =~ /^n/i
105
+ q[:'gender-form'] = (gender =~ /^m/i) ? 'masculine' : 'feminine'
106
+ end
107
+ end
108
+
109
+ def normalize_plural_options(options)
110
+ end
111
+
112
+ def ordinalize_options_for(options)
102
113
  q = { :name => 'ordinal-%02d' }
103
114
 
104
115
  unless options.nil?
@@ -106,10 +117,6 @@ module CSL
106
117
  q[:name] = 'long-ordinal-%02d'
107
118
  end
108
119
 
109
- gender = (options[:'gender-form'] || options[:gender]).to_s
110
- unless gender.empty? || gender =~ /^n/i
111
- q[:'gender-form'] = (gender =~ /^m/i) ? 'masculine' : 'feminine'
112
- end
113
120
  end
114
121
 
115
122
  q
@@ -1,10 +1,11 @@
1
1
  module CSL
2
2
  class Locale
3
-
3
+
4
4
  class StyleOptions < Node
5
5
  has_no_children
6
- attr_defaults :'punctuation-in-quote' => false
6
+ attr_defaults :'punctuation-in-quote' => false,
7
+ :'limit-day-ordinals-to-day-1' => false
7
8
  end
8
-
9
+
9
10
  end
10
11
  end