Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add bibliography class #1595

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
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