citeproc 1.1.0 → 1.3.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.
@@ -92,20 +92,19 @@ module CiteProc
92
92
  end
93
93
 
94
94
  # Names quack sorta like a String
95
- def_delegators :to_s, :=~, :===, *String.instance_methods(false).reject { |m|
96
- m.to_s =~ /^[\W_]|[!=_]$|^(to_s|inspect|replace|first|last|dup|clone)$/
97
- }
95
+ def_delegators :to_s,
96
+ :to_str,
97
+ :=~, :===, :match, :match?, :start_with?, :end_with?, :include?,
98
+ :upcase, :downcase, :capitalize, :sub, :gsub, :split, :strip
98
99
 
99
- # Delegate bang! methods to each field's value
100
- String.instance_methods(false).each do |m|
101
- if m.to_s.end_with?('!')
102
- define_method(m) do |*arguments, &block|
103
- Name.parts.each do |part|
104
- p = attributes[part]
105
- p.send(m, *arguments, &block) if p.respond_to?(m)
106
- end
107
- self
100
+ # Delegate in-place transformations to each field's value
101
+ [:gsub!, :sub!, :upcase!, :downcase!, :capitalize!, :strip!].each do |m|
102
+ define_method(m) do |*arguments, &block|
103
+ Name.parts.each do |part|
104
+ p = attributes[part]
105
+ p.send(m, *arguments, &block) if p.respond_to?(m)
108
106
  end
107
+ self
109
108
  end
110
109
  end
111
110
 
@@ -145,6 +144,11 @@ module CiteProc
145
144
  !empty? && !literal?
146
145
  end
147
146
 
147
+ # @return [Boolean] whether or not the Name has no given, family, or literal name
148
+ def empty?
149
+ [given, family, literal].all? { |part| part.nil? || part.empty? }
150
+ end
151
+
148
152
  # A name is `romanesque' if it contains only romanesque characters. This
149
153
  # should be the case for the majority of names written in latin- or
150
154
  # greek-based script. It will be false, for example, for names written
@@ -231,7 +235,7 @@ module CiteProc
231
235
 
232
236
  # @return [Boolean] whether or not initials will be used for printing
233
237
  def initials?
234
- !!options[:'initialize-with'] && personal? && romanesque?
238
+ !!options[:'initialize-with'] && personal? && family? && romanesque?
235
239
  end
236
240
 
237
241
  def initialize_with
@@ -239,7 +243,7 @@ module CiteProc
239
243
  end
240
244
 
241
245
  def initialize_existing_only?
242
- options[:initialize].to_s == 'false'
246
+ !CiteProc.boolean(options[:initialize], true)
243
247
  end
244
248
 
245
249
  def initialize_without_hyphen?
@@ -254,8 +258,6 @@ module CiteProc
254
258
  case
255
259
  when !initials?
256
260
  given
257
- when initialize_existing_only?
258
- existing_initials_of given
259
261
  else
260
262
  initials_of given
261
263
  end
@@ -307,7 +309,7 @@ module CiteProc
307
309
  end
308
310
 
309
311
  def to_s
310
- [given, family].compact_join(' ')
312
+ format
311
313
  end
312
314
 
313
315
  # @return [String] the name formatted according to the current options
@@ -316,7 +318,7 @@ module CiteProc
316
318
  when literal?
317
319
  literal.to_s
318
320
  when static_order?
319
- [family, initials].compact.join(' ')
321
+ [family, initials].compact.join(romanesque? ? ' ' : '')
320
322
  when !short_form?
321
323
  case
322
324
  when !sort_order?
@@ -351,7 +353,7 @@ module CiteProc
351
353
 
352
354
  # @return [String] the name as a string stripped off all markup
353
355
  def strip_markup
354
- gsub(Variable.markup, '')
356
+ to_s.gsub(Variable.markup, '')
355
357
  end
356
358
 
357
359
  # @return [self] the name with all parts stripped off markup
@@ -379,36 +381,30 @@ module CiteProc
379
381
 
380
382
  def initials_of(string)
381
383
  return unless string
382
- string = string.dup
383
-
384
- string.gsub!(/([[:upper:]])[^[:upper:]\s-]*\s*/, "\\1#{initialize_with}")
385
-
386
- initialize_hyphen!(string)
387
384
 
388
- string.strip!
389
- string
390
- end
391
-
392
- def initialize_hyphen!(string)
393
- if initialize_without_hyphen?
394
- string.tr!('-', '')
395
- else
396
- string.gsub!(/\s*-/, '-')
385
+ # Split abbreviations like "M.E" into "M. E"
386
+ string = string.gsub(/\.(?=[[:alpha:]])/, '. ')
387
+ string = string.tr('-', ' ') if initialize_without_hyphen?
388
+
389
+ string.scan(/([^\s-]+)\s*(-)?\s*/).map { |part, hyphen|
390
+ part = initial_of(part) || " #{part} "
391
+ hyphen ? "#{part.rstrip}-" : part
392
+ }.join.gsub(/-\s+/, '-').squeeze(' ').strip
393
+ end
394
+
395
+ # @return [String, nil] the initial for the given part of a name
396
+ # or nil if the part should not be initialized
397
+ def initial_of(part)
398
+ case part
399
+ when /\A([[:upper:]])\z/, /\A(.+)\.\z/
400
+ "#{$1}#{initialize_with}"
401
+ when /\A[[:upper:]]{2}[[:lower:]]/
402
+ # Two letter initials like "Ts" for "TSerendorjiin"
403
+ "#{part[0, 2].capitalize}#{initialize_with}" unless initialize_existing_only?
404
+ when /\A[[:upper:]]/
405
+ "#{part[0]}#{initialize_with}" unless initialize_existing_only?
397
406
  end
398
407
  end
399
-
400
- def existing_initials_of(string)
401
- return unless string
402
- string = string.dup
403
-
404
- string.gsub!(/([[:upper:]])([[:upper:]])/, '\1 \2')
405
- string.gsub!(/\b([[:upper:]])\b[^[:alpha:]-]*/, "\\1#{initialize_with}")
406
-
407
- initialize_hyphen!(string)
408
-
409
- string.strip!
410
- string
411
- end
412
408
  end
413
409
 
414
410
 
@@ -13,24 +13,29 @@ module CiteProc
13
13
  ['i', 1]
14
14
  ].freeze
15
15
 
16
+ # Numbers (with optional prefixes and suffixes) or roman numerals
17
+ RANGE_BOUND = /(?:[[:alnum:]]*\d[[:alnum:]]*|[ivxlcdm]+)/i
18
+
19
+ # Ranges between numbers; escaped hyphens are not range delimiters
20
+ RANGE = /(?<![[:alnum:]])#{RANGE_BOUND}\s*[–-]\s*#{RANGE_BOUND}(?![[:alnum:]])/
21
+
16
22
  class << self
17
23
  def pluralize?(string)
18
- /\S[\s,&–-]\S|\df/ === string
24
+ /\S[\s,&]\S|\df/.match?(string) || RANGE.match?(string)
19
25
  end
20
26
 
21
27
  # @param number [#to_i] the number to convert
22
- # @return [String] roman equivalent of the passed-in number
28
+ # @return [String, Integer] roman equivalent of the passed-in number;
29
+ # the number itself if it is out of range
23
30
  def romanize(number)
24
- number, roman = number.to_i, ''
31
+ number = number.to_i
25
32
 
26
- return number unless number > 0 || number < MAX_ROMAN
33
+ return number unless number > 0 && number < MAX_ROMAN
27
34
 
28
- FACTORS.each do |code, factor|
35
+ FACTORS.map { |code, factor|
29
36
  count, number = number.divmod(factor)
30
- roman << (code * count)
31
- end
32
-
33
- roman
37
+ code * count
38
+ }.join
34
39
  end
35
40
  end
36
41
 
@@ -19,11 +19,12 @@ module CiteProc
19
19
 
20
20
  attr_reader :type, :conditions, :skip_conditions, :custom_matcher
21
21
 
22
- def initialize(attributes = nil)
22
+ def initialize(attributes = nil, &block)
23
23
  @conditions, @skip_conditions = {}, {}
24
24
 
25
25
  if block_given?
26
26
  @type = :ruby
27
+ @custom_matcher = block
27
28
 
28
29
  else
29
30
  unless attributes.nil? || attributes.empty?
@@ -54,12 +55,12 @@ module CiteProc
54
55
  @type = other.type
55
56
  @conditions = other.conditions.deep_copy
56
57
  @skip_conditions = other.skip_conditions.deep_copy
57
- @custom_matcher = other
58
+ @custom_matcher = other.custom_matcher
58
59
  end
59
60
 
60
61
  def type=(type)
61
62
  raise TypeError, "failed to set selector type to #{type.inspect}" unless
62
- type.respond_to(:to_sym) && Selector.types.include?(type.to_sym)
63
+ type.respond_to?(:to_sym) && Selector.types.include?(type.to_sym)
63
64
 
64
65
  @type = type.to_sym
65
66
  end
@@ -69,7 +70,7 @@ module CiteProc
69
70
  end
70
71
 
71
72
  def custom_matcher?
72
- defined?(@custom_matcher)
73
+ !custom_matcher.nil?
73
74
  end
74
75
 
75
76
  alias ruby? custom_matcher?
@@ -15,6 +15,17 @@ module CiteProc
15
15
  process(:bibliography, items, options)
16
16
  end
17
17
 
18
+ # @param value [String, Boolean, nil] an xsd:boolean value
19
+ # @param default [Boolean] the value if the value is not set
20
+ # @return [Boolean] the boolean value
21
+ def boolean(value, default = false)
22
+ case value.to_s
23
+ when 'true', '1' then true
24
+ when 'false', '0' then false
25
+ else default
26
+ end
27
+ end
28
+
18
29
  end
19
30
 
20
31
  end
@@ -20,7 +20,7 @@ module CiteProc
20
20
  :names => %w{
21
21
  author collection-editor composer container-author recipient editor
22
22
  editorial-director illustrator interviewer original-author translator
23
- director reviewed-author
23
+ director reviewed-author narrator
24
24
  },
25
25
 
26
26
  :number => %w{
@@ -131,12 +131,15 @@ module CiteProc
131
131
  # @return [Object] the value wrapped by this variable
132
132
  attr_accessor :value
133
133
 
134
+ # In-place transformations of the value
134
135
  def_delegators :@value, :to_s,
135
- *::String.instance_methods(false).select {|m| m.to_s =~ /!$/ }
136
+ :gsub!, :sub!, :upcase!, :downcase!, :capitalize!, :strip!
136
137
 
137
- def_delegators :to_s, :=~, :===, *String.instance_methods(false).reject { |m|
138
- m.to_s =~ /^[\W_]|[!=_]$|^(to_s|inspect|replace|first|last|dup|clone|to_f|to_i)$/
139
- }
138
+ # Variables quack sorta like a String
139
+ def_delegators :to_s,
140
+ :to_str, :empty?,
141
+ :=~, :===, :match, :match?, :start_with?, :end_with?, :include?,
142
+ :upcase, :downcase, :capitalize, :sub, :gsub, :split, :strip
140
143
 
141
144
 
142
145
  # Creates new Variable for the passed-in value
@@ -226,7 +229,7 @@ module CiteProc
226
229
  #
227
230
  # @return [Boolean] whether or not the variable's value is numeric
228
231
  def numeric?
229
- !!match(/^[\w\.:;]*\d+[\w\.:;]*(\s*[,&-]\s*[\w\.:;]*\d+[\w\.:;]*)*$/i)
232
+ !!to_s.match(/^[\w\.:;]*\d+[\w\.:;]*(\s*[,&-]\s*[\w\.:;]*\d+[\w\.:;]*)*$/i)
230
233
  end
231
234
 
232
235
  def date?
@@ -247,7 +250,7 @@ module CiteProc
247
250
 
248
251
  # @return [String] the variable's value stripped of markup
249
252
  def strip_markup
250
- gsub(Variable.markup, '')
253
+ to_s.gsub(Variable.markup, '')
251
254
  end
252
255
 
253
256
  # Strips markup off the variable's value.
@@ -274,6 +277,20 @@ module CiteProc
274
277
  end
275
278
  end
276
279
 
280
+ # Variables are equal (in the sense of eql?) if they are of
281
+ # the same type and have the same value.
282
+ #
283
+ # @param other [Object] the object used for comparison
284
+ # @return [Boolean]
285
+ def eql?(other)
286
+ other.instance_of?(self.class) && value.eql?(other.value)
287
+ end
288
+
289
+ # @return [Integer] a hash value based on the type and value
290
+ def hash
291
+ [self.class, value].hash
292
+ end
293
+
277
294
  # @!method to_s
278
295
  # @return [String] the variable's value as a string
279
296
  alias to_citeproc to_s
@@ -1,3 +1,3 @@
1
1
  module CiteProc
2
- VERSION = '1.1.0'.freeze
2
+ VERSION = '1.3.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: citeproc
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sylvester Keil
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-01-18 00:00:00.000000000 Z
10
+ date: 2026-10-07 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: namae
@@ -101,14 +100,8 @@ executables: []
101
100
  extensions: []
102
101
  extra_rdoc_files: []
103
102
  files:
104
- - ".document"
105
- - ".github/workflows/ci.yml"
106
- - ".yardopts"
107
- - AGPL
108
103
  - BSDL
109
- - Gemfile
110
104
  - README.md
111
- - Rakefile
112
105
  - lib/citeproc.rb
113
106
  - lib/citeproc/abbreviate.rb
114
107
  - lib/citeproc/assets.rb
@@ -132,7 +125,6 @@ homepage: https://github.com/inukshuk/citeproc
132
125
  licenses:
133
126
  - BSD-2-Clause
134
127
  metadata: {}
135
- post_install_message:
136
128
  rdoc_options: []
137
129
  require_paths:
138
130
  - lib
@@ -140,15 +132,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
140
132
  requirements:
141
133
  - - ">="
142
134
  - !ruby/object:Gem::Version
143
- version: '2.3'
135
+ version: '3.1'
144
136
  required_rubygems_version: !ruby/object:Gem::Requirement
145
137
  requirements:
146
138
  - - ">="
147
139
  - !ruby/object:Gem::Version
148
140
  version: '0'
149
141
  requirements: []
150
- rubygems_version: 3.5.6
151
- signing_key:
142
+ rubygems_version: 4.0.22
152
143
  specification_version: 4
153
144
  summary: A cite processor interface.
154
145
  test_files: []
data/.document DELETED
@@ -1,5 +0,0 @@
1
- lib/**/*.rb
2
- bin/*
3
- -
4
- AGPL
5
- README.md
@@ -1,33 +0,0 @@
1
- name: CI
2
- on:
3
- push:
4
- branches:
5
- - master
6
- pull_request:
7
- branches:
8
- - master
9
- jobs:
10
- build:
11
- runs-on: ubuntu-latest
12
- env:
13
- BUNDLE_WITHOUT: debug:extra
14
- strategy:
15
- matrix:
16
- ruby-version:
17
- - '3.4'
18
- - '3.3'
19
- - '3.2'
20
- - '3.1'
21
- steps:
22
- - uses: actions/checkout@v4
23
- - uses: ruby/setup-ruby@v1
24
- with:
25
- ruby-version: ${{ matrix.ruby-version }}
26
- bundler-cache: true
27
- - run: bundle exec rake
28
- - name: Upload coverage results
29
- if: matrix.ruby-version == '3.3'
30
- continue-on-error: true
31
- uses: coverallsapp/github-action@v2
32
- with:
33
- github-token: ${{ github.token }}
data/.yardopts DELETED
@@ -1,2 +0,0 @@
1
- --no-private
2
- --document