csl 1.0.2 → 1.1.0

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.
@@ -1,21 +1,77 @@
1
1
  module CSL
2
-
3
- class Sort < Node
4
-
5
- attr_children :key
6
-
7
- alias keys key
8
-
9
- def initialize(attributes = {})
10
- super(attributes)
11
- children[:key] = []
12
-
13
- yield self if block_given?
14
- end
15
-
16
- class Key < Node
2
+ class Style
3
+
4
+ class Sort < Node
5
+
6
+ attr_children :key
7
+
8
+ alias keys key
9
+
10
+ def initialize(attributes = {})
11
+ super(attributes)
12
+ children[:key] = []
13
+
14
+ yield self if block_given?
15
+ end
16
+
17
+ class Key < Node
18
+
19
+ attr_struct :variable, :macro, :sort,
20
+ :'names-use-min', :'names-use-first', :'names-use-last'
21
+
22
+ attr_defaults :sort => 'ascending'
23
+
24
+ has_no_children
25
+
26
+ def name_options
27
+ options = {}
28
+
29
+ options[:'et-al-use-min'] = options[:'et-al-subsequent-use-min'] =
30
+ attributes[:'names-use-min'] if attribute? :'names-use-min'
31
+
32
+ options[:'et-al-use-first'] = options[:'et-al-subsequent-use-first'] =
33
+ attributes[:'names-use-first'] if attribute? :'names-use-first'
34
+
35
+ options[:'et-al-use-last'] = options[:'et-al-subsequent-use-last'] =
36
+ attributes[:'names-use-last'] if attribute? :'names-use-last'
37
+
38
+ options
39
+ end
40
+
41
+ def ascending?
42
+ attributes[:sort] =~ /^ascending$/i
43
+ end
44
+
45
+ def ascending!
46
+ attributes[:sort] = 'ascending'
47
+ end
48
+
49
+ def descending?
50
+ !ascending?
51
+ end
52
+
53
+ def descending!
54
+ attributes[:sort] = 'descending'
55
+ end
56
+
57
+ def macro?
58
+ attribute? :macro
59
+ end
60
+
61
+ def macro
62
+ fail 'cannot resolve macro: not associated with style' unless
63
+ !root? && root.respond_to?(:macros)
64
+
65
+ root.macros[attributes[:macro].to_s]
66
+ end
67
+
68
+ def variable
69
+ attributes[:variable]
70
+ end
71
+
72
+ end
73
+
17
74
  end
18
-
75
+
19
76
  end
20
-
21
- end
77
+ end
@@ -14,7 +14,7 @@ module CSL
14
14
  def variable
15
15
  attributes[:variable]
16
16
  end
17
-
17
+
18
18
  def variable_options
19
19
  attributes_for :form
20
20
  end
@@ -25,8 +25,10 @@ module CSL
25
25
 
26
26
  # @return [Macro, nil]
27
27
  def macro
28
- raise unless parent.respond_to?(:macros)
29
- parent.macros[attributes[:macro]]
28
+ raise "failed to resolve macro #{attributes[:macro].inspect}" unless
29
+ !root? && root.respond_to?(:macros)
30
+
31
+ root.macros[attributes[:macro]]
30
32
  end
31
33
 
32
34
  def has_term?
@@ -43,4 +45,4 @@ module CSL
43
45
  end
44
46
 
45
47
  end
46
- end
48
+ end
@@ -32,7 +32,7 @@ module CSL
32
32
  end
33
33
  end
34
34
  alias delete delete_children
35
-
35
+
36
36
  # Deletes child nodes that are equal to the passed-in node. Returns all
37
37
  # deleted children. If no children were deleted, returns nil. If the
38
38
  # optional block is given, returns the result block if no children were
@@ -114,6 +114,17 @@ module CSL
114
114
  end
115
115
  alias >> find_children
116
116
 
117
+ def closest(name, conditions = {})
118
+ case
119
+ when root?
120
+ nil
121
+ when parent.match?(name, conditions)
122
+ parent
123
+ else
124
+ parent.closest(name, conditions)
125
+ end
126
+ end
127
+
117
128
  # @return [Boolean] true if this node has child nodes; false otherwise.
118
129
  def has_children?
119
130
  !empty?
@@ -287,7 +298,7 @@ module CSL
287
298
  # standard Array that normally holds the child nodes. The benefit of
288
299
  # using the Struct is that all child nodes are accessible by name and
289
300
  # need not be looked up; this improves performance, however, note that
290
- # a node defining it's children that way can only contain nodes of the
301
+ # a node defining its children that way can only contain nodes of the
291
302
  # given types.
292
303
  #
293
304
  # This method also generates accessors for each child. The writer
@@ -324,7 +335,7 @@ module CSL
324
335
  raise ArgumentError, "failed to convert #{value.inspect} to node: #{e.message}"
325
336
  end unless value.respond_to?(:nodename)
326
337
 
327
- children << value
338
+ add_child value
328
339
  value
329
340
  end
330
341
 
@@ -437,7 +448,7 @@ module CSL
437
448
  private
438
449
 
439
450
  def resolve(nodename)
440
- keys.include?(nodename.to_sym) && send(nodename)
451
+ keys.include?(nodename.to_sym) && self[nodename]
441
452
  end
442
453
  })
443
454
  end
@@ -474,4 +485,4 @@ module CSL
474
485
  end
475
486
 
476
487
  end
477
- end
488
+ end
@@ -1,3 +1,3 @@
1
- module CSL
2
- VERSION = '1.0.2'.freeze
1
+ module CSL
2
+ VERSION = '1.1.0'.freeze
3
3
  end
@@ -4,13 +4,13 @@ require 'spec_helper'
4
4
  module CSL
5
5
  describe Locale::Terms do
6
6
 
7
- it { should_not be nil }
7
+ it { should_not be nil }
8
8
 
9
- describe '#to_xml' do
10
- it 'returns <terms/> by default' do
11
- subject.to_xml.should == '<terms/>'
12
- end
13
- end
9
+ describe '#to_xml' do
10
+ it 'returns <terms/> by default' do
11
+ subject.to_xml.should == '<terms/>'
12
+ end
13
+ end
14
14
 
15
15
  describe '.specialize' do
16
16
  it 'filters the passed in hash to contain only match-able entries' do
@@ -18,40 +18,40 @@ module CSL
18
18
  end
19
19
  end
20
20
 
21
- describe '#ordinalize' do
21
+ describe '#ordinalize' do
22
22
 
23
- describe "given standard English terms" do
24
- let(:en) do
25
- Locale::Terms.parse <<-EOS
26
- <terms>
27
- <term name="ordinal">th</term>
23
+ describe "given standard English terms" do
24
+ let(:en) do
25
+ Locale::Terms.parse <<-EOS
26
+ <terms>
27
+ <term name="ordinal">th</term>
28
28
  <term name="ordinal-01">st</term>
29
29
  <term name="ordinal-02">nd</term>
30
30
  <term name="ordinal-03">rd</term>
31
31
  <term name="ordinal-11">th</term>
32
32
  <term name="ordinal-12">th</term>
33
33
  <term name="ordinal-13">th</term>
34
- </terms>
35
- EOS
36
- end
34
+ </terms>
35
+ EOS
36
+ end
37
37
 
38
38
  %w{
39
39
  ordinal ordinal-01 ordinal-02 ordinal-03 ordinal
40
40
  }.each_with_index do |ordinal, number|
41
- it "returns #{ordinal.inspect} for #{number}" do
42
- en.ordinalize(number)[:name].should == ordinal
43
- end
44
- end
45
- end
41
+ it "returns #{ordinal.inspect} for #{number}" do
42
+ en.ordinalize(number)[:name].should == ordinal
43
+ end
44
+ end
45
+ end
46
46
 
47
- end
47
+ end
48
48
 
49
49
  describe '#lookup' do
50
50
 
51
- describe "given standard English terms" do
52
- let(:en) do
53
- Locale::Terms.parse <<-EOS
54
- <terms>
51
+ describe "given standard English terms" do
52
+ let(:en) do
53
+ Locale::Terms.parse <<-EOS
54
+ <terms>
55
55
  <term name="page" form="short">
56
56
  <single>p.</single>
57
57
  <multiple>pp.</multiple>
@@ -79,42 +79,42 @@ module CSL
79
79
  </term>
80
80
  <term name="editor" form="verb">edited by</term>
81
81
  <term name="editor" form="verb-short">ed.</term>
82
- </terms>
83
- EOS
84
- end
82
+ </terms>
83
+ EOS
84
+ end
85
85
 
86
- it 'returns nil if there is no matching term' do
87
- en.lookup(:foo).should be_nil
88
- end
86
+ it 'returns nil if there is no matching term' do
87
+ en.lookup(:foo).should be_nil
88
+ end
89
89
 
90
- it 'returns the long form by default' do
91
- en.lookup(:page).should be_long
92
- end
90
+ it 'returns the long form by default' do
91
+ en.lookup(:page).should be_long
92
+ end
93
93
 
94
- it 'returns the term in the passed-in form if available' do
95
- en.lookup(:section, :form => 'long').should be_long
96
- en.lookup(:section, :form => 'short').should be_short
97
- en.lookup(:section, :form => 'symbol').should be_symbol
94
+ it 'returns the term in the passed-in form if available' do
95
+ en.lookup(:section, :form => 'long').should be_long
96
+ en.lookup(:section, :form => 'short').should be_short
97
+ en.lookup(:section, :form => 'symbol').should be_symbol
98
98
 
99
- en.lookup(:editor).should be_long
100
- en.lookup(:editor, :form => 'long').should be_long
101
- en.lookup(:editor, :form => 'verb').should be_verb
102
- en.lookup(:editor, :form => 'verb-short').should be_verb_short
103
- end
99
+ en.lookup(:editor).should be_long
100
+ en.lookup(:editor, :form => 'long').should be_long
101
+ en.lookup(:editor, :form => 'verb').should be_verb
102
+ en.lookup(:editor, :form => 'verb-short').should be_verb_short
103
+ end
104
104
 
105
- it 'returns the right fallback form if the passed-in form is not available' do
106
- en.lookup(:page, :form => 'verb').should be_long
107
- en.lookup(:page, :form => 'verb-short').should be_long
108
- en.lookup(:page, :form => 'symbol').should be_short
105
+ it 'returns the right fallback form if the passed-in form is not available' do
106
+ en.lookup(:page, :form => 'verb').should be_long
107
+ en.lookup(:page, :form => 'verb-short').should be_long
108
+ en.lookup(:page, :form => 'symbol').should be_short
109
109
  end
110
110
 
111
- it 'ignores irrelevant options' do
112
- en.lookup(:page, :plural => true).should_not be_nil
113
- end
114
- end
111
+ it 'ignores irrelevant options' do
112
+ en.lookup(:page, :plural => true).should_not be_nil
113
+ end
114
+ end
115
115
 
116
116
  end
117
- end
117
+ end
118
118
 
119
119
  describe Locale::Term do
120
120
 
@@ -129,13 +129,13 @@ module CSL
129
129
  it { should_not be_short_ordinal }
130
130
  it { should_not be_long_ordinal }
131
131
 
132
- it 'is not a textnode by default' do
133
- subject.should_not be_textnode
134
- end
132
+ it 'is not a textnode by default' do
133
+ subject.should_not be_textnode
134
+ end
135
135
 
136
- it 'is a textnode when the text is "foo"' do
137
- Locale::Term.new { |t| t.text = 'foo' }.should be_textnode
138
- end
136
+ it 'is a textnode when the text is "foo"' do
137
+ Locale::Term.new { |t| t.text = 'foo' }.should be_textnode
138
+ end
139
139
 
140
140
  describe 'gender attribute is set' do
141
141
  let(:m) { Locale::Term.new(:name => 'month-05') { |t| t.masculine!; t.text = 'Mai' } }
@@ -237,19 +237,19 @@ module CSL
237
237
  end
238
238
  end
239
239
 
240
- describe '#to_xml' do
241
- it 'returns <term/> by default' do
242
- subject.to_xml.should == '<term/>'
243
- end
240
+ describe '#to_xml' do
241
+ it 'returns <term/> by default' do
242
+ subject.to_xml.should == '<term/>'
243
+ end
244
244
 
245
- it 'returns <term>foo</term> when the text is "foo"' do
246
- Locale::Term.new { |t| t.text = 'foo' }.to_xml.should == '<term>foo</term>'
247
- end
245
+ it 'returns <term>foo</term> when the text is "foo"' do
246
+ Locale::Term.new { |t| t.text = 'foo' }.to_xml.should == '<term>foo</term>'
247
+ end
248
248
 
249
- it 'returns <term><multiple>foo</multiple></term> when multiple is "foo"' do
250
- Locale::Term.new { |t| t.multiple = 'foo' }.to_xml.should == '<term><multiple>foo</multiple></term>'
251
- end
249
+ it 'returns <term><multiple>foo</multiple></term> when multiple is "foo"' do
250
+ Locale::Term.new { |t| t.multiple = 'foo' }.to_xml.should == '<term><multiple>foo</multiple></term>'
251
+ end
252
252
 
253
- end
253
+ end
254
254
  end
255
- end
255
+ end
@@ -4,35 +4,35 @@ require 'spec_helper'
4
4
 
5
5
  module CSL
6
6
 
7
- describe Locale do
7
+ describe Locale do
8
8
 
9
- let(:locale) { Locale.new }
9
+ let(:locale) { Locale.new }
10
10
 
11
- let(:en) { Locale.new('en-US') }
12
- let(:gb) { Locale.new('en-GB') }
13
- let(:de) { Locale.new('de-DE') }
11
+ let(:en) { Locale.new('en-US') }
12
+ let(:gb) { Locale.new('en-GB') }
13
+ let(:de) { Locale.new('de-DE') }
14
14
 
15
- describe '.regions' do
15
+ describe '.regions' do
16
16
 
17
- it 'returns the default region when passed a language symbol' do
18
- Locale.regions[:en].should == :US
19
- end
17
+ it 'returns the default region when passed a language symbol' do
18
+ Locale.regions[:en].should == :US
19
+ end
20
20
 
21
- end
21
+ end
22
22
 
23
- describe '.languages' do
23
+ describe '.languages' do
24
24
 
25
- describe 'the language hash' do
26
- it 'returns the default language when passed a region string' do
27
- %w{ US en GB en AT de DE de }.map(&:to_sym).each_slice(2) do |region, language|
28
- Locale.languages[region].should == language
29
- end
30
- end
31
- end
25
+ describe 'the language hash' do
26
+ it 'returns the default language when passed a region string' do
27
+ %w{ US en GB en AT de DE de }.map(&:to_sym).each_slice(2) do |region, language|
28
+ Locale.languages[region].should == language
29
+ end
30
+ end
31
+ end
32
32
 
33
- end
33
+ end
34
34
 
35
- describe '.normalize' do
35
+ describe '.normalize' do
36
36
  {
37
37
  'en' => 'en-US',
38
38
  '-GB' => 'en-GB',
@@ -43,18 +43,18 @@ module CSL
43
43
  Locale.normalize(tag).should == expected
44
44
  end
45
45
  end
46
- end
46
+ end
47
47
 
48
- describe '.new' do
49
- it { should_not be_nil }
48
+ describe '.new' do
49
+ it { should_not be_nil }
50
50
 
51
- it 'defaults to default language' do
52
- Locale.new.language.should == Locale.default.split(/-/)[0].to_sym
53
- end
51
+ it 'defaults to default language' do
52
+ Locale.new.language.should == Locale.default.split(/-/)[0].to_sym
53
+ end
54
54
 
55
- it 'defaults to default region' do
56
- Locale.new.region.should == Locale.default.split(/-/)[1].to_sym
57
- end
55
+ it 'defaults to default region' do
56
+ Locale.new.region.should == Locale.default.split(/-/)[1].to_sym
57
+ end
58
58
 
59
59
  it 'contains no dates by default' do
60
60
  Locale.new.dates.should be_nil
@@ -64,7 +64,7 @@ module CSL
64
64
  Locale.new.terms.should be_nil
65
65
  end
66
66
 
67
- end
67
+ end
68
68
 
69
69
  describe '.load' do
70
70
 
@@ -87,80 +87,121 @@ module CSL
87
87
 
88
88
  end
89
89
 
90
- describe '#set' do
90
+ describe '#set' do
91
91
 
92
- it 'when passed "en-GB" sets language to :en and region to :GB' do
93
- locale.set('en-GB')
94
- [locale.language, locale.region].should == [:en, :GB]
95
- end
92
+ it 'when passed "en-GB" sets language to :en and region to :GB' do
93
+ locale.set('en-GB')
94
+ [locale.language, locale.region].should == [:en, :GB]
95
+ end
96
96
 
97
- it 'when passed "de" sets language to :de and region to :DE' do
98
- locale.set('de')
99
- [locale.language, locale.region].should == [:de, :DE]
100
- end
97
+ it 'when passed "de" sets language to :de and region to :DE' do
98
+ locale.set('de')
99
+ [locale.language, locale.region].should == [:de, :DE]
100
+ end
101
101
 
102
- it 'when passed "-AT" sets language to :de and region to :AT' do
103
- locale.set('-AT')
104
- [locale.language, locale.region].should == [:de, :AT]
105
- end
102
+ it 'when passed "-AT" sets language to :de and region to :AT' do
103
+ locale.set('-AT')
104
+ [locale.language, locale.region].should == [:de, :AT]
105
+ end
106
106
 
107
- end
107
+ end
108
108
 
109
- describe '#merge!' do
110
- let(:locale_with_options) { Locale.new('en', :foo => 'bar') }
109
+ describe '#merge!' do
110
+ let(:locale_with_options) { Locale.new('en', :foo => 'bar') }
111
111
 
112
- describe 'style options' do
113
- it 'does not change the options if none are set on either locale' do
114
- expect { locale.merge!(en) }.not_to change { locale.options }
115
- end
112
+ describe 'style options' do
113
+ it 'does not change the options if none are set on either locale' do
114
+ expect { locale.merge!(en) }.not_to change { locale.options }
115
+ end
116
116
 
117
- it 'creates a duplicate option element if the first locale has no options' do
118
- locale.should_not have_options
119
- locale.merge!(locale_with_options)
120
- locale.should have_options
121
- locale.options[:foo].should == 'bar'
117
+ it 'creates a duplicate option element if the first locale has no options' do
118
+ locale.should_not have_options
119
+ locale.merge!(locale_with_options)
120
+ locale.should have_options
121
+ locale.options[:foo].should == 'bar'
122
122
  locale.options.should_not equal(locale_with_options.options)
123
- end
124
-
125
- it 'merges the options if both locales have options' do
126
- locale << Locale::StyleOptions.new(:bar => 'foo')
127
-
128
- expect { locale.merge!(locale_with_options) }.not_to change { locale.options.object_id }
129
-
130
- locale.options[:foo].should == 'bar'
131
- locale.options[:bar].should == 'foo'
132
- end
133
-
134
- it 'overrides the options with those in the other locale' do
135
- locale << Locale::StyleOptions.new(:bar => 'foo', :foo => 'foo')
136
- locale.merge!(locale_with_options)
137
- locale.options[:foo].should == 'bar'
138
- locale.options[:bar].should == 'foo'
139
- end
140
- end
141
-
142
- describe 'dates' do
143
- it 'does not change the dates if none are set on either locale' do
144
- expect { locale.merge!(en) }.not_to change { locale.dates }
145
- end
146
-
147
- it 'creates duplicate date elements if the first locale has no options' do
148
- locale.merge!(Locale.load('en-US'))
149
- locale.should have_dates
150
- end
151
- end
152
- end
153
-
154
- describe '#legacy?' do
155
- it 'returns false by default' do
156
- locale.should_not be_legacy
157
- end
158
-
159
- it 'returns true if the version is less than 1.0.1' do
160
- locale.version = '0.8'
161
- locale.should be_legacy
162
- end
163
- end
164
-
165
- end
166
- end
123
+ end
124
+
125
+ it 'merges the options if both locales have options' do
126
+ locale << Locale::StyleOptions.new(:bar => 'foo')
127
+
128
+ expect { locale.merge!(locale_with_options) }.not_to change { locale.options.object_id }
129
+
130
+ locale.options[:foo].should == 'bar'
131
+ locale.options[:bar].should == 'foo'
132
+ end
133
+
134
+ it 'overrides the options with those in the other locale' do
135
+ locale << Locale::StyleOptions.new(:bar => 'foo', :foo => 'foo')
136
+ locale.merge!(locale_with_options)
137
+ locale.options[:foo].should == 'bar'
138
+ locale.options[:bar].should == 'foo'
139
+ end
140
+ end
141
+
142
+ describe 'dates' do
143
+ it 'does not change the dates if none are set on either locale' do
144
+ expect { locale.merge!(en) }.not_to change { locale.dates }
145
+ end
146
+
147
+ it 'creates duplicate date elements if the first locale has no options' do
148
+ locale.merge!(Locale.load('en-US'))
149
+ locale.should have_dates
150
+ end
151
+ end
152
+ end
153
+
154
+ describe '#legacy?' do
155
+ it 'returns false by default' do
156
+ locale.should_not be_legacy
157
+ end
158
+
159
+ it 'returns true if the version is less than 1.0.1' do
160
+ locale.version = '0.8'
161
+ locale.should be_legacy
162
+ end
163
+ end
164
+
165
+ describe '#quote' do
166
+
167
+ it 'quotes the passed-in string' do
168
+ locale.store 'open-quote', '»'
169
+ locale.store 'close-quote', '«'
170
+
171
+ locale.quote('foo').should == '»foo«'
172
+ end
173
+
174
+ it 'does not alter the string if there are no quotes in the locale' do
175
+ locale.quote('foo').should == 'foo'
176
+ end
177
+
178
+ it 'adds quotes inside final punctuation if punctuation-in-quote option is set' do
179
+ locale.store 'open-quote', '»'
180
+ locale.store 'close-quote', '«'
181
+
182
+ locale.quote('foo.').should == '»foo«.'
183
+ locale.quote('foo,').should == '»foo«,'
184
+
185
+ locale.quote('foo!').should == '»foo!«'
186
+ locale.quote('foo?').should == '»foo?«'
187
+
188
+ locale.punctuation_in_quotes!
189
+
190
+ locale.quote('foo.').should == '»foo.«'
191
+ locale.quote('foo,').should == '»foo,«'
192
+
193
+ locale.quote('foo!').should == '»foo!«'
194
+ locale.quote('foo?').should == '»foo?«'
195
+ end
196
+
197
+ it 'replaces existing quotes with inner quotes' do
198
+ locale.store 'open-quote', '“'
199
+ locale.store 'close-quote', '”'
200
+ locale.store 'open-inner-quote', '‘'
201
+ locale.store 'close-inner-quote', '’'
202
+
203
+ locale.quote('“foo”').should == '“‘foo’”'
204
+ end
205
+ end
206
+ end
207
+ end