asciidoctor-bibliography 0.2.1 → 0.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +5 -0
  3. data/.gitignore +8 -2
  4. data/.hound.yml +3 -0
  5. data/.oss-guides.rubocop.yml +1076 -0
  6. data/.rubocop.yml +4 -10
  7. data/.travis.yml +2 -1
  8. data/Gemfile +1 -1
  9. data/README.adoc +224 -24
  10. data/Rakefile +2 -2
  11. data/asciidoctor-bibliography.gemspec +23 -23
  12. data/lib/asciidoctor-bibliography.rb +2 -2
  13. data/lib/asciidoctor-bibliography/asciidoctor.rb +4 -4
  14. data/lib/asciidoctor-bibliography/asciidoctor/bibliographer_preprocessor.rb +9 -56
  15. data/lib/asciidoctor-bibliography/bibliographer.rb +6 -7
  16. data/lib/asciidoctor-bibliography/citation.rb +56 -53
  17. data/lib/asciidoctor-bibliography/citation_item.rb +19 -10
  18. data/lib/asciidoctor-bibliography/database.rb +18 -6
  19. data/lib/asciidoctor-bibliography/databases/bibtex.rb +10 -10
  20. data/lib/asciidoctor-bibliography/errors.rb +16 -0
  21. data/lib/asciidoctor-bibliography/formatters/csl.rb +13 -2
  22. data/lib/asciidoctor-bibliography/formatters/tex.rb +42 -44
  23. data/lib/asciidoctor-bibliography/helpers.rb +9 -9
  24. data/lib/asciidoctor-bibliography/index.rb +17 -13
  25. data/lib/asciidoctor-bibliography/options.rb +97 -0
  26. data/lib/asciidoctor-bibliography/version.rb +1 -1
  27. data/samples/latex_macros_in_bibtex/sample.adoc +1 -1
  28. data/samples/standard/sample-default.adoc +3 -3
  29. data/samples/standard/sample-default.html +9 -8
  30. data/samples/standard/sample-din.adoc +1 -1
  31. data/samples/standard/sample-din.html +4 -3
  32. data/samples/standard/sample-ieee.adoc +1 -1
  33. data/samples/standard/sample-ieee.html +4 -3
  34. data/samples/tex/sample-authoryear.adoc +7 -17
  35. data/samples/tex/sample-authoryear.html +10 -32
  36. data/samples/tex/sample-numbers.adoc +7 -18
  37. data/samples/tex/sample-numbers.html +7 -29
  38. data/samples/tex/{sample-ordering.adoc → sample-sort.adoc} +6 -4
  39. data/spec/citation_item_spec.rb +67 -19
  40. data/spec/database_spec.rb +21 -16
  41. data/spec/helpers_spec.rb +46 -44
  42. data/spec/options_spec.rb +43 -0
  43. data/spec/spec_helper.rb +53 -55
  44. metadata +9 -7
  45. data/lib/asciidoctor-bibliography/exceptions.rb +0 -5
  46. data/samples/tex/sample-din.adoc +0 -74
  47. data/samples/tex/sample-din.html +0 -556
  48. data/samples/tex/sample-ordering.html +0 -467
@@ -24,19 +24,18 @@ module AsciidoctorBibliography
24
24
  end
25
25
 
26
26
  def sort
27
- if options['order'] == 'alphabetical'
28
- @occurring_keys = @occurring_keys.sort_by do |target|
29
- first_author_family_name(target)
30
- end
27
+ return unless options["order"] == "alphabetical"
28
+ @occurring_keys = @occurring_keys.sort_by do |target|
29
+ first_author_family_name(target)
31
30
  end
32
31
  end
33
32
 
34
33
  private
35
34
 
36
35
  def first_author_family_name(key)
37
- authors = database.find { |h| h['id'] == key }['author']
38
- return '' if authors.nil?
39
- authors.map { |h| h['family'] }.compact.first # TODO: is the first also alphabetically the first?
36
+ authors = database.find_entry_by_id(key)["author"]
37
+ return "" if authors.nil?
38
+ authors.map { |h| h["family"] }.compact.first # TODO: is the first also alphabetically the first?
40
39
  end
41
40
  end
42
41
  end
@@ -1,12 +1,13 @@
1
- require 'securerandom'
2
- require_relative 'formatters/csl'
3
- require_relative 'formatters/tex'
4
- require_relative 'citation_item'
1
+ require "securerandom"
2
+ require_relative "formatters/csl"
3
+ require_relative "formatters/tex"
4
+ require_relative "citation_item"
5
5
 
6
6
  module AsciidoctorBibliography
7
7
  class Citation
8
- TEX_MACROS_NAMES = Formatters::TeX::MACROS.keys.map { |s| Regexp.escape s }.concat(['fullcite']).join('|')
9
- REGEXP = /\\?(#{TEX_MACROS_NAMES}):(?:(\S*?)?\[(|.*?[^\\])\])(?:\+(\S*?)?\[(|.*?[^\\])\])*/
8
+ MACRO_NAME_REGEXP = Formatters::TeX::MACROS.keys.concat(%w[cite fullcite]).
9
+ map { |s| Regexp.escape s }.join("|").freeze
10
+ REGEXP = /\\?(#{MACRO_NAME_REGEXP}):(?:(\S*?)?\[(|.*?[^\\])\])(?:\+(\S*?)?\[(|.*?[^\\])\])*/
10
11
  REF_ATTRIBUTES = %i[chapter page section clause].freeze
11
12
 
12
13
  attr_reader :macro, :citation_items
@@ -25,70 +26,72 @@ module AsciidoctorBibliography
25
26
  end
26
27
 
27
28
  def render(bibliographer)
28
- if macro == 'cite'
29
+ case macro
30
+ when "cite"
29
31
  render_citation_with_csl(bibliographer)
30
- elsif macro == 'fullcite'
32
+ when "fullcite"
31
33
  render_fullcite_with_csl(bibliographer)
32
- elsif Formatters::TeX::MACROS.keys.include? macro
33
- formatter = Formatters::TeX.new(bibliographer.options['citation-style'])
34
+ when *Formatters::TeX::MACROS.keys
35
+ formatter = Formatters::TeX.new(bibliographer.options.tex_style)
34
36
  formatter.import bibliographer.database
35
- formatter.render(bibliographer, self)
37
+ formatter.render bibliographer, self
36
38
  end
37
39
  end
38
40
 
39
- def render_citation_with_csl(bibliographer)
40
- formatter = Formatters::CSL.new(bibliographer.options['reference-style'])
41
+ def render_fullcite_with_csl(bibliographer)
42
+ formatter = Formatters::CSL.new(bibliographer.options.style)
43
+ prepare_fullcite_item bibliographer, formatter
44
+ formatted_citation = formatter.render(:bibliography, id: citation_items.first.key).join
45
+ formatted_citation = Helpers.html_to_asciidoc formatted_citation
46
+ # We prepend an empty interpolation to avoid interferences w/ standard syntax (e.g. block role is "\n[foo]")
47
+ "{empty}" + formatted_citation
48
+ end
41
49
 
42
- cites_with_local_attributes = citation_items.map { |cite| prepare_cite_metadata bibliographer, cite }
43
- formatter.import cites_with_local_attributes
44
- formatter.sort(mode: :citation)
45
- items = formatter.data.map(&:cite)
46
- items.each { |item| prepare_citation_item item, hyperlink: bibliographer.options['hyperlinks'] == 'true' }
50
+ def prepare_fullcite_item(bibliographer, formatter)
51
+ formatter.import([bibliographer.database.find_entry_by_id(citation_items.first.key)])
52
+ end
47
53
 
54
+ def render_citation_with_csl(bibliographer)
55
+ formatter = Formatters::CSL.new(bibliographer.options.style)
56
+ items = prepare_items bibliographer, formatter
48
57
  formatted_citation = formatter.engine.renderer.render(items, formatter.engine.style.citation)
58
+ escape_brackets_inside_xref! formatted_citation
49
59
  # We prepend an empty interpolation to avoid interferences w/ standard syntax (e.g. block role is "\n[foo]")
50
- '{empty}' + formatted_citation.gsub(/{{{(?<xref_label>.*?)}}}/) do
51
- # We escape closing square brackets inside the xref label.
52
- ['[', Regexp.last_match[:xref_label].gsub(']', '\]'), ']'].join
53
- end
60
+ "{empty}" + formatted_citation
54
61
  end
55
62
 
56
- def prepare_cite_metadata(bibliographer, cite)
57
- bibliographer.database.find { |e| e['id'] == cite.key }
58
- .merge('citation-number': bibliographer.appearance_index_of(cite.key))
59
- .merge('citation-label': cite.key) # TODO: smart label generators
60
- .merge('locator': cite.locators.any? ? ' ' : nil)
61
- # TODO: why is 'locator' necessary to display locators? (and not just in the item, later)
63
+ def escape_brackets_inside_xref!(string)
64
+ string.gsub!(/{{{(?<xref_label>.*?)}}}/) do
65
+ ["[", Regexp.last_match[:xref_label].gsub("]", '\]'), "]"].join
66
+ end
62
67
  end
63
68
 
64
- def prepare_citation_item(item, hyperlink:)
65
- # Wrap into hyperlink
66
- if hyperlink
67
- item.prefix = "xref:#{xref_id(item.id)}{{{" + item.prefix.to_s
68
- item.suffix = item.suffix.to_s + '}}}'
69
- end
70
- # Assign locator
71
- locator = citation_items.find { |cite| cite.key == item.id }.locators.first
72
- item.label, item.locator = locator unless locator.nil?
73
- # TODO: suppress_author and only_author options?
69
+ def prepare_items(bibliographer, formatter)
70
+ cites_with_local_attributes = citation_items.map { |cite| prepare_metadata bibliographer, cite }
71
+ formatter.import cites_with_local_attributes
72
+ formatter.sort(mode: :citation)
73
+ formatter.data.map(&:cite).each { |item| prepare_item bibliographer.options, item }
74
74
  end
75
75
 
76
- def render_fullcite_with_csl(bibliographer)
77
- formatter = Formatters::CSL.new(bibliographer.options['reference-style'])
76
+ def prepare_metadata(bibliographer, cite)
77
+ bibliographer.database.find_entry_by_id(cite.key).
78
+ merge('citation-number': bibliographer.appearance_index_of(cite.key)).
79
+ merge('citation-label': cite.key). # TODO: smart label generators
80
+ merge('locator': cite.locator.nil? ? nil : " ")
81
+ # TODO: why is a non blank 'locator' necessary to display locators set at a later stage?
82
+ end
78
83
 
79
- # NOTE: being able to overwrite a more general family of attributes would be neat.
80
- mergeable_attributes = Helpers.slice(citation_items.first.named_attributes || {}, *REF_ATTRIBUTES.map(&:to_s))
84
+ def prepare_item(options, item)
85
+ # TODO: hyperlink, suppress_author and only_author options
86
+ ci = citation_items.detect { |c| c.key == item.id }
87
+ wrap_item item, ci.prefix, ci.suffix
88
+ wrap_item item, "xref:#{xref_id(item.id)}{{{", "}}}" if options.hyperlinks?
89
+ item.label, item.locator = ci.locator
90
+ end
81
91
 
82
- # reject empty values
83
- mergeable_attributes.reject! do |_key, value|
84
- value.nil? || value.empty?
85
- end
86
- # TODO: as is, citation items other than the first are simply ignored.
87
- database_entry = bibliographer.database.find { |e| e['id'] == citation_items.first.key }
88
- database_entry.merge!(mergeable_attributes)
89
- formatter.import([database_entry])
90
- '{empty}' + Helpers.html_to_asciidoc(formatter.render(:bibliography, id: citation_items.first.key).join)
91
- # '{empty}' + Helpers.html_to_asciidoc(formatter.render(:citation, id: citation_items.first.key))
92
+ def wrap_item(item, prefix, suffix)
93
+ item.prefix = prefix.to_s + item.prefix.to_s
94
+ item.suffix = item.suffix.to_s + suffix.to_s
92
95
  end
93
96
 
94
97
  def uuid
@@ -96,7 +99,7 @@ module AsciidoctorBibliography
96
99
  end
97
100
 
98
101
  def xref_id(key)
99
- ['bibliography', key].compact.join('-')
102
+ ["bibliography", key].compact.join("-")
100
103
  end
101
104
 
102
105
  def xref(key, label)
@@ -1,26 +1,35 @@
1
- require 'asciidoctor/attribute_list'
1
+ require "asciidoctor/attribute_list"
2
2
 
3
3
  module AsciidoctorBibliography
4
4
  class CitationItem
5
+ LOCATORS = CiteProc::CitationItem.labels.map(&:to_s).push("locator").freeze
6
+
5
7
  attr_accessor :key, :target, :positional_attributes, :named_attributes, :locators
6
8
 
7
9
  def initialize
8
10
  yield self if block_given?
9
11
  end
10
12
 
13
+ def prefix
14
+ named_attributes["prefix"]
15
+ end
16
+
17
+ def suffix
18
+ named_attributes["suffix"]
19
+ end
20
+
11
21
  def locators
12
- Helpers
13
- .slice(named_attributes || {}, *CiteProc::CitationItem.labels.map(&:to_s))
14
- .reject { |_, value| value.nil? || value.empty? } # equivalent to Hash#compact
22
+ named_attributes.select { |key, _| LOCATORS.include? key }
23
+ end
24
+
25
+ def locator
26
+ locators.first
15
27
  end
16
28
 
17
29
  def parse_attribute_list(string)
18
- parsed_attributes =
19
- ::Asciidoctor::AttributeList.new(string).parse
20
- .group_by { |hash_key, _| hash_key.is_a? Integer }
21
- .values.map { |a| Hash[a] }
22
- self.positional_attributes = parsed_attributes.first.values
23
- self.named_attributes = parsed_attributes.last
30
+ parsed_attributes = ::Asciidoctor::AttributeList.new(string).parse
31
+ self.named_attributes = parsed_attributes.reject { |key, _| key.is_a? Integer }
32
+ self.positional_attributes = parsed_attributes.select { |key, _| key.is_a? Integer }.values
24
33
  self.key = positional_attributes.shift
25
34
  end
26
35
  end
@@ -1,5 +1,5 @@
1
- require_relative 'databases/bibtex'
2
- require_relative 'exceptions'
1
+ require_relative "databases/bibtex"
2
+ require_relative "errors"
3
3
 
4
4
  module AsciidoctorBibliography
5
5
  # This is an array of citeproc entries.
@@ -14,13 +14,25 @@ module AsciidoctorBibliography
14
14
  concat Database.load(filename)
15
15
  end
16
16
 
17
+ def find_entry_by_id(id)
18
+ result = detect { |entry| entry["id"] == id }
19
+ if result.nil?
20
+ message = "No entry with id '#{id}' was found in the bibliographic database."
21
+ raise Errors::Database::IdNotFound, message
22
+ end
23
+ result
24
+ end
25
+
17
26
  def self.load(filename)
18
- case File.extname(filename)
27
+ filepath = File.expand_path filename
28
+ raise Errors::Database::FileNotFound, filepath unless File.exist?(filepath)
29
+
30
+ fileext = File.extname filepath
31
+ case fileext
19
32
  when *Databases::BibTeX::EXTENSIONS
20
- Databases::BibTeX.load(filename)
33
+ Databases::BibTeX.load filepath
21
34
  else
22
- raise Exceptions::DatabaseFormatNotSupported,
23
- 'Bibliographic database format not supported.'
35
+ raise Errors::Database::UnsupportedFormat, fileext
24
36
  end
25
37
  end
26
38
  end
@@ -1,12 +1,12 @@
1
- require 'bibtex'
2
- require 'bibtex/filters'
3
- require 'latex/decode/base'
4
- require 'latex/decode/maths'
5
- require 'latex/decode/accents'
6
- require 'latex/decode/diacritics'
7
- require 'latex/decode/punctuation'
8
- require 'latex/decode/symbols'
9
- require 'latex/decode/greek'
1
+ require "bibtex"
2
+ require "bibtex/filters"
3
+ require "latex/decode/base"
4
+ require "latex/decode/maths"
5
+ require "latex/decode/accents"
6
+ require "latex/decode/diacritics"
7
+ require "latex/decode/punctuation"
8
+ require "latex/decode/symbols"
9
+ require "latex/decode/greek"
10
10
 
11
11
  module AsciidoctorBibliography
12
12
  module Databases
@@ -33,7 +33,7 @@ module AsciidoctorBibliography
33
33
  LaTeX::Decode::Symbols.decode!(text)
34
34
  LaTeX::Decode::Greek.decode!(text)
35
35
  text.gsub!(/\\url\{(.+?)\}/, ' \\1 ')
36
- text.gsub!(/\\\w+(?=\s+\w)/, '')
36
+ text.gsub!(/\\\w+(?=\s+\w)/, "")
37
37
  text.gsub!(/\\\w+(?:\[.+?\])?\s*\{(.+?)\}/, '\\1')
38
38
  LaTeX::Decode::Base.strip_braces(text)
39
39
  LaTeX.normalize_C(text)
@@ -0,0 +1,16 @@
1
+ module AsciidoctorBibliography
2
+ module Errors
3
+ class Error < StandardError; end
4
+
5
+ module Options
6
+ class Missing < Error; end
7
+ class Invalid < Error; end
8
+ end
9
+
10
+ module Database
11
+ class UnsupportedFormat < Error; end
12
+ class FileNotFound < Error; end
13
+ class IdNotFound < Error; end
14
+ end
15
+ end
16
+ end
@@ -1,5 +1,6 @@
1
- require 'citeproc'
2
- require 'csl/styles'
1
+ require "citeproc"
2
+ require "csl/styles"
3
+ require "yaml"
3
4
 
4
5
  module AsciidoctorBibliography
5
6
  module Formatters
@@ -8,6 +9,16 @@ module AsciidoctorBibliography
8
9
  super style: style, format: :html
9
10
  end
10
11
 
12
+ def replace_bibliography_sort(array)
13
+ new_keys = array.map(&::CSL::Style::Sort::Key.method(:new))
14
+ new_sort = ::CSL::Style::Sort.new.add_children(*new_keys)
15
+
16
+ bibliography = engine.style.find_child("bibliography")
17
+ bibliography.find_child("sort")&.unlink
18
+
19
+ bibliography.add_child new_sort
20
+ end
21
+
11
22
  def sort(mode:)
12
23
  # Valid modes are :citation and :bibliography
13
24
  engine.sort! data, engine.style.send(mode).sort_keys if engine.style.send(mode).sort?
@@ -3,20 +3,19 @@ module AsciidoctorBibliography
3
3
  # This formatter emulates the behaviour of traditional Bib(La)TeX/NatBib citations.
4
4
  class TeX
5
5
  MACROS = {
6
- # NOTE: cite = citet
7
- 'cite' => { type: :textual, bracketed: true, authors: :abbreviated },
8
- 'citet' => { type: :textual, bracketed: true, authors: :abbreviated },
9
- 'citet*' => { type: :textual, bracketed: true, authors: :full },
10
- 'citealt' => { type: :textual, bracketed: false, authors: :abbreviated },
11
- 'citealt*' => { type: :textual, bracketed: false, authors: :full },
12
- 'citep' => { type: :parenthetical, bracketed: true, authors: :abbreviated },
13
- 'citep*' => { type: :parenthetical, bracketed: true, authors: :full },
14
- 'citealp' => { type: :parenthetical, bracketed: false, authors: :abbreviated },
15
- 'citealp*' => { type: :parenthetical, bracketed: false, authors: :full },
16
- 'citeauthor' => { type: :authors_only, bracketed: false, authors: :abbreviated },
17
- 'citeauthor*' => { type: :authors_only, bracketed: false, authors: :full },
18
- 'citeyear' => { type: :years_only, bracketed: false },
19
- 'citeyearpar' => { type: :years_only, bracketed: true }
6
+ # NOTE: \citet is equivalent to \cite, so we reserve the latter for CSL styling.
7
+ "citet" => { type: :textual, bracketed: true, authors: :abbreviated },
8
+ "citet*" => { type: :textual, bracketed: true, authors: :full },
9
+ "citealt" => { type: :textual, bracketed: false, authors: :abbreviated },
10
+ "citealt*" => { type: :textual, bracketed: false, authors: :full },
11
+ "citep" => { type: :parenthetical, bracketed: true, authors: :abbreviated },
12
+ "citep*" => { type: :parenthetical, bracketed: true, authors: :full },
13
+ "citealp" => { type: :parenthetical, bracketed: false, authors: :abbreviated },
14
+ "citealp*" => { type: :parenthetical, bracketed: false, authors: :full },
15
+ "citeauthor" => { type: :authors_only, bracketed: false, authors: :abbreviated },
16
+ "citeauthor*" => { type: :authors_only, bracketed: false, authors: :full },
17
+ "citeyear" => { type: :years_only, bracketed: false },
18
+ "citeyearpar" => { type: :years_only, bracketed: true },
20
19
  }.freeze
21
20
 
22
21
  attr_accessor :opening_bracket,
@@ -27,10 +26,10 @@ module AsciidoctorBibliography
27
26
  :years_separator
28
27
 
29
28
  def initialize(format)
30
- if format == 'numbers'
31
- bibpunct = '{[}{]}{,}{n}{,}{,}'
32
- elsif format == 'authoryear'
33
- bibpunct = '{(}{)}{;}{a}{,}{,}'
29
+ if format == "numbers"
30
+ bibpunct = "{[}{]}{,}{n}{,}{,}"
31
+ elsif format == "authoryear"
32
+ bibpunct = "{(}{)}{;}{a}{,}{,}"
34
33
  else
35
34
  raise StandardError, "Unknown TeX citation format: #{format}"
36
35
  end
@@ -50,56 +49,55 @@ module AsciidoctorBibliography
50
49
  macro_options = MACROS[citation.macro]
51
50
  output = []
52
51
  case macro_options[:type]
53
- when :full
54
52
  # NOTE: deliberately repetitive to improve redability.
55
53
  when :textual
56
54
  citation.citation_items.each do |cite|
57
55
  authors = authors(macro_options[:authors], cite)
58
- year = if @style == 'n'
56
+ year = if @style == "n"
59
57
  bibliographer.appearance_index_of(cite.key)
60
58
  else
61
59
  year(cite)
62
60
  end
63
- cetera = Helpers.join_nonempty([year].concat(extra(cite)), @years_separator + ' ')
61
+ cetera = Helpers.join_nonempty([year].concat(extra(cite)), @years_separator + " ")
64
62
  cetera = bracket(cetera) if macro_options[:bracketed]
65
- label = Helpers.join_nonempty([authors, cetera], ' ')
63
+ label = Helpers.join_nonempty([authors, cetera], " ")
66
64
  output << citation.xref(cite.key, label)
67
65
  end
68
- output = output.join(@cites_separator + ' ')
66
+ output = output.join(@cites_separator + " ")
69
67
  when :parenthetical
70
68
  citation.citation_items.each do |cite|
71
- if @style == 'n'
69
+ if @style == "n"
72
70
  authors = nil
73
71
  year = bibliographer.appearance_index_of(cite.key)
74
72
  else
75
73
  authors = authors(macro_options[:authors], cite)
76
74
  year = year(cite)
77
75
  end
78
- cetera = Helpers.join_nonempty([year].concat(extra(cite)), @years_separator + ' ')
79
- label = Helpers.join_nonempty([authors, cetera], @author_year_separator + ' ')
76
+ cetera = Helpers.join_nonempty([year].concat(extra(cite)), @years_separator + " ")
77
+ label = Helpers.join_nonempty([authors, cetera], @author_year_separator + " ")
80
78
  output << citation.xref(cite.key, label)
81
79
  end
82
- output = output.join(@cites_separator + ' ')
80
+ output = output.join(@cites_separator + " ")
83
81
  output = bracket(output) if macro_options[:bracketed]
84
82
  when :authors_only
85
83
  citation.citation_items.each do |cite|
86
84
  authors = authors(macro_options[:authors], cite)
87
85
  year = nil
88
- cetera = Helpers.join_nonempty([year].concat(extra(cite)), @years_separator + ' ')
89
- label = Helpers.join_nonempty([authors, cetera], @author_year_separator + ' ')
86
+ cetera = Helpers.join_nonempty([year].concat(extra(cite)), @years_separator + " ")
87
+ label = Helpers.join_nonempty([authors, cetera], @author_year_separator + " ")
90
88
  output << citation.xref(cite.key, label)
91
89
  end
92
- output = output.join(@cites_separator + ' ')
90
+ output = output.join(@cites_separator + " ")
93
91
  output = bracket(output) if macro_options[:bracketed]
94
92
  when :years_only
95
93
  citation.citation_items.each do |cite|
96
94
  authors = nil
97
95
  year = year(cite)
98
- cetera = Helpers.join_nonempty([year].concat(extra(cite)), @years_separator + ' ')
99
- label = Helpers.join_nonempty([authors, cetera], @author_year_separator + ' ')
96
+ cetera = Helpers.join_nonempty([year].concat(extra(cite)), @years_separator + " ")
97
+ label = Helpers.join_nonempty([authors, cetera], @author_year_separator + " ")
100
98
  output << citation.xref(cite.key, label)
101
99
  end
102
- output = output.join(@cites_separator + ' ')
100
+ output = output.join(@cites_separator + " ")
103
101
  output = bracket(output) if macro_options[:bracketed]
104
102
  else
105
103
  raise StandardError, "Unknown TeX citation macro type: #{macro_options[:type]}"
@@ -115,24 +113,24 @@ module AsciidoctorBibliography
115
113
  end
116
114
 
117
115
  def find_entry(key)
118
- entry = @database.find { |h| h['id'] == key }
116
+ entry = @database.detect { |h| h["id"] == key }
119
117
  raise StandardError, "Can't find entry: #{key}" if entry.nil?
120
118
  entry
121
119
  end
122
120
 
123
121
  def year(cite)
124
122
  entry = find_entry(cite.key)
125
- issued = entry['issued']
123
+ issued = entry["issued"]
126
124
 
127
125
  if issued.nil?
128
- puts "asciidoctor-bibliography: citation (#{cite.key}) has no 'issued' information"
129
- return ''
126
+ warn "asciidoctor-bibliography: citation (#{cite.key}) has no 'issued' information"
127
+ return ""
130
128
  end
131
129
 
132
- date_parts = issued['date-parts']
133
- return '' if date_parts.nil?
130
+ date_parts = issued["date-parts"]
131
+ return "" if date_parts.nil?
134
132
 
135
- return '' if date_parts.first.nil?
133
+ return "" if date_parts.first.nil?
136
134
  date_parts.first.first
137
135
  end
138
136
 
@@ -168,20 +166,20 @@ module AsciidoctorBibliography
168
166
 
169
167
  def authors_list(cite)
170
168
  entry = find_entry(cite.key)
171
- authors = entry['author']
169
+ authors = entry["author"]
172
170
  return [] if authors.nil?
173
- authors.map { |h| h['family'] }.compact
171
+ authors.map { |h| h["family"] }.compact
174
172
  end
175
173
 
176
174
  def authors_abbreviated(cite)
177
175
  authors = authors_list(cite)
178
- return '' if authors.empty?
176
+ return "" if authors.empty?
179
177
  authors.length > 1 ? "#{authors.first} et al." : authors.first
180
178
  end
181
179
 
182
180
  def authors_full(cite)
183
181
  authors = authors_list(cite)
184
- return '' if authors.empty?
182
+ return "" if authors.empty?
185
183
  Helpers.to_sentence authors
186
184
  end
187
185
  end