csl 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -21,6 +21,28 @@ module CSL
21
21
 
22
22
  alias each each_child
23
23
 
24
+ # @example
25
+ # terms.store(term)
26
+ # terms.store('book', ['book', 'books'])
27
+ # terms.store('book', 'bk', :form => 'short')
28
+ #
29
+ # Shorthand method to stores a new term translations.
30
+ #
31
+ # @param [Term, String] the term; or the the term's name
32
+ # @param [String] the term's translation
33
+ # @param [Hash] additional term attributes
34
+ #
35
+ # @return [self]
36
+ def store(term, translation = nil, options = nil)
37
+ unless term.is_a?(Term)
38
+ term = Term.new(:name => term)
39
+ term.attributes.merge(options) unless options.nil?
40
+ term.set(*translation)
41
+ end
42
+
43
+ self << term
44
+ end
45
+
24
46
  # If a style uses a term in a form that is undefined, there is a
25
47
  # fallback to other forms: "verb-short" first falls back to "verb",
26
48
  # "symbol" first falls back to "short", and "verb" and "short" both
@@ -151,7 +173,7 @@ module CSL
151
173
  delete_children tmp
152
174
  end
153
175
 
154
- private
176
+ protected
155
177
 
156
178
  # @!attribute [r] registry
157
179
  # @return [Hash] a private registry to map term names to the respective
@@ -163,6 +185,8 @@ module CSL
163
185
  # term objects for quick ordinal look-up
164
186
  attr_reader :ordinals
165
187
 
188
+ private
189
+
166
190
  def added_child(term)
167
191
  raise ValidationError, "failed to register term #{term.inspect}: name attribute missing" unless
168
192
  term.attribute?(:name)
@@ -214,7 +238,7 @@ module CSL
214
238
 
215
239
  def specialize(options)
216
240
  specialized = {}
217
-
241
+
218
242
  options.each do |key, value|
219
243
  key = key.to_sym
220
244
 
@@ -330,6 +354,21 @@ module CSL
330
354
 
331
355
  alias plural pluralize
332
356
 
357
+ alias singular= single=
358
+ alias plural= multiple=
359
+
360
+ def set(singular, plural = nil)
361
+ if plural.nil?
362
+ self.text = singular
363
+ else
364
+ self.single = singular
365
+ self.multiple = plural
366
+ end
367
+
368
+ self
369
+ end
370
+
371
+
333
372
  # @!method masculine?
334
373
  # @return [Boolean] whether or not the term is masculine
335
374
 
@@ -0,0 +1,44 @@
1
+ module CSL
2
+ module InheritableNameOptions
3
+ def inheritable_name_options
4
+ options = attributes_for(*Schema.attr(:name))
5
+
6
+ if attribute?(:'name-delimiter')
7
+ options[:delimiter] = attributes[:'name-delimiter']
8
+ end
9
+
10
+ if attribute?(:'name-form')
11
+ options[:form] = attributes[:'name-form']
12
+ end
13
+
14
+ options
15
+ end
16
+
17
+ def inheritable_names_options
18
+ return {} unless attribute? :'names-delimiter'
19
+ { :delimiter => attributes[:'names-delimiter'] }
20
+ end
21
+ end
22
+
23
+ module InheritsNameOptions
24
+ def inherits(name)
25
+ inheritable_options = "inheritable_#{name}".to_sym
26
+
27
+ define_method("inherited_#{name}") do |node, style|
28
+ options = {}
29
+
30
+ if node.respond_to?(inheritable_options)
31
+ options = node.send(inheritable_options).merge(options)
32
+ end
33
+
34
+ style ||= root
35
+
36
+ if !root? && style.respond_to?(inheritable_options)
37
+ options = style.send(inheritable_options).merge(options)
38
+ end
39
+
40
+ options
41
+ end
42
+ end
43
+ end
44
+ end
@@ -137,8 +137,10 @@ module CSL
137
137
  attr_reader :keys
138
138
  end
139
139
 
140
- def initialize(attrs = {})
141
- super(*attrs.symbolize_keys.values_at(*keys))
140
+ CSL.silence_warnings do
141
+ def initialize(attrs = {})
142
+ super(*attrs.symbolize_keys.values_at(*keys))
143
+ end
142
144
  end
143
145
 
144
146
  # @return [<Symbol>] a list of symbols representing the names/keys
@@ -190,6 +192,7 @@ module CSL
190
192
 
191
193
  self
192
194
  end
195
+ alias merge! merge
193
196
 
194
197
  # @overload values_at(selector, ... )
195
198
  # Returns an array containing the attributes in self according
@@ -231,9 +234,7 @@ module CSL
231
234
 
232
235
  def initialize_copy(other)
233
236
  super
234
- @attributes = self.class.create_attributes(other.attributes)
235
- @children = self.class.create_children
236
- @parent, @ancestors, @descendants, @siblings, @root, @depth = nil
237
+ initialize(other.attributes)
237
238
  end
238
239
 
239
240
  def deep_copy
@@ -246,6 +247,19 @@ module CSL
246
247
  copy
247
248
  end
248
249
 
250
+ def merge!(options)
251
+ attributes.merge!(options)
252
+ self
253
+ end
254
+
255
+ def reverse_merge!(options)
256
+ options.each_pair do |key, value|
257
+ attributes[key] = value unless attribute? key
258
+ end
259
+
260
+ self
261
+ end
262
+
249
263
  # @return [Boolean] whether or not the node has default attributes
250
264
  def has_default_attributes?
251
265
  !default_attributes.empty?
@@ -417,9 +431,41 @@ module CSL
417
431
  }.compact]
418
432
  end
419
433
 
434
+
420
435
  # @return [Hash] the node's formatting options
421
436
  def formatting_options
422
- attributes_for Schema.attr(:formatting)
437
+ options = attributes_for Schema.attr(:formatting)
438
+
439
+ if !root? && parent.respond_to?(:inheritable_formatting_options)
440
+ parent.inheritable_formatting_options.merge(options)
441
+ else
442
+ options
443
+ end
444
+ end
445
+
446
+ # Whether or not page ranges should be formatted when
447
+ # rendering this node.
448
+ #
449
+ # Page ranges must be formatted if the node is part of
450
+ # a {Style} with a page-range-format value.
451
+ #
452
+ # @return [Boolean] whether or not page ranges should
453
+ # be formatted
454
+ def format_page_ranges?
455
+ root.respond_to?(:has_page_range_format?) && root.has_page_range_format?
456
+ end
457
+
458
+ def page_range_format
459
+ return unless format_page_ranges?
460
+ root.page_range_format
461
+ end
462
+
463
+ def strip_periods?
464
+ attribute?(:'strip-periods') && !!(attributes[:'strip-periods'].to_s =~ /^true$/i)
465
+ end
466
+
467
+ def quotes?
468
+ attribute?(:'quotes') && !!(attributes[:'quotes'].to_s =~ /^true$/i)
423
469
  end
424
470
 
425
471
  def <=>(other)
@@ -536,6 +582,8 @@ module CSL
536
582
  true
537
583
  end
538
584
 
585
+ remove_method :empty?
586
+
539
587
  def empty?
540
588
  text.nil? || text.empty?
541
589
  end
@@ -7,21 +7,21 @@ module CSL
7
7
  include Singleton
8
8
 
9
9
  attr_accessor :parser
10
-
10
+
11
11
  @engines = {
12
12
  :nokogiri => lambda { |source|
13
13
  Nokogiri::XML::Document.parse(source, nil, nil,
14
- Nokogiri::XML::ParseOptions::DEFAULT_XML | Nokogiri::XML::ParseOptions::NOBLANKS)
14
+ Nokogiri::XML::ParseOptions::DEFAULT_XML | Nokogiri::XML::ParseOptions::NOBLANKS | Nokogiri::XML::ParseOptions::NOENT)
15
15
  },
16
16
  :default => lambda { |source|
17
17
  REXML::Document.new(source, :compress_whitespace => :all, :ignore_whitespace_nodes => :all)
18
18
  }
19
19
  }
20
-
20
+
21
21
  class << self
22
22
  attr_reader :engines
23
23
  end
24
-
24
+
25
25
  def initialize
26
26
  require 'nokogiri'
27
27
  @parser = Parser.engines[:nokogiri]
@@ -29,23 +29,23 @@ module CSL
29
29
  require 'rexml/document'
30
30
  @parser = Parser.engines[:default]
31
31
  end
32
-
32
+
33
33
  def parse(*arguments)
34
34
  parse!(*arguments)
35
35
  rescue
36
36
  nil
37
37
  end
38
-
38
+
39
39
  def parse!(source, scope = Node)
40
40
  root = parser[source].children.detect { |child| !skip?(child) }
41
41
  parse_tree root, scope
42
42
  end
43
43
 
44
44
  private
45
-
45
+
46
46
  def parse_node(node, scope = Node)
47
47
  attributes, text = parse_attributes(node), parse_text(node)
48
-
48
+
49
49
  if text
50
50
  n = TextNode.create node.name, attributes
51
51
  n.text = text
@@ -54,26 +54,26 @@ module CSL
54
54
  scope.create node.name, attributes
55
55
  end
56
56
  end
57
-
57
+
58
58
  def parse_attributes(node)
59
59
  Hash[*node.attributes.map { |n, a|
60
60
  [n.to_sym, a.respond_to?(:value) ? a.value : a.to_s]
61
61
  }.flatten]
62
62
  end
63
-
63
+
64
64
  def parse_tree(node, scope = Node)
65
65
  return nil if node.nil?
66
-
66
+
67
67
  root = parse_node node, scope
68
68
  scope = specialize_scope(root, scope)
69
-
69
+
70
70
  node.children.each do |child|
71
71
  root << parse_tree(child, scope) unless comment?(child)
72
72
  end unless root.textnode?
73
-
73
+
74
74
  root
75
75
  end
76
-
76
+
77
77
  def parse_text(node)
78
78
  if node.respond_to?(:has_text?)
79
79
  node.has_text? && node.text
@@ -82,7 +82,7 @@ module CSL
82
82
  child && child.respond_to?(:text?) && child.text? && child.text
83
83
  end
84
84
  end
85
-
85
+
86
86
  def comment?(node)
87
87
  node.respond_to?(:comment?) && node.comment? ||
88
88
  node.respond_to?(:node_type) &&
@@ -103,5 +103,5 @@ module CSL
103
103
  end
104
104
  end
105
105
  end
106
-
107
- end
106
+
107
+ end
@@ -1,17 +1,17 @@
1
1
  module CSL
2
-
2
+
3
3
  class Schema
4
-
4
+
5
5
  @version = '1.0.1'.freeze
6
6
  @major_version = '1.0'.freeze
7
7
 
8
8
  @namespace = 'http://purl.org/net/xbiblio/csl'.freeze
9
9
  @preamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".freeze
10
-
10
+
11
11
  @default_license = 'http://creativecommons.org/licenses/by-sa/3.0/'
12
12
  @default_rights_string =
13
13
  'This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License'
14
-
14
+
15
15
  @types = %w{ article article-journal article-magazine article-newspaper
16
16
  bill book broadcast chapter entry entry-dictionary entry-encyclopedia
17
17
  figure graphic interview legal_case legislation manuscript map
@@ -31,7 +31,7 @@ module CSL
31
31
 
32
32
  :number => %w{
33
33
  chapter-number collection-number edition issue number number-of-pages
34
- number-of-volumes volume
34
+ number-of-volumes volume
35
35
  },
36
36
 
37
37
  :text => %w{
@@ -60,7 +60,7 @@ module CSL
60
60
  [:date,:names,:text,:number].reduce([]) { |s,a| s.concat(@variables[a]) }.sort
61
61
 
62
62
  @variables.freeze
63
-
63
+
64
64
  @attributes = Hash.new { |h,k| h.fetch(k.to_sym, nil) }.merge({
65
65
  :affixes => %w{
66
66
  prefix suffix
@@ -72,13 +72,22 @@ module CSL
72
72
  font-style font-variant font-weight text-decoration vertical-align
73
73
  },
74
74
  :name => %w{
75
- name-form name-delimiter and delimiter-precedes-et-al initialize-with
75
+ and delimiter-precedes-et-al initialize-with
76
76
  delimiter-precedes-last et-al-min et-al-use-first et-al-subsequent-min
77
77
  et-al-subsequent-use-first et-al-use-last name-as-sort-order
78
78
  sort-separator initialize
79
79
  },
80
80
  :names => %w{
81
- names-delimiter
81
+ names-delimiter name-delimiter name-form
82
+ },
83
+ :bibliography => %w{
84
+ hanging-indent second-field-align line-spacing entry-spacing
85
+ },
86
+ :citation => %w{
87
+ disambiguate-add-names disambiguate-add-given-name
88
+ givenname-disambiguation-rule disambiguate-add-year-suffix
89
+ cite-group-delimiter collapse year-suffix-delimiter
90
+ after-collapse-delimiter near-note-distance
82
91
  },
83
92
  :conditionals => %w{
84
93
  disambiguate position
@@ -90,7 +99,7 @@ module CSL
90
99
  variable variable-any variable-all variable-none
91
100
  }
92
101
  })
93
-
102
+
94
103
  @attributes.each_value { |v| v.map!(&:to_sym).freeze }
95
104
 
96
105
  @attributes[:formatting] = [:'text-case', :display].concat(
@@ -103,11 +112,11 @@ module CSL
103
112
  :form => %w{ numeric numeric-leading-zeros ordinal long short }
104
113
  }
105
114
  })
106
-
115
+
107
116
  @values.freeze
108
117
 
109
118
  @file = File.expand_path('../../../vendor/schema/csl.rng', __FILE__)
110
-
119
+
111
120
  @validators = {
112
121
  :nokogiri => lambda { |schema, style|
113
122
  begin
@@ -117,12 +126,12 @@ module CSL
117
126
  [[0, $!.message]]
118
127
  end
119
128
  },
120
-
129
+
121
130
  :default => lambda { |schema, style|
122
131
  raise ValidationError, "please `gem install nokogiri' for validation support"
123
132
  }
124
133
  }
125
-
134
+
126
135
  begin
127
136
  # TODO enable java validator when nokogiri issue is fixed
128
137
  if RUBY_PLATFORM =~ /java/i
@@ -135,19 +144,19 @@ module CSL
135
144
  rescue LoadError
136
145
  @validator = @validators[:default]
137
146
  end
138
-
147
+
139
148
  class << self
140
-
149
+
141
150
  attr_accessor :version, :major_version, :namespace, :types,
142
151
  :variables, :categories, :attributes, :preamble, :values,
143
152
  :default_rights_string, :default_license
144
-
153
+
145
154
  private :new
146
-
155
+
147
156
  def attr(*arguments)
148
157
  attributes.values_at(*arguments).flatten(1)
149
158
  end
150
-
159
+
151
160
  # Validates the passed-in style or list of styles. The style argument(s)
152
161
  # can either be a {Style} object, a style's file handle, XML content
153
162
  # or a valid location (wildcards are supported). The method returns
@@ -172,9 +181,9 @@ module CSL
172
181
  def validate(node)
173
182
  case
174
183
  when node.is_a?(Node)
175
- validator[schema, node.to_xml]
184
+ @validator[@schema, node.to_xml]
176
185
  when node.respond_to?(:read)
177
- validator[schema, node.read]
186
+ @validator[@schema, node.read]
178
187
  when node.is_a?(Enumerable) && !node.is_a?(String)
179
188
  node.map { |n| validate(n) }.flatten(1)
180
189
  when node.respond_to?(:to_s)
@@ -182,23 +191,23 @@ module CSL
182
191
 
183
192
  case
184
193
  when node =~ /^\s*</
185
- validator[schema, node]
194
+ @validator[@schema, node]
186
195
  when File.exists?(node)
187
- validator[schema, File.open(node, 'r:UTF-8')]
196
+ @validator[@schema, File.open(node, 'r:UTF-8')]
188
197
  else
189
198
  glob = Dir.glob(node)
190
-
199
+
191
200
  if glob.empty?
192
- validator[schema, Kernel.open(node)]
201
+ @validator[@schema, Kernel.open(node)]
193
202
  else
194
- glob.map { |n| validator[schema, File.open(n, 'r:UTF-8')] }.flatten(1)
203
+ glob.map { |n| @validator[@schema, File.open(n, 'r:UTF-8')] }.flatten(1)
195
204
  end
196
205
  end
197
206
  else
198
207
  raise ArgumentError, "failed to validate #{node.inspect}: not a CSL node"
199
208
  end
200
209
  end
201
-
210
+
202
211
  # Whether or not the passed-in style (or list of styles) is valid.
203
212
  #
204
213
  # @see validate
@@ -215,11 +224,7 @@ module CSL
215
224
  def valid?(style)
216
225
  validate(style).empty?
217
226
  end
218
-
219
- private
220
-
221
- attr_reader :validators, :validator, :schema
222
227
  end
223
-
224
- end
228
+
229
+ end
225
230
  end