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 +21 -8
- data/lib/csl/loader.rb +22 -15
- data/lib/csl/locale.rb +92 -36
- data/lib/csl/locale/date.rb +16 -16
- data/lib/csl/locale/ordinalize.rb +14 -7
- data/lib/csl/locale/style_options.rb +4 -3
- data/lib/csl/locale/term.rb +92 -22
- data/lib/csl/node.rb +96 -42
- data/lib/csl/parser.rb +21 -19
- data/lib/csl/style.rb +1 -10
- data/lib/csl/version.rb +1 -1
- data/spec/csl/locale/style_options_spec.rb +1 -1
- data/spec/csl/locale/term_spec.rb +46 -19
- data/spec/csl/locale_spec.rb +78 -40
- metadata +32 -13
- data/.gitmodules +0 -6
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/
|
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/
|
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/
|
30
|
+
$ git clone https://github.com/inukshuk/csl-ruby.git
|
15
31
|
|
16
|
-
To get started, install the development dependencies
|
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/
|
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
|
-
#
|
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
|
-
|
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 =
|
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
|
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
|
-
|
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' =>
|
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
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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 =
|
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
|
data/lib/csl/locale/date.rb
CHANGED
@@ -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,
|
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',
|
36
|
-
*Schema.attr(:
|
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
|
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
|
-
|
101
|
-
|
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
|