Skip to content
73 changes: 73 additions & 0 deletions lib/review/book/bibliography.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
begin
require 'bibtex'
require 'citeproc'
require 'csl/styles'
require 'review/book/bibliography_format'
rescue LoadError
# raise ReVIEW::ConfigError inside the class
end

module ReVIEW
module Book
class Bibliography
def initialize(bibfile, config = nil)
@bibtex = BibTeX.parse(bibfile, filter: :latex)
@config = config
format('text')
rescue NameError
raise ReVIEW::ConfigError, 'not found bibtex libraries. disabled bibtex feature.'
end

def format(format)
style = @config['bib-csl-style'] || 'acm-siggraph'
@citeproc = CiteProc::Processor.new(style: style, format: format, locale: 'en-US')
@citeproc.import(@bibtex.to_citeproc)
self
rescue NameError
raise ReVIEW::ConfigError, 'not found bibtex libraries. disabled bibtex feature.'
end

def ref(keys)
authors = []
keys.split(',').each do |key|
authors << { id: key.strip }
end

cited = @citeproc.render(:citation, authors)

# FIXME: need to apply CSL style
if cited.gsub(/[\[\]()\s,]/, '') == ''
refnums = []
refnames = authors.map(&:values).flatten
@citeproc.bibliography.ids.each_with_index do |key, idx|
if refnames.include?(key)
refnums << (idx + 1)
end
end
cited = "[#{refnums.join(', ')}]"
end

cited
end

def list(key = nil)
b = @citeproc.bibliography
content = []

(0..(b.references.size - 1)).each do |i|
id = b.ids[i]
reference = b.references[i]

if key.blank? || key == id
content << [b.prefix, reference, b.suffix].compact.join
end
end
[
b.header,
content.join(b.connector),
b.footer
].compact.join(b.connector)
end
end
end
end
29 changes: 29 additions & 0 deletions lib/review/book/bibliography_format.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
begin
require 'citeproc'
require 'citeproc/ruby'
rescue LoadError
# raise ReVIEW::ConfigError inside the class
end

# https://github.com/inukshuk/citeproc-ruby
module CiteProc
module Ruby
module Formats
# class Html
# end

class Latex
def bibliography(bibliography)
bibliography.header = '\\begin{description}'
bibliography.footer = '\\end{description}'

bibliography.prefix = '\\item[] '
bibliography.suffix = ''

bibliography.connector = "\n"
bibliography
end
end
end
end
end
16 changes: 15 additions & 1 deletion lib/review/book/chapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
require 'review/book/book_unit'
require 'review/lineinput'
require 'review/preprocessor'
require 'review/book/bibliography'

module ReVIEW
module Book
class Chapter < BookUnit
attr_reader :number, :book
attr_reader :number, :book, :bibliography

def self.mkchap(book, name, number = nil)
name += book.ext if File.extname(name).empty?
Expand Down Expand Up @@ -51,6 +52,19 @@ def initialize(book, number, name, path, io = nil)
if !@content && @path && File.exist?(@path)
@content = File.read(@path, mode: 'rt:BOM|utf-8')
@number = nil if %w[nonum nodisp notoc].include?(find_first_header_option)

# bibliography
if @book && @book.config
chapter_bibfile = File.join(File.dirname(@path), File.basename(@path, '.re') + '.bib')
if File.exist?(chapter_bibfile)
@bibliography = Book::Bibliography.new(chapter_bibfile, @book.config)
else
book_bibfile = File.join(@book.basedir, @book.config['bookname'] + '.bib')
if File.exist?(book_bibfile)
@bibliography = Book::Bibliography.new(book_bibfile, @book.config)
end
end
end
end

super()
Expand Down
8 changes: 8 additions & 0 deletions lib/review/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,14 @@ def bibpaper(lines, id, caption)
puts
end

def inline_bibref(id)
@chapter.bibliography.format('text').ref(id)
end

def bibliography
puts @chapter.bibliography.format('text').list
end

def inline_hd(id)
m = /\A([^|]+)\|(.+)/.match(id)
if m && m[1]
Expand Down
2 changes: 2 additions & 0 deletions lib/review/compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ def inline_defined?(name)
defsingle :firstlinenum, 1
defsingle :beginchild, 0
defsingle :endchild, 0
defsingle :bibliography, 0

definline :chapref
definline :chap
Expand Down Expand Up @@ -274,6 +275,7 @@ def inline_defined?(name)
definline :pageref
definline :w
definline :wb
definline :bibref

private

Expand Down
8 changes: 8 additions & 0 deletions lib/review/htmlbuilder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,14 @@ def inline_bib(id)
app_error "unknown bib: #{id}"
end

def inline_bibref(id)
@chapter.bibliography.format('html').ref(id)
end

def bibliography
puts @chapter.bibliography.format('html').list
end

def inline_hd_chap(chap, id)
n = chap.headline_index.number(id)
str = if n.present? && chap.number && over_secnolevel?(n)
Expand Down
8 changes: 8 additions & 0 deletions lib/review/latexbuilder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1275,6 +1275,14 @@ def inline_bib(id)
macro('reviewbibref', "[#{@chapter.bibpaper(id).number}]", bib_label(id))
end

def inline_bibref(id)
@chapter.bibliography.format('latex').ref(id)
end

def bibliography
puts @chapter.bibliography.format('latex').list
end

def inline_hd_chap(chap, id)
n = chap.headline_index.number(id)
str = if n.present? && chap.number && over_secnolevel?(n)
Expand Down
3 changes: 3 additions & 0 deletions review.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ Gem::Specification.new do |gem|
gem.add_dependency('rouge')
gem.add_dependency('rubyzip')
gem.add_dependency('tty-logger')
gem.add_development_dependency('bibtex-ruby')
gem.add_development_dependency('citeproc-ruby')
gem.add_development_dependency('csl-styles')
gem.add_development_dependency('mini_magick')
gem.add_development_dependency('pygments.rb')
gem.add_development_dependency('rake')
Expand Down
151 changes: 151 additions & 0 deletions test/test_bibliography.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
require 'book_test_helper'
require 'review/book/bibliography'

class BibliographyTest < Test::Unit::TestCase
include BookTestHelper
def setup
mktmpbookdir do |_dir, book, _files|
@book = book
end
@bib = Book::Bibliography.new(bibfile, @book.config)
end

def test_new
assert @bib
end

def test_ref_text
@book.config['bib-csl-style'] = 'acm-siggraph'
assert_equal '[Thomas et al. 2009]', @bib.format('text').ref('pickaxe')

@book.config['bib-csl-style'] = 'apa'
assert_equal '(Thomas et al., 2009)', @bib.format('text').ref('pickaxe')

@book.config['bib-csl-style'] = 'ieee'
assert_equal '[1]', @bib.format('text').ref('pickaxe')
end

def test_ref_text_multiple
@book.config['bib-csl-style'] = 'acm-siggraph'
assert_equal '[Thomas et al. 2009; van Fraassen 1989]',
@bib.format('text').ref('pickaxe,fraassen_1989')

assert_equal '[Thomas et al. 2009; van Fraassen 1989]',
@bib.format('text').ref('pickaxe, fraassen_1989')

@book.config['bib-csl-style'] = 'apa'
assert_equal '(Thomas et al., 2009; van Fraassen, 1989)',
@bib.format('text').ref('pickaxe,fraassen_1989')

@book.config['bib-csl-style'] = 'ieee'
assert_equal '[1, 2]',
@bib.format('text').ref('pickaxe,fraassen_1989')
end

def test_cite_html
@book.config['bib-csl-style'] = 'acm-siggraph'
assert_equal '[Thomas et al. 2009]', @bib.format('html').ref('pickaxe')

@book.config['bib-csl-style'] = 'apa'
assert_equal '(Thomas et al., 2009)', @bib.format('html').ref('pickaxe')
end

def test_ref_latex
@book.config['bib-csl-style'] = 'acm-siggraph'
assert_equal '[Thomas et al. 2009]', @bib.format('latex').ref('pickaxe')

@book.config['bib-csl-style'] = 'apa'
assert_equal '(Thomas et al., 2009)', @bib.format('latex').ref('pickaxe')
end

def test_list
@book.config['bib-csl-style'] = 'acm-siggraph'
expect = <<-EOS
Fraassen, B.C. van. 1989. Laws and Symmetry. Oxford University Press, Oxford.
Thomas, D. and Hunt, A. 2019. The Pragmatic Programmer: Your Journey to Mastery, 20th Anniversary Edition. The Pragmatic Bookshelf.
Thomas, D., Fowler, C., and Hunt, A. 2009. Programming Ruby 1.9: The Pragmatic Programmer’s Guide. The Pragmatic Bookshelf, Raleigh, North Carolina.
EOS
assert_equal expect.chomp, @bib.format('text').list
end

def test_list_html
@book.config['bib-csl-style'] = 'acm-siggraph'
expect = <<-EOS
<ol class="csl-bibliography">
<li class="csl-entry"><span style="font-variant: small-caps">Fraassen, B.C. van</span>. 1989. <i>Laws and Symmetry</i>. Oxford University Press, Oxford.</li>
<li class="csl-entry"><span style="font-variant: small-caps">Thomas, D. and Hunt, A.</span> 2019. <i>The Pragmatic Programmer: Your Journey to Mastery, 20th Anniversary Edition</i>. The Pragmatic Bookshelf.</li>
<li class="csl-entry"><span style="font-variant: small-caps">Thomas, D., Fowler, C., and Hunt, A.</span> 2009. <i>Programming Ruby 1.9: The Pragmatic Programmer’s Guide</i>. The Pragmatic Bookshelf, Raleigh, North Carolina.</li>
</ol>
EOS
assert_equal expect.chomp, @bib.format('html').list

@book.config['bib-csl-style'] = 'ieee'
expect = <<-EOS
<ol class="csl-bibliography">
<li class="csl-entry" style="margin-bottom: 0.0em">[1]D. Thomas, C. Fowler, and A. Hunt, <i>Programming Ruby 1.9: The Pragmatic Programmer’s Guide</i>. Raleigh, North Carolina: The Pragmatic Bookshelf, 2009.</li>
<li class="csl-entry" style="margin-bottom: 0.0em">[2]B. C. van Fraassen, <i>Laws and Symmetry</i>. Oxford: Oxford University Press, 1989.</li>
<li class="csl-entry" style="margin-bottom: 0.0em">[3]D. Thomas and A. Hunt, <i>The Pragmatic Programmer: Your Journey to Mastery, 20th Anniversary Edition</i>. The Pragmatic Bookshelf, 2019.</li>
</ol>
EOS
assert_equal expect.chomp, @bib.format('html').list
end

def test_list_latex
@book.config['bib-csl-style'] = 'acm-siggraph'
expect = <<-EOS
\\begin{description}
\\item[] Fraassen, B.C. van. 1989. \\emph{Laws and Symmetry}. Oxford University Press, Oxford.
\\item[] Thomas, D. and Hunt, A. 2019. \\emph{The Pragmatic Programmer: Your Journey to Mastery, 20th Anniversary Edition}. The Pragmatic Bookshelf.
\\item[] Thomas, D., Fowler, C., and Hunt, A. 2009. \\emph{Programming Ruby 1.9: The Pragmatic Programmer’s Guide}. The Pragmatic Bookshelf, Raleigh, North Carolina.
\\end{description}
EOS
assert_equal expect.chomp, @bib.format('latex').list

@book.config['bib-csl-style'] = 'ieee'
expect = <<-EOS
\\begin{description}
\\item[] [1]D. Thomas, C. Fowler, and A. Hunt, \\emph{Programming Ruby 1.9: The Pragmatic Programmer’s Guide}. Raleigh, North Carolina: The Pragmatic Bookshelf, 2009.
\\item[] [2]B. C. van Fraassen, \\emph{Laws and Symmetry}. Oxford: Oxford University Press, 1989.
\\item[] [3]D. Thomas and A. Hunt, \\emph{The Pragmatic Programmer: Your Journey to Mastery, 20th Anniversary Edition}. The Pragmatic Bookshelf, 2019.
\\end{description}
EOS
assert_equal expect.chomp, @bib.format('latex').list
end

def test_sist02
@book.config['bib-csl-style'] = 'sist02'
key = 'pickaxe'

# The sort order depends on the execution environment (OS).
# Therefore if the reference number is the same, it is assumed to be passed.
assert_equal @bib.format('text').list(key)[1], @bib.format('text').ref(key)[1]
end

private

def bibfile
<<-EOS
@book{pickaxe,
address = {Raleigh, North Carolina},
author = {Thomas, Dave and Fowler, Chad and Hunt, Andy},
publisher = {The Pragmatic Bookshelf},
series = {The Facets of Ruby},
title = {Programming Ruby 1.9: The Pragmatic Programmer's Guide},
year = {2009}
}
@book{fraassen_1989,
Address = {Oxford},
Author = {Bas C. van Fraassen},
Publisher = {Oxford University Press},
Title = {Laws and Symmetry},
Year = 1989
}
@book{pragbook,
author = {Thomas, Dave and Hunt, Andy},
publisher = {The Pragmatic Bookshelf},
title = {The Pragmatic Programmer: Your Journey to Mastery, 20th Anniversary Edition},
year = {2019}
}
EOS
end
end